From 3728863b634816ba76ffa5dc4ae58de09f782844 Mon Sep 17 00:00:00 2001 From: Ivan Neplokhov Date: Mon, 2 Feb 2026 15:47:44 +0400 Subject: [PATCH] some --- package.json | 23 ++++---- scripts/notarize.js | 28 ++++++++++ src/renderer/App.jsx | 12 +++-- src/renderer/hooks/useAccountManagement.js | 61 ++++++++++++++++++++-- src/renderer/hooks/useAppTabGroups.js | 2 - src/renderer/hooks/useTabProps.js | 20 +------ src/renderer/styles/app.css | 32 ++++++++++++ 7 files changed, 138 insertions(+), 40 deletions(-) create mode 100644 scripts/notarize.js diff --git a/package.json b/package.json index 8e3c82e..057f4bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "telegram-invite-automation", - "version": "1.2.0", + "version": "1.3.0", "private": true, "description": "Automated user parsing and invites for Telegram groups", "main": "src/main/index.js", @@ -8,16 +8,16 @@ "scripts": { "dev": "concurrently -k \"vite\" \"wait-on http://127.0.0.1:5173 && electron .\"", "start": "electron .", - "build": "vite build && electron-builder", - "build:win": "vite build && electron-builder --win", - "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": "NODE_OPTIONS=--no-warnings vite build && NODE_OPTIONS=--no-warnings electron-builder", + "build:win": "NODE_OPTIONS=--no-warnings vite build && NODE_OPTIONS=--no-warnings electron-builder --win", + "build:win:x64": "NODE_OPTIONS=--no-warnings vite build && NODE_OPTIONS=--no-warnings electron-builder --win --x64", + "build:win:x64:installer": "NODE_OPTIONS=--no-warnings vite build && NODE_OPTIONS=--no-warnings electron-builder --win --x64 --config.win.target=nsis", + "build:win:x64:portable": "NODE_OPTIONS=--no-warnings vite build && NODE_OPTIONS=--no-warnings electron-builder --win --x64 --config.win.target=portable", "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", - "dist": "vite build && electron-builder", + "build:mac": "NODE_OPTIONS=--no-warnings vite build && NODE_OPTIONS=--no-warnings electron-builder --mac", + "build:all": "NODE_OPTIONS=--no-warnings vite build && NODE_OPTIONS=--no-warnings electron-builder --win --mac", + "build:linux": "NODE_OPTIONS=--no-warnings vite build && NODE_OPTIONS=--no-warnings electron-builder --linux", + "dist": "NODE_OPTIONS=--no-warnings vite build && NODE_OPTIONS=--no-warnings electron-builder", "build:converter:mac": "bash scripts/build-converter.sh", "build:converter:win": "powershell -ExecutionPolicy Bypass -File scripts/build-converter.ps1" }, @@ -29,6 +29,7 @@ "react-dom": "^18.2.0" }, "devDependencies": { + "@electron/notarize": "^2.2.0", "@vitejs/plugin-react": "^4.2.1", "concurrently": "^8.2.2", "electron": "^29.1.0", @@ -69,12 +70,14 @@ "asarUnpack": [ "**/*.node" ], + "afterSign": "scripts/notarize.js", "electronLanguages": [ "ru", "en-US" ], "mac": { "category": "public.app-category.productivity", + "hardenedRuntime": true, "target": [ "dmg" ], diff --git a/scripts/notarize.js b/scripts/notarize.js new file mode 100644 index 0000000..04a169f --- /dev/null +++ b/scripts/notarize.js @@ -0,0 +1,28 @@ +const path = require("path"); +const { notarize } = require("@electron/notarize"); + +exports.default = async function notarizing(context) { + const { electronPlatformName, appOutDir } = context; + if (electronPlatformName !== "darwin") return; + + const appleId = process.env.APPLE_ID; + const appleIdPassword = process.env.APPLE_ID_PASSWORD; + const teamId = process.env.APPLE_TEAM_ID; + + if (!appleId || !appleIdPassword || !teamId) { + console.log("[notarize] Skipped: APPLE_ID / APPLE_ID_PASSWORD / APPLE_TEAM_ID not set."); + return; + } + + const appName = context.packager.appInfo.productFilename; + const appPath = path.join(appOutDir, `${appName}.app`); + + console.log(`[notarize] Notarizing ${appPath}`); + await notarize({ + tool: "notarytool", + appPath, + appleId, + appleIdPassword, + teamId + }); +}; diff --git a/src/renderer/App.jsx b/src/renderer/App.jsx index a015b3b..148e9ca 100644 --- a/src/renderer/App.jsx +++ b/src/renderer/App.jsx @@ -235,8 +235,10 @@ export default function App() { pushStep("Проверка прав инвайта", okCount > 0 ? "ok" : "warn", `OK: ${okCount} / ${inviteAccessStatus.length}`); } pushStep("Очередь", queueStats.total > 0 ? "ok" : "warn", `В очереди: ${queueStats.total}`); - const intervalOk = Number(taskForm.minInterval) <= Number(taskForm.maxInterval); - pushStep("Интервалы и лимиты", intervalOk ? "ok" : "warn", `Мин: ${taskForm.minInterval} · Макс: ${taskForm.maxInterval}`); + const minInterval = Number(taskForm.minIntervalMinutes || 0); + const maxInterval = Number(taskForm.maxIntervalMinutes || 0); + const intervalOk = minInterval > 0 && maxInterval > 0 && minInterval <= maxInterval; + pushStep("Интервалы и лимиты", intervalOk ? "ok" : "warn", `Мин: ${minInterval || "—"} · Макс: ${maxInterval || "—"}`); if (taskForm.inviteViaAdmins) { pushStep("Инвайт через админов", taskForm.inviteAdminMasterId ? "ok" : "warn", taskForm.inviteAdminMasterId ? "Мастер-админ выбран" : "Не выбран мастер-админ"); } else { @@ -552,7 +554,9 @@ export default function App() { loadAccountAssignments, showNotification, setTaskNotice, - setAccounts + setAccounts, + membershipStatus, + refreshMembership }); const { applyTaskPreset } = useTaskPresets({ hasSelectedTask, @@ -883,8 +887,6 @@ export default function App() { accessStatus, roleSummary, mutualContactDiagnostics, - accountById, - formatAccountLabel, accountEvents, clearAccountEvents, onSettingsChange, diff --git a/src/renderer/hooks/useAccountManagement.js b/src/renderer/hooks/useAccountManagement.js index 379a50c..cd9ba0f 100644 --- a/src/renderer/hooks/useAccountManagement.js +++ b/src/renderer/hooks/useAccountManagement.js @@ -11,7 +11,9 @@ export default function useAccountManagement({ loadAccountAssignments, showNotification, setTaskNotice, - setAccounts + setAccounts, + membershipStatus, + refreshMembership }) { const persistAccountRoles = async (next) => { if (!window.api || selectedTaskId == null) return; @@ -76,7 +78,7 @@ export default function useAccountManagement({ persistAccountRoles(next); }; - const applyRolePreset = (type) => { + const applyRolePreset = async (type) => { if (!hasSelectedTask) return; const availableIds = selectedAccountIds.length ? selectedAccountIds @@ -116,11 +118,62 @@ export default function useAccountManagement({ const existing = taskAccountRoles[id] || {}; next[id] = { monitor: false, invite: false, confirm: true, inviteLimit: existing.inviteLimit || 0 }; }); + if (monitorIds.length < monitorCount) { + if (typeof refreshMembership === "function") { + await refreshMembership("roles"); + } + const monitorRoleIds = Object.entries(next) + .filter(([, roles]) => roles && roles.monitor) + .map(([id]) => Number(id)); + const monitorInCompetitors = monitorRoleIds.filter((id) => { + const membership = membershipStatus && membershipStatus[id]; + if (!membership) return false; + const total = Number(membership.competitorTotal || 0); + return total > 0 && Number(membership.competitorCount || 0) > 0; + }); + if (!monitorRoleIds.length) { + showNotification("Нет бота мониторинга. Назначьте роль мониторинга.", "error"); + } else if (!monitorInCompetitors.length) { + showNotification("Нет бота мониторинга в группах конкурентов. Введите бота в конкурентов или включите авто‑вступление.", "error"); + } else { + showNotification("Не хватает аккаунтов для роли мониторинга.", "error"); + } + } if (inviteIds.length < inviteCount) { - showNotification("Не хватает аккаунтов для роли инвайта.", "error"); + if (typeof refreshMembership === "function") { + await refreshMembership("roles"); + } + const inviteRoleIds = Object.entries(next) + .filter(([, roles]) => roles && roles.invite) + .map(([id]) => Number(id)); + const inviteInOurGroup = inviteRoleIds.filter( + (id) => membershipStatus && membershipStatus[id] && membershipStatus[id].ourGroupMember + ); + if (!inviteRoleIds.length) { + showNotification("Нет инвайт‑бота. Назначьте роль инвайта.", "error"); + } else if (!inviteInOurGroup.length) { + showNotification("Нет инвайт‑бота в нашей группе. Введите бота в нашу группу или включите авто‑вступление.", "error"); + } else { + showNotification("Не хватает аккаунтов для роли инвайта.", "error"); + } } if (taskForm.separateConfirmRoles && confirmIds.length < confirmCount) { - showNotification("Не хватает аккаунтов для роли подтверждения.", "error"); + if (typeof refreshMembership === "function") { + await refreshMembership("roles"); + } + const confirmRoleIds = Object.entries(next) + .filter(([, roles]) => roles && roles.confirm) + .map(([id]) => Number(id)); + const confirmInOurGroup = confirmRoleIds.filter( + (id) => membershipStatus && membershipStatus[id] && membershipStatus[id].ourGroupMember + ); + if (!confirmRoleIds.length) { + showNotification("Нет подтверждающего бота. Назначьте роль подтверждения.", "error"); + } else if (!confirmInOurGroup.length) { + showNotification("Нет подтверждающего бота в нашей группе. Введите бота в нашу группу или включите авто‑вступление.", "error"); + } else { + showNotification("Не хватает аккаунтов для роли подтверждения.", "error"); + } } } const ids = Object.keys(next).map((id) => Number(id)); diff --git a/src/renderer/hooks/useAppTabGroups.js b/src/renderer/hooks/useAppTabGroups.js index 4244700..cc38511 100644 --- a/src/renderer/hooks/useAppTabGroups.js +++ b/src/renderer/hooks/useAppTabGroups.js @@ -108,8 +108,6 @@ export default function useAppTabGroups({ accessStatus, roleSummary, mutualContactDiagnostics, - accountById, - formatAccountLabel, accountEvents, clearAccountEvents, onSettingsChange, diff --git a/src/renderer/hooks/useTabProps.js b/src/renderer/hooks/useTabProps.js index 65e108c..a16f2e5 100644 --- a/src/renderer/hooks/useTabProps.js +++ b/src/renderer/hooks/useTabProps.js @@ -99,14 +99,6 @@ export default function useTabProps( setConfirmPage, confirmPageCount, pagedConfirmQueue, - queueItems, - queueStats, - queueSearch, - setQueueSearch, - queuePage, - setQueuePage, - queuePageCount, - pagedQueue, clearConfirmQueue, auditSearch, setAuditSearch, @@ -122,9 +114,7 @@ export default function useTabProps( selectedTask, accessStatus, roleSummary, - mutualContactDiagnostics, - accountById, - formatAccountLabel + mutualContactDiagnostics } = logsTab; const { queueStats, @@ -242,14 +232,6 @@ export default function useTabProps( setConfirmPage, confirmPageCount, pagedConfirmQueue, - queueItems, - queueStats, - queueSearch, - setQueueSearch, - queuePage, - setQueuePage, - queuePageCount, - pagedQueue, clearConfirmQueue, auditSearch, setAuditSearch, diff --git a/src/renderer/styles/app.css b/src/renderer/styles/app.css index 3f3fd41..ebb10bb 100644 --- a/src/renderer/styles/app.css +++ b/src/renderer/styles/app.css @@ -1962,6 +1962,38 @@ label .hint { margin-top: 6px; } +.log-table { + display: grid; + gap: 8px; + margin-top: 10px; +} + +.log-head, +.log-row { + display: grid; + grid-template-columns: 1.2fr 1.6fr 1.4fr 0.6fr 0.8fr; + gap: 12px; + align-items: center; +} + +.log-head { + font-size: 12px; + color: #64748b; + font-weight: 600; +} + +.log-row { + padding: 8px 0; + border-bottom: 1px solid #e2e8f0; + font-size: 13px; +} + +.log-empty { + font-size: 13px; + color: #64748b; + padding: 8px 0; +} + .log-result { font-size: 13px; color: #1f2937;