diff --git a/package.json b/package.json index f1a5133..78f5cec 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "build:win:x64": "vite build && electron-builder --win --x64", "build:win:x64:installer": "vite build && electron-builder --win --x64 --config.win.target=nsis", "build:win:x64:portable": "vite build && electron-builder --win --x64 --config.win.target=portable", - "build:win:x64:portable:zip": "npm run build:win:x64:portable && powershell -Command \"Compress-Archive -Force -Path dist/release/*portable*.exe -DestinationPath dist/release/Telegram-Invite-Automation-win-portable-x64.zip\"", + "build:win:x64:portable:zip": "npm run build:win:x64:portable && zip -j -o dist/release/Telegram-Invite-Automation-win-portable-x64.zip dist/release/Telegram-Invite-Automation-win-x64-*.exe", "build:mac": "vite build && electron-builder --mac", "build:all": "vite build && electron-builder --win --mac", "build:linux": "vite build && electron-builder --linux", diff --git a/resources/converter/win/tgconvertor.exe b/resources/converter/win/tgconvertor.exe new file mode 100644 index 0000000..9c242fb Binary files /dev/null and b/resources/converter/win/tgconvertor.exe differ diff --git a/src/main/store.js b/src/main/store.js index 84cb9e1..55aeddc 100644 --- a/src/main/store.js +++ b/src/main/store.js @@ -459,15 +459,16 @@ function initStore(userDataPath) { .run(now, queueId); } - function recordInvite(taskId, userId, username, accountId, accountPhone, sourceChat, status, error, skippedReason, action) { + function recordInvite(taskId, userId, username, accountId, accountPhone, sourceChat, status, error, skippedReason, action, userAccessHash) { const now = dayjs().toISOString(); db.prepare(` - INSERT INTO invites (task_id, user_id, username, account_id, account_phone, source_chat, action, skipped_reason, invited_at, status, error) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + INSERT INTO invites (task_id, user_id, username, user_access_hash, account_id, account_phone, source_chat, action, skipped_reason, invited_at, status, error) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `).run( taskId || 0, userId, username || "", + userAccessHash || "", accountId || 0, accountPhone || "", sourceChat || "", @@ -564,6 +565,7 @@ function initStore(userDataPath) { taskId: row.task_id || 0, userId: row.user_id, username: row.username || "", + userAccessHash: row.user_access_hash || "", accountId: row.account_id || 0, accountPhone: row.account_phone || "", sourceChat: row.source_chat || "", diff --git a/src/main/taskRunner.js b/src/main/taskRunner.js index c6e78e2..8c124ee 100644 --- a/src/main/taskRunner.js +++ b/src/main/taskRunner.js @@ -105,7 +105,8 @@ class TaskRunner { "success", "", "", - "invite" + "invite", + item.user_access_hash ); } else { errors.push(`${item.user_id}: ${result.error}`); @@ -125,7 +126,8 @@ class TaskRunner { "failed", result.error || "", result.error || "", - "invite" + "invite", + item.user_access_hash ); } } diff --git a/src/renderer/App.jsx b/src/renderer/App.jsx index 722204a..ed779c1 100644 --- a/src/renderer/App.jsx +++ b/src/renderer/App.jsx @@ -114,6 +114,7 @@ export default function App() { const [inviteFilter, setInviteFilter] = useState("all"); const [taskSort, setTaskSort] = useState("activity"); const [sidebarExpanded, setSidebarExpanded] = useState(false); + const [expandedInviteId, setExpandedInviteId] = useState(null); const bellRef = useRef(null); const competitorGroups = useMemo(() => { @@ -362,6 +363,23 @@ export default function App() { return date.toLocaleString("ru-RU"); }; + const explainInviteError = (error) => { + if (!error) return ""; + if (error === "USER_ID_INVALID") { + return "Чаще всего нет access_hash (пользователь скрыт/удален/анонимный)."; + } + if (error === "CHAT_WRITE_FORBIDDEN") { + return "Аккаунт не может приглашать: нет прав или он не участник группы."; + } + if (error === "AUTH_KEY_DUPLICATED") { + return "Сессия используется в другом месте, Telegram отозвал ключ."; + } + if (error.includes("FLOOD") || error.includes("PEER_FLOOD")) { + return "Ограничение Telegram по частоте действий."; + } + return ""; + }; + const showNotification = (text, tone) => { if (tone === "success") return; const entry = { text, tone, id: Date.now() }; @@ -2026,6 +2044,29 @@ export default function App() { {invite.error && invite.error !== "" && (
Причина: {invite.error}
)} + {invite.error && explainInviteError(invite.error) && ( +
Вероятная причина: {explainInviteError(invite.error)}
+ )} + + {expandedInviteId === invite.id && ( +
+
Задача: {invite.taskId}
+
Аккаунт ID: {invite.accountId || "—"}
+
Действие: {invite.action || "invite"}
+
Статус: {invite.status}
+
Пропуск: {invite.skippedReason || "—"}
+
Ошибка: {invite.error || "—"}
+
Вероятная причина: {explainInviteError(invite.error) || "—"}
+
Access Hash: {invite.userAccessHash || "—"}
+
Время: {formatTimestamp(invite.invitedAt)}
+
+ )} ))} diff --git a/src/renderer/styles/app.css b/src/renderer/styles/app.css index 5e0825a..2088bea 100644 --- a/src/renderer/styles/app.css +++ b/src/renderer/styles/app.css @@ -883,6 +883,17 @@ button.danger { color: #475569; } +.invite-details { + margin-top: 10px; + padding: 10px 12px; + border-radius: 10px; + background: #f1f5f9; + color: #334155; + font-size: 12px; + display: grid; + gap: 4px; +} + .wrap { word-break: break-all; }