import React, { memo } from "react"; function LogsTab({ logsTab, setLogsTab, hasSelectedTask, exportLogs, clearLogs, exportInvites, exportProblemInvites, exportFallback, updateFallbackStatus, clearFallback, clearInvites, logSearch, setLogSearch, logPage, setLogPage, logPageCount, pagedLogs, inviteSearch, setInviteSearch, invitePage, setInvitePage, invitePageCount, inviteFilter, setInviteFilter, pagedInvites, fallbackSearch, setFallbackSearch, fallbackPage, setFallbackPage, fallbackPageCount, pagedFallback, auditSearch, setAuditSearch, auditPage, setAuditPage, auditPageCount, pagedAudit, formatTimestamp, explainInviteError, accountById, formatAccountLabel, expandedInviteId, setExpandedInviteId, inviteStats, selectedTask, taskAccountRoles, accessStatus, inviteAccessStatus, selectedTaskName, roleSummary, mutualContactDiagnostics }) { const strategyLabel = (strategy) => { switch (strategy) { case "access_hash": return "access_hash (из сообщения)"; case "participants": return "участники группы"; case "username": return "username"; case "entity": return "getEntity(userId)"; case "retry": return "повторная попытка"; default: return strategy || "—"; } }; const formatStrategies = (meta) => { if (!meta) return ""; try { const parsed = JSON.parse(meta); if (!Array.isArray(parsed)) return meta; return parsed .map((item) => `${strategyLabel(item.strategy)}: ${item.ok ? "ok" : "fail"}${item.detail ? ` (${item.detail})` : ""}`) .map((line) => `- ${line}`) .join("\n"); } catch (error) { return meta; } }; const hasStrategySuccess = (meta) => { if (!meta) return false; try { const parsed = JSON.parse(meta); return Array.isArray(parsed) && parsed.some((item) => item.ok); } catch (error) { return false; } }; const formatTargetType = (value) => { if (!value) return "—"; if (value === "channel") return "канал"; if (value === "megagroup") return "супергруппа"; if (value === "group") return "группа"; return value; }; const formatErrorWithExplain = (value) => { if (!value) return "—"; const code = String(value); const explanation = explainInviteError(code.split(/[:(]/, 1)[0].trim()); if (!explanation) return code; if (code.includes("(")) return code; return `${code} (${explanation})`; }; const explainRawError = (value) => { if (!value) return ""; const code = String(value).split(/[:(]/, 1)[0].trim(); return explainInviteError(code); }; const getDurationMs = (start, finish) => { const startMs = new Date(start).getTime(); const finishMs = new Date(finish).getTime(); if (!Number.isFinite(startMs) || !Number.isFinite(finishMs)) return null; return Math.max(0, finishMs - startMs); }; const hasBothRoles = (accountId) => { if (!accountId || !taskAccountRoles) return false; const roles = taskAccountRoles[String(accountId)] || taskAccountRoles[accountId]; return Boolean(roles && roles.monitor && roles.invite); }; const formatInviteStatus = (status) => { switch (status) { case "success": return "Успех"; case "skipped": return "Пропуск"; case "unconfirmed": return "Не подтверждено"; case "failed": default: return "Ошибка"; } }; return (

Логи и история

{logsTab === "logs" && ( <> )} {logsTab === "invites" && ( <> )} {logsTab === "fallback" && ( <> )}
{logsTab === "logs" && ( <>
{ setLogSearch(event.target.value); setLogPage(1); }} placeholder="Поиск по логам" />
{logPage}/{logPageCount}
{pagedLogs.length === 0 &&
Логи пока пустые.
} {pagedLogs.map((log) => { const successIds = Array.isArray(log.successIds) ? log.successIds : []; const errors = Array.isArray(log.errors) ? log.errors : []; const errorMap = new Map(); errors.forEach((err) => { const parts = String(err).split(":"); if (parts.length < 2) return; const id = parts[0].trim(); const code = parts.slice(1).join(":").trim(); if (!id) return; errorMap.set(id, code); }); const resultRows = [ ...successIds.map((id) => ({ id: String(id), status: "success", message: "успех" })), ...Array.from(errorMap.entries()).map(([id, code]) => ({ id, status: "error", message: `${code} (${explainInviteError(code) || "Причина не определена"})` })) ]; return (
Старт цикла: {formatTimestamp(log.startedAt)}
Завершение: {formatTimestamp(log.finishedAt)}
Добавлено: {log.invitedCount}
{log.meta && (log.meta.batchSize || log.meta.cycleLimit || log.meta.queueCount) && (
Цикл: лимит {log.meta.cycleLimit ?? "—"}, очередь {log.meta.queueCount ?? "—"}, взято {log.meta.batchSize ?? "—"}
)}
Пользователи: {successIds.length ? successIds.join(", ") : "—"}
{resultRows.length > 0 && (
Результаты:
{resultRows.map((row) => (
{row.id} — {row.message}
))}
)} {log.invitedCount === 0 && errors.length === 0 && (
Причина: цикл завершён сразу — очередь пуста
)} {errors.length > 0 && (
Ошибки: {errors.map((err) => { const code = String(err).split(":").pop().trim(); const reason = explainInviteError(code) || "Причина не определена"; return code ? `${err} (${reason})` : err; }).join(" | ")}
)} {(() => { const durationMs = getDurationMs(log.startedAt, log.finishedAt); if (durationMs != null && durationMs < 1000) { return
Цикл завершён сразу: очередь пуста или ошибка на первой попытке.
; } return null; })()}
); })} )} {logsTab === "invites" && ( <> {inviteStats && (
Всего: {inviteStats.total} | Успех: {inviteStats.success} | Ошибка: {inviteStats.failed} | Пропуск: {inviteStats.skipped} | Не подтверждено: {inviteStats.unconfirmed}
)}
{ setInviteSearch(event.target.value); setInvitePage(1); }} placeholder="Поиск по инвайтам" />
{invitePage}/{invitePageCount}
{pagedInvites.length === 0 &&
История пока пустая.
} {pagedInvites.map((invite) => (
{formatTimestamp(invite.invitedAt)}
{formatInviteStatus(invite.status)}
ID: {invite.userId}
Ник: {invite.username ? `@${invite.username}` : "— (нет username в источнике)"}
Источник: {invite.sourceChat || "—"}
Цель: {invite.targetChat || "—"}{invite.targetType ? ` (${formatTargetType(invite.targetType)})` : ""}
Инвайт: {(() => { const account = accountById.get(invite.accountId); return account ? formatAccountLabel(account) : (invite.accountPhone || "—"); })()} {invite.watcherAccountId && invite.accountId && ( {invite.watcherAccountId === invite.accountId ? "Инвайт тем же аккаунтом, что наблюдал" : ""} )}
{invite.watcherAccountId && invite.accountId && invite.watcherAccountId !== invite.accountId && selectedTask && selectedTask.randomAccounts && hasBothRoles(invite.watcherAccountId) && (
Примечание: у наблюдателя стоят обе роли, но включен случайный выбор — инвайт выполнен другим аккаунтом.
)}
Наблюдатель: {(() => { const account = accountById.get(invite.watcherAccountId); return account ? formatAccountLabel(account) : (invite.watcherPhone || "—"); })()}
{invite.skippedReason && invite.skippedReason !== "" && (
Результат: {formatErrorWithExplain(invite.skippedReason)}
)} {invite.error && invite.error !== "" && (
Ошибка: {formatErrorWithExplain(invite.error)}
)}
Проверка участия: {invite.confirmError ? formatErrorWithExplain(invite.confirmError) : (invite.confirmed ? "OK" : "Не подтверждено")}
{invite.confirmError && invite.confirmError.includes("(") && (
Проверял: {invite.confirmError.slice(invite.confirmError.indexOf("(") + 1, invite.confirmError.lastIndexOf(")"))}
)} {invite.strategy && (
Стратегия: {invite.strategy}
)} {invite.strategyMeta && (
{`Стратегии:\n${formatStrategies(invite.strategyMeta)}`}
)} {invite.strategyMeta && !hasStrategySuccess(invite.strategyMeta) && (
Все стратегии не сработали
)} {expandedInviteId === invite.id && (
Задача: {invite.taskId}
Аккаунт ID: {invite.accountId || "—"}
Наблюдатель ID: {invite.watcherAccountId || "—"}
Наблюдатель: {(() => { const account = accountById.get(invite.watcherAccountId); return account ? formatAccountLabel(account) : (invite.watcherPhone || "—"); })()}
Цель: {invite.targetChat || "—"}
Тип цели: {formatTargetType(invite.targetType)}
Действие: {invite.action || "invite"}
Статус: {formatInviteStatus(invite.status)}
Результат: {formatErrorWithExplain(invite.skippedReason)}
Ошибка: {formatErrorWithExplain(invite.error)}
Проверка участия: {invite.confirmError ? formatErrorWithExplain(invite.confirmError) : (invite.confirmed ? "OK" : "Не подтверждено")}
{invite.confirmError && invite.confirmError.includes("(") && (
Проверял: {invite.confirmError.slice(invite.confirmError.indexOf("(") + 1, invite.confirmError.lastIndexOf(")"))}
)} {invite.watcherAccountId && invite.accountId && invite.watcherAccountId !== invite.accountId && selectedTask && selectedTask.randomAccounts && hasBothRoles(invite.watcherAccountId) && (
Примечание: у наблюдателя стоят обе роли, но включен случайный выбор — инвайт выполнен другим аккаунтом.
)}
Пояснение: {explainRawError(invite.error) || explainRawError(invite.confirmError) || "Причина не определена"}
Стратегия: {invite.strategy || "—"}
{invite.strategyMeta ? `Стратегии:\n${formatStrategies(invite.strategyMeta)}` : "Стратегии: —"}
{invite.strategyMeta && !hasStrategySuccess(invite.strategyMeta) && (
Результат: все стратегии не сработали
)}
Access Hash: {invite.userAccessHash || "—"}
Время: {formatTimestamp(invite.invitedAt)}
)}
))} )} {logsTab === "fallback" && ( <>
{ setFallbackSearch(event.target.value); setFallbackPage(1); }} placeholder="Поиск по fallback" />
{fallbackPage}/{fallbackPageCount}
{pagedFallback.length === 0 &&
Fallback пока пуст.
} {pagedFallback.map((item) => (
{formatTimestamp(item.createdAt)}
{item.route}
ID: {item.userId}
Ник: {item.username ? `@${item.username}` : "—"}
Источник: {item.sourceChat || "—"}
Цель: {item.targetChat || "—"}
Причина: {item.reason}
Статус: {item.status}
))} )} {logsTab === "audit" && ( <>
{ setAuditSearch(event.target.value); setAuditPage(1); }} placeholder="Поиск по изменениям" />
{auditPage}/{auditPageCount}
{pagedAudit.length === 0 &&
История изменений пуста.
} {pagedAudit.map((item) => (
{formatTimestamp(item.createdAt)}
{item.action}
{item.details || "—"}
))} )} {logsTab === "diagnostics" && ( <>
Для: {selectedTaskName}
{accessStatus && accessStatus.length > 0 && (
Доступ к группам
{accessStatus.map((item, index) => (
{item.type === "our" ? "Наша" : "Конкурент"}: {item.title || item.value}
{item.ok ? "Доступ есть" : "Нет доступа"}
{!item.ok &&
{item.details}
}
))}
)} {inviteAccessStatus && inviteAccessStatus.length > 0 && (
Права инвайта
Проверяются аккаунты с ролью инвайта: {roleSummary ? roleSummary.invite.length : "—"}
{inviteAccessStatus.map((item, index) => (
{(() => { const account = accountById.get(item.accountId); return account ? formatAccountLabel(account) : (item.accountPhone || item.accountId); })()}: {item.title || item.targetChat} {item.targetType ? ` (${formatTargetType(item.targetType)})` : ""}
{item.canInvite ? "Можно инвайтить" : "Нет прав"}
{!item.canInvite &&
{item.reason || "—"}
}
))}
)} {mutualContactDiagnostics && (
USER_NOT_MUTUAL_CONTACT
Ошибок в истории: {mutualContactDiagnostics.count}
Цель: {(() => { const entry = inviteAccessStatus && inviteAccessStatus[0]; if (entry) { const label = entry.title || entry.targetChat || (selectedTask ? selectedTask.ourGroup : "—"); const typeLabel = formatTargetType(entry.targetType); return typeLabel ? `${label} (${typeLabel})` : label; } return selectedTask ? selectedTask.ourGroup : "—"; })()}
{inviteAccessStatus && inviteAccessStatus.length ? "Права проверены" : "Нет данных проверки"}
{mutualContactDiagnostics.recent.map((item) => (
Пользователь: {item.userId}{item.username ? ` (@${item.username})` : ""}
{formatTimestamp(item.invitedAt)}
))}
Возможные причины: Telegram ограничивает инвайт, если пользователь скрывает приём приглашений, целевая группа требует взаимного контакта, или у пользователя есть приватные ограничения.
)} {!accessStatus?.length && !inviteAccessStatus?.length && (
Диагностика пока пустая.
)} )}
); } export default memo(LogsTab);