diff --git a/src/renderer/hooks/useTaskActions.js b/src/renderer/hooks/useTaskActions.js index 795cd41..d595336 100644 --- a/src/renderer/hooks/useTaskActions.js +++ b/src/renderer/hooks/useTaskActions.js @@ -87,7 +87,7 @@ export default function useTaskActions({ const chosen = pool.slice(0, required); accountRolesMap = {}; chosen.forEach((accountId) => { - const existing = accountRolesMap[accountId] || {}; + const existing = taskAccountRoles[accountId] || {}; accountRolesMap[accountId] = { monitor: true, invite: true, confirm: true, inviteLimit: existing.inviteLimit || 1 }; }); accountIds = chosen; @@ -98,7 +98,7 @@ export default function useTaskActions({ accountIds = accounts.map((account) => account.id); accountRolesMap = {}; accountIds.forEach((accountId) => { - const existing = accountRolesMap[accountId] || {}; + const existing = taskAccountRoles[accountId] || {}; accountRolesMap[accountId] = { monitor: true, invite: true, confirm: true, inviteLimit: existing.inviteLimit || 1 }; }); setTaskAccountRoles(accountRolesMap); diff --git a/src/renderer/tabs/LogsTab.jsx b/src/renderer/tabs/LogsTab.jsx index 635f1b4..ccfcbce 100644 --- a/src/renderer/tabs/LogsTab.jsx +++ b/src/renderer/tabs/LogsTab.jsx @@ -168,14 +168,36 @@ function LogsTab({ const buildUserReason = (invite) => { const codeRaw = invite.error || invite.confirmError || invite.skippedReason || ""; const code = String(codeRaw).split(/[:(]/, 1)[0].trim(); + const isInviteLinkTarget = String(invite.targetChat || "").includes("t.me/+"); if (invite.status === "success") return "Пользователь добавлен и участие подтверждено."; if (invite.status === "unconfirmed") { + if (isInviteLinkTarget) { + return "Инвайт отправлен. Для цели по invite-ссылке вступление может подтверждаться с задержкой."; + } if (code === "USER_NOT_PARTICIPANT") return "Инвайт отправлен, но пользователь еще не вступил в группу."; return explainRawError(invite.confirmError) || "Инвайт отправлен, но участие пока не подтверждено."; } if (invite.status === "skipped") return explainRawError(invite.skippedReason) || "Попытка была пропущена."; return explainRawError(invite.error) || "Telegram отклонил попытку приглашения."; }; + const buildDetailedExplanation = (invite) => { + if (!invite) return "Причина не определена"; + if (invite.status === "unconfirmed") { + const isInviteLinkTarget = String(invite.targetChat || "").includes("t.me/+"); + const confirmCode = String(invite.confirmError || "").split(/[:(]/, 1)[0].trim(); + const hasRightForbidden = String(invite.strategyMeta || "").includes("RIGHT_FORBIDDEN"); + if (confirmCode === "USER_NOT_PARTICIPANT") { + if (hasRightForbidden) { + return "У admin_invite не хватило прав (RIGHT_FORBIDDEN), а проверка показала, что пользователь пока не вступил в группу."; + } + return isInviteLinkTarget + ? "Инвайт отправлен, но для цели по invite-ссылке пользователь может вступить позже (задержка подтверждения/ожидание одобрения)." + : "Инвайт отправлен, но пользователь ещё не вступил в группу на момент проверки."; + } + return explainRawError(invite.confirmError) || "Инвайт отправлен, но участие пока не подтверждено."; + } + return explainRawError(invite.error) || explainRawError(invite.confirmError) || "Причина не определена"; + }; const buildInviteSummary = (invite) => { const userLabel = invite.username ? `@${invite.username}${invite.userId ? ` (ID: ${invite.userId})` : ""}` @@ -193,8 +215,11 @@ function LogsTab({ if (invite.status === "success") { reason = "Успешно добавлен в группу"; } else if (invite.status === "unconfirmed") { + const isInviteLinkTarget = String(invite.targetChat || "").includes("t.me/+"); if (invite.confirmError && invite.confirmError.includes("USER_NOT_PARTICIPANT")) { - reason = "Инвайт отправлен, но пользователь ещё не вступил (нужно согласие/вход по ссылке)"; + reason = isInviteLinkTarget + ? "Инвайт отправлен, ожидаем вступление (цель по invite-ссылке)" + : "Инвайт отправлен, но пользователь ещё не вступил (нужно согласие/вход по ссылке)"; } else { reason = explainRawError(invite.confirmError) || "Участие не подтверждено"; } @@ -250,6 +275,13 @@ function LogsTab({ return "Ошибка"; } }; + const formatInviteStatusForRow = (invite) => { + if (!invite) return "—"; + if (invite.status === "unconfirmed" && String(invite.targetChat || "").includes("t.me/+")) { + return "Ожидает вступления"; + } + return formatInviteStatus(invite.status); + }; return (
@@ -527,7 +559,7 @@ function LogsTab({
{formatTimestamp(invite.invitedAt)}
- {formatInviteStatus(invite.status)} + {formatInviteStatusForRow(invite)}
@@ -543,7 +575,7 @@ function LogsTab({ {expandedInviteId === invite.id && (
-
Что случилось: {formatInviteStatus(invite.status)}
+
Что случилось: {formatInviteStatusForRow(invite)}
Почему: {buildUserReason(invite)}
{suggestAction(invite) && (
Что сделать: {suggestAction(invite).replace(/^Совет:\s*/, "")}
@@ -607,7 +639,7 @@ function LogsTab({
Цель: {invite.targetChat || "—"}
Тип цели: {formatTargetType(invite.targetType)}
Действие: {invite.action || "invite"}
-
Статус: {formatInviteStatus(invite.status)}
+
Статус: {formatInviteStatusForRow(invite)}
Результат: {formatErrorWithExplain(invite.skippedReason)}
Ошибка: {formatErrorWithExplain(invite.error)}
{invite.confirmError && invite.confirmError.includes("(") && ( @@ -617,7 +649,7 @@ function LogsTab({ && selectedTask && selectedTask.randomAccounts && hasBothRoles(invite.watcherAccountId) && (
Примечание: у наблюдателя стоят обе роли, но включен случайный выбор — инвайт выполнен другим аккаунтом.
)} -
Пояснение: {explainRawError(invite.error) || explainRawError(invite.confirmError) || "Причина не определена"}
+
Пояснение: {buildDetailedExplanation(invite)}
Стратегия: {invite.strategy || "—"}
{invite.strategyMeta && !hasStrategySuccess(invite.strategyMeta) && (
Результат: все стратегии не сработали