This commit is contained in:
Ivan Neplokhov 2026-01-15 19:05:37 +04:00
parent 217b6398e6
commit 3329b91c3f
6 changed files with 62 additions and 6 deletions

View File

@ -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",

Binary file not shown.

View File

@ -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 || "",

View File

@ -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
);
}
}

View File

@ -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 !== "" && (
<div className="log-errors">Причина: {invite.error}</div>
)}
{invite.error && explainInviteError(invite.error) && (
<div className="log-users">Вероятная причина: {explainInviteError(invite.error)}</div>
)}
<button
type="button"
className="ghost"
onClick={() => setExpandedInviteId(expandedInviteId === invite.id ? null : invite.id)}
>
{expandedInviteId === invite.id ? "Скрыть детали" : "Подробнее"}
</button>
{expandedInviteId === invite.id && (
<div className="invite-details">
<div>Задача: {invite.taskId}</div>
<div>Аккаунт ID: {invite.accountId || "—"}</div>
<div>Действие: {invite.action || "invite"}</div>
<div>Статус: {invite.status}</div>
<div>Пропуск: {invite.skippedReason || "—"}</div>
<div>Ошибка: {invite.error || "—"}</div>
<div>Вероятная причина: {explainInviteError(invite.error) || "—"}</div>
<div>Access Hash: {invite.userAccessHash || "—"}</div>
<div>Время: {formatTimestamp(invite.invitedAt)}</div>
</div>
)}
</div>
</div>
))}

View File

@ -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;
}