P9(5): restore upstream Telegram bot (settings, backup, notifications)
Per user decision 2026-10-21 (reverses the 2026-10-20 drop-Telegram call): port the upstream 3x-ui v3.7.0-generation Telegram bot wholesale instead of rewriting a minimal set, adapted to this fork. - new internal/web/service/tgbot package (telego): bot lifecycle, commands, reports, callback router, send helpers; token validation via telego; custom API server URL passes the fork's SSRF sanitizer (loopback/private bases fall back to the default endpoint, logged) - settings: full upstream key set (tgBotEnable/Token/Proxy/APIServer/ ChatId, tgRunTime, tgBotBackup, tgCpu/tgMemory, tgLang, tgEnabledEvents) with getters/setters; settings page gains a Telegram tab + sidebar entry; token shown masked, clearTgBotToken honored - wiring (web.go): stats-notify cron registered from tgRunTime when the bot is enabled; CheckHashStorage job; CPU/memory alarm jobs gated on tgEnabledEvents + thresholds; login notifications via event bus; POST /panel/api/setting/testTgBot and /panel/api/backuptotgbot - lite adaptation: StatsNotifyJob no longer gates on a local Xray core (this panel never runs one) so the periodic report and the scheduled DB backup (sendDocument) can actually fire - translations: upstream TG keys restored across all locales End-to-end smoke (real binary + fake Telegram API over HTTPS at a public-looking address, browser form wire format): bot starts on settings change (getMe), testTgBot delivers, panel restart re-arms the bot, tgRunTime='@every 1m' cron really fires sendDocument carrying the panel SQLite DB (magic verified) plus the report sendMessage, login notification delivered, dead-endpoint failure is logged not silent. 13/13 PASS — see P9-REPORT.md.
This commit is contained in:
@@ -88,7 +88,8 @@
|
||||
"noExpiry": "Без строку",
|
||||
"copyAllConfigs": "Копіювати всі конфігурації",
|
||||
"copyAllConfigsCopied": "Всі конфігурації скопійовано",
|
||||
"email": "Email"
|
||||
"email": "Email",
|
||||
"individualLinks": "Окремі посилання"
|
||||
},
|
||||
"menu": {
|
||||
"theme": "Тема",
|
||||
@@ -1168,7 +1169,44 @@
|
||||
"httpMethodDesc": "Метод запитів під час перевірок.",
|
||||
"note": "Балансувальники leastPing/leastLoad завжди мають burstObservatory. Цей перемикач налаштовує її параметри probe — вимкніть, щоб використовувати вбудовані значення за замовчуванням. Зміни застосовуються після перезапуску панелі."
|
||||
}
|
||||
}
|
||||
},
|
||||
"TGBotSettings": "Telegram-бот",
|
||||
"notifications": "Сповіщення",
|
||||
"notifyTime": {
|
||||
"custom": "Власний (crontab)",
|
||||
"daily": "@daily — щодня о 00:00",
|
||||
"every": "@every — повторювати з інтервалом",
|
||||
"hourly": "@hourly — щогодини",
|
||||
"hours": "Години",
|
||||
"interval": "Інтервал",
|
||||
"minutes": "Хвилини",
|
||||
"monthly": "@monthly — щомісяця",
|
||||
"seconds": "Секунди",
|
||||
"unit": "Одиниця",
|
||||
"weekly": "@weekly — щотижня"
|
||||
},
|
||||
"telegramAPIServer": "Telegram API сервер",
|
||||
"telegramAPIServerDesc": "Сервер Telegram API для використання. Залиште поле порожнім, щоб використовувати сервер за умовчанням.",
|
||||
"telegramBotEnable": "Увімкнути Telegram Bot",
|
||||
"telegramBotEnableDesc": "Вмикає бота Telegram.",
|
||||
"telegramBotLanguage": "Мова Telegram-бота",
|
||||
"telegramChatId": "Ідентифікатор чату адміністратора",
|
||||
"telegramChatIdDesc": "Ідентифікатори чату адміністратора Telegram. (розділені комами) (отримайте тут {'@'}userinfobot) або (використовуйте команду '/id' у боті)",
|
||||
"telegramNotifyTime": "Час сповіщення",
|
||||
"telegramNotifyTimeDesc": "Як часто бот Telegram надсилає періодичні звіти. Виберіть готовий інтервал або «Власний», щоб ввести вираз crontab.",
|
||||
"telegramToken": "Telegram-токен",
|
||||
"telegramTokenConfigured": "Налаштовано; залиште порожнім, щоб зберегти поточний токен.",
|
||||
"telegramTokenDesc": "Токен бота Telegram, отриманий від '{'@'}BotFather'.",
|
||||
"telegramTokenPlaceholder": "Налаштовано — введіть новий токен для заміни",
|
||||
"testTgBot": "Надіслати тестове повідомлення",
|
||||
"tgBotNotEnabled": "Бот Telegram не увімкнено",
|
||||
"tgBotNotRunning": "Бот Telegram не запущено",
|
||||
"tgEventBusNotify": "Сповіщення про події в Telegram",
|
||||
"tgEventBusNotifyDesc": "Виберіть, які події спричиняють сповіщення в Telegram",
|
||||
"tgNotifyBackup": "Резервне копіювання бази даних",
|
||||
"tgNotifyBackupDesc": "Надіслати файл резервної копії бази даних зі звітом.",
|
||||
"tgTestFailed": "Тест Telegram не вдався",
|
||||
"tgTestSuccess": "Тестове повідомлення надіслано в Telegram"
|
||||
},
|
||||
"xray": {
|
||||
"save": "Зберегти",
|
||||
@@ -1657,7 +1695,182 @@
|
||||
}
|
||||
},
|
||||
"tgbot": {
|
||||
"noIpRecord": "❗ Немає запису IP!"
|
||||
"noIpRecord": "❗ Немає запису IP!",
|
||||
"keyboardClosed": "❌ Клавіатуру закрито!",
|
||||
"noResult": "❗ Немає результату!",
|
||||
"noQuery": "❌ Запит не знайдено! Будь ласка, використовуйте команду ще раз!",
|
||||
"wentWrong": "❌ Щось пішло не так!",
|
||||
"noInbounds": "❗ Вхідні не знайдені!",
|
||||
"unlimited": "♾ Необмежено (Скинути)",
|
||||
"add": "Додати",
|
||||
"month": "Місяць",
|
||||
"months": "Місяці",
|
||||
"days": "Дні",
|
||||
"hours": "Години",
|
||||
"minutes": "Хвилини",
|
||||
"unknown": "Невідомо",
|
||||
"inbounds": "Вхідні",
|
||||
"clients": "Клієнти",
|
||||
"offline": "🔴 Не в мережі",
|
||||
"online": "🟢 У мережі",
|
||||
"commands": {
|
||||
"unknown": "❗ Невідома команда.",
|
||||
"pleaseChoose": "👇 Будь ласка, виберіть:\r\n",
|
||||
"help": "🤖 Ласкаво просимо до цього бота! Він розроблений, щоб надавати певні дані з веб-панелі та дозволяє вносити зміни за потреби.\r\n\r\n",
|
||||
"start": "👋 Привіт <i>{{ .Firstname }}</i>.\r\n",
|
||||
"welcome": "🤖 Ласкаво просимо до <b>{{ .Hostname }}</b> бота керування.\r\n",
|
||||
"status": "✅ Бот в порядку!",
|
||||
"usage": "❗ Введіть текст для пошуку!",
|
||||
"getID": "🆔 Ваш ідентифікатор: <code>{{ .ID }}</code>",
|
||||
"helpAdminCommands": "Для перезапуску Xray Core:\r\n<code>/restart</code>\r\n\r\nДля пошуку електронної пошти клієнта:\r\n<code>/usage [Електронна пошта]</code>\r\n\r\nДля пошуку вхідних (зі статистикою клієнта):\r\n<code>/inbound [Примітка]</code>\r\n\r\nID чату Telegram:\r\n<code>/id</code>",
|
||||
"helpClientCommands": "Для пошуку статистики використовуйте наступну команду:\r\n<code>/usage [Електронна пошта]</code>\r\n\r\nID чату Telegram:\r\n<code>/id</code>",
|
||||
"restartUsage": "\r\n\r\n<code>/restart</code>",
|
||||
"restartSuccess": "✅ Операція успішна!",
|
||||
"restartFailed": "❗ Помилка в операції.\r\n\r\n<code>Помилка: {{ .Error }}</code>.",
|
||||
"xrayNotRunning": "❗ Xray Core не запущений.",
|
||||
"startDesc": "Показати головне меню",
|
||||
"helpDesc": "Довідка по боту",
|
||||
"statusDesc": "Перевірити статус бота",
|
||||
"idDesc": "Показати ваш Telegram ID",
|
||||
"usageDesc": "Показати трафік клієнта: /usage email",
|
||||
"inboundDesc": "Пошук вхідних: /inbound назва (адмін)",
|
||||
"restartDesc": "Перезапустити ядро Xray (адмін)",
|
||||
"clearallDesc": "Скинути трафік усіх клієнтів (адмін)"
|
||||
},
|
||||
"messages": {
|
||||
"cpuThreshold": "Навантаження ЦП {{ .Percent }}% перевищує порогове значення {{ .Threshold }}%",
|
||||
"selectUserFailed": "❌ Помилка під час вибору користувача!",
|
||||
"userSaved": "✅ Користувача Telegram збережено.",
|
||||
"loginSuccess": "✅ Успішно ввійшли в панель\r\n",
|
||||
"loginFailed": "❗️ Помилка входу в панель.\r\n",
|
||||
"report": "🕰 Заплановані звіти: {{ .RunTime }}\r\n",
|
||||
"datetime": "⏰ Дата й час: {{ .DateTime }}\r\n",
|
||||
"hostname": "💻 Хост: {{ .Hostname }}\r\n",
|
||||
"version": "🚀 3X-UI Версія: {{ .Version }}\r\n",
|
||||
"xrayVersion": "📡 Xray Версія: {{ .XrayVersion }}\r\n",
|
||||
"ipv6": "🌐 IPv6: {{ .IPv6 }}\r\n",
|
||||
"ipv4": "🌐 IPv4: {{ .IPv4 }}\r\n",
|
||||
"ip": "🌐 IP: {{ .IP }}\r\n",
|
||||
"ips": "🔢 IP:\r\n{{ .IPs }}\r\n",
|
||||
"serverUpTime": "⏳ Час роботи: {{ .UpTime }} {{ .Unit }}\r\n",
|
||||
"serverLoad": "📈 Завантаження системи: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n",
|
||||
"serverMemory": "📋 RAM: {{ .Current }}/{{ .Total }}\r\n",
|
||||
"tcpCount": "🔹 TCP: {{ .Count }}\r\n",
|
||||
"udpCount": "🔸 UDP: {{ .Count }}\r\n",
|
||||
"traffic": "🚦 Трафік: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n",
|
||||
"xrayStatus": "ℹ️ Статус: {{ .State }}\r\n",
|
||||
"username": "👤 Ім'я користувача: {{ .Username }}\r\n",
|
||||
"reason": "❗️ Причина: {{ .Reason }}\r\n",
|
||||
"time": "⏰ Час: {{ .Time }}\r\n",
|
||||
"inbound": "📍 Вхідний: {{ .Remark }}\r\n",
|
||||
"port": "🔌 Порт: {{ .Port }}\r\n",
|
||||
"expire": "📅 Дата закінчення: {{ .Time }}\r\n",
|
||||
"expireIn": "📅 Термін дії: {{ .Time }}\r\n",
|
||||
"active": "💡 Активний: {{ .Enable }}\r\n",
|
||||
"enabled": "🚨 Увімкнено: {{ .Enable }}\r\n",
|
||||
"online": "🌐 Стан підключення: {{ .Status }}\r\n",
|
||||
"lastOnline": "🔙 Був(ла) онлайн: {{ .Time }}\r\n",
|
||||
"email": "📧 Email: {{ .Email }}\r\n",
|
||||
"upload": "🔼 Завантаження: ↑{{ .Upload }}\r\n",
|
||||
"download": "🔽 Завантаження: ↓{{ .Download }}\r\n",
|
||||
"total": "📊 Усього: ↑↓{{ .UpDown }} / {{ .Total }}\r\n",
|
||||
"TGUser": "👤 Користувач Telegram: {{ .TelegramID }}\r\n",
|
||||
"exhaustedCount": "🚨 Вичерпано кількість {{ .Type }} count:\r\n",
|
||||
"onlinesCount": "🌐 Онлайн-клієнти: {{ .Count }}\r\n",
|
||||
"disabled": "🛑 Вимкнено: {{ .Disabled }}\r\n",
|
||||
"depleteSoon": "🔜 Скоро вичерпається: {{ .Deplete }}\r\n\r\n",
|
||||
"backupTime": "🗄 Час резервного копіювання: {{ .Time }}\r\n",
|
||||
"refreshedOn": "\r\n📋🔄 Оновлено: {{ .Time }}\r\n\r\n",
|
||||
"yes": "✅ Так",
|
||||
"no": "❌ Ні",
|
||||
"received_email": "📧📥 Електронна пошта оновлена.",
|
||||
"received_comment": "💬📥 Коментар оновлено.",
|
||||
"email_prompt": "📧 Стандартний email: {{ .ClientEmail }}\n\nВведіть ваш email.",
|
||||
"comment_prompt": "💬 Стандартний коментар: {{ .ClientComment }}\n\nВведіть ваш коментар.",
|
||||
"cancel": "❌ Процес скасовано! \n\nВи можете знову розпочати, використовуючи /start у будь-який час. 🔄",
|
||||
"error_add_client": "⚠️ Помилка:\n\n {{ .error }}",
|
||||
"using_default_value": "Гаразд, залишу значення за замовчуванням. 😊",
|
||||
"incorrect_input": "Ваш ввід невірний.\nФрази повинні бути без пробілів.\nПравильний приклад: aaaaaa\nНеправильний приклад: aaa aaa 🚫",
|
||||
"AreYouSure": "Ви впевнені? 🤔",
|
||||
"SuccessResetTraffic": "📧 Електронна пошта: {{ .ClientEmail }}\n🏁 Результат: ✅ Успішно",
|
||||
"FailedResetTraffic": "📧 Електронна пошта: {{ .ClientEmail }}\n🏁 Результат: ❌ Невдача \n\n🛠️ Помилка: [ {{ .ErrorMessage }} ]",
|
||||
"FinishProcess": "🔚 Процес скидання трафіку завершено для всіх клієнтів.",
|
||||
"eventOutboundDown": "Вихідне з'єднання {{ .Tag }} НЕДОСТУПНЕ",
|
||||
"eventOutboundUp": "Вихідне з'єднання {{ .Tag }} ДОСТУПНЕ",
|
||||
"eventErrorDetail": "Помилка: {{ .Error }}",
|
||||
"eventDelayDetail": "Затримка: {{ .Delay }} мс",
|
||||
"eventXrayCrash": "Стався збій Xray",
|
||||
"eventXrayCrashError": "Помилка: {{ .Error }}",
|
||||
"eventNodeDown": "Вузол {{ .Name }} НЕДОСТУПНИЙ",
|
||||
"eventNodeUp": "Вузол {{ .Name }} ДОСТУПНИЙ",
|
||||
"eventLoginFallback": "Невдала спроба входу з {{ .Source }}",
|
||||
"memoryThreshold": "Використання пам'яті {{ .Percent }}% перевищує порогове значення {{ .Threshold }}%"
|
||||
},
|
||||
"buttons": {
|
||||
"closeKeyboard": "❌ Закрити клавіатуру",
|
||||
"cancel": "❌ Скасувати",
|
||||
"cancelReset": "❌ Скасувати скидання",
|
||||
"cancelIpLimit": "❌ Скасувати обмеження IP",
|
||||
"confirmResetTraffic": "✅ Підтвердити скидання трафіку?",
|
||||
"confirmClearIps": "✅ Підтвердити очищення IP-адрес?",
|
||||
"confirmRemoveTGUser": "✅ Підтвердити видалення користувача Telegram?",
|
||||
"confirmToggle": "✅ Підтвердити ввімкнути/вимкнути користувача?",
|
||||
"dbBackup": "Отримати резервну копію БД",
|
||||
"serverUsage": "Використання сервера",
|
||||
"getInbounds": "Отримати вхідні",
|
||||
"depleteSoon": "Скоро вичерпати",
|
||||
"clientUsage": "Отримати використання",
|
||||
"onlines": "Онлайн-клієнти",
|
||||
"commands": "Команди",
|
||||
"refresh": "🔄 Оновити",
|
||||
"clearIPs": "❌ Очистити IP-адреси",
|
||||
"removeTGUser": "❌ Видалити користувача Telegram",
|
||||
"selectTGUser": "👤 Виберіть користувача Telegram",
|
||||
"selectOneTGUser": "👤 Виберіть користувача Telegram:",
|
||||
"resetTraffic": "📈 Скинути трафік",
|
||||
"resetExpire": "📅 Змінити термін дії",
|
||||
"ipLog": "🔢 IP журнал",
|
||||
"ipLimit": "🔢 IP Ліміт",
|
||||
"setTGUser": "👤 Встановити користувача Telegram",
|
||||
"toggle": "🔘 Увімкнути / Вимкнути",
|
||||
"custom": "🔢 Своє",
|
||||
"confirmNumber": "✅ Підтвердити: {{ .Num }}",
|
||||
"confirmNumberAdd": "✅ Підтвердити додавання: {{ .Num }}",
|
||||
"limitTraffic": "🚧 Ліміт трафіку",
|
||||
"getBanLogs": "Отримати журнали заборон",
|
||||
"allClients": "Всі Клієнти",
|
||||
"addClient": "Додати клієнта",
|
||||
"submitDisable": "Надіслати як вимкнено ☑️",
|
||||
"submitEnable": "Надіслати як увімкнено ✅",
|
||||
"use_default": "🏷️ Використати типове",
|
||||
"change_email": "⚙️📧 Email",
|
||||
"change_comment": "⚙️💬 Коментар",
|
||||
"ResetAllTraffics": "Скинути весь трафік",
|
||||
"SortedTrafficUsageReport": "Відсортований звіт про використання трафіку"
|
||||
},
|
||||
"answers": {
|
||||
"successfulOperation": "✅ Операція успішна!",
|
||||
"errorOperation": "❗ Помилка в роботі.",
|
||||
"getInboundsFailed": "❌ Не вдалося отримати вхідні повідомлення.",
|
||||
"getClientsFailed": "❌ Не вдалося отримати клієнтів.",
|
||||
"canceled": "❌ {{ .Email }}: Операцію скасовано.",
|
||||
"clientRefreshSuccess": "✅ {{ .Email }}: Клієнт успішно оновлено.",
|
||||
"IpRefreshSuccess": "✅ {{ .Email }}: IP-адреси успішно оновлено.",
|
||||
"TGIdRefreshSuccess": "✅ {{ .Email }}: Користувач Telegram клієнта успішно оновлено.",
|
||||
"resetTrafficSuccess": "✅ {{ .Email }}: Трафік скинуто успішно.",
|
||||
"setTrafficLimitSuccess": "✅ {{ .Email }}: Ліміт трафіку успішно збережено.",
|
||||
"expireResetSuccess": "✅ {{ .Email }}: Успішно скинуто дні закінчення терміну дії.",
|
||||
"resetIpSuccess": "✅ {{ .Email }}: IP обмеження {{ .Count }} успішно збережено.",
|
||||
"clearIpSuccess": "✅ {{ .Email }}: IP успішно очищено.",
|
||||
"getIpLog": "✅ {{ .Email }}: Отримати IP-журнал.",
|
||||
"getUserInfo": "✅ {{ .Email }}: Отримати інформацію про користувача Telegram.",
|
||||
"removedTGUserSuccess": "✅ {{ .Email }}: Користувача Telegram видалено успішно.",
|
||||
"enableSuccess": "✅ {{ .Email }}: Увімкнути успішно.",
|
||||
"disableSuccess": "✅ {{ .Email }}: Успішно вимкнено.",
|
||||
"askToAddUserId": "Вашу конфігурацію не знайдено!\r\nБудь ласка, попросіть свого адміністратора використовувати ваш ідентифікатор Telegram у вашій конфігурації.\r\n\r\nВаш ідентифікатор користувача: <code>{{ .TgUserID }}</code>",
|
||||
"chooseClient": "Виберіть клієнта для Вхідного {{ .Inbound }}",
|
||||
"chooseInbound": "Виберіть Вхідний"
|
||||
}
|
||||
},
|
||||
"email": {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user