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:
Muse
2026-10-21 17:16:32 +00:00
parent 5031a70ccb
commit ceca96571d
49 changed files with 8351 additions and 50 deletions
+216 -3
View File
@@ -88,7 +88,8 @@
"noExpiry": "Süresiz",
"copyAllConfigs": "Tüm Yapılandırmaları Kopyala",
"copyAllConfigsCopied": "Tüm yapılandırmalar kopyalandı",
"email": "E-posta"
"email": "E-posta",
"individualLinks": "Bireysel Bağlantılar"
},
"menu": {
"theme": "Tema",
@@ -1168,7 +1169,44 @@
"httpMethodDesc": "Probe isteklerinde kullanılan HTTP yöntemi.",
"note": "leastPing/leastLoad dengeleyicileri her zaman bir burstObservatory taşır. Bu anahtar probe parametrelerini özelleştirir — yerleşik varsayılanları kullanmak için kapatın. Değişiklikler panel yeniden başlatıldıktan sonra uygulanır."
}
}
},
"TGBotSettings": "Telegram Botu",
"notifications": "Bildirimler",
"notifyTime": {
"custom": "Özel (crontab)",
"daily": "@daily — her gün 00:00'da",
"every": "@every — bir aralıkla tekrarla",
"hourly": "@hourly — her saat",
"hours": "Saat",
"interval": "Aralık",
"minutes": "Dakika",
"monthly": "@monthly — her ay",
"seconds": "Saniye",
"unit": "Birim",
"weekly": "@weekly — her hafta"
},
"telegramAPIServer": "Telegram API Sunucusu",
"telegramAPIServerDesc": "Kullanılacak Telegram API sunucusu. Varsayılan sunucuyu kullanmak için boş bırakın.",
"telegramBotEnable": "Telegram Botunu Etkinleştir",
"telegramBotEnableDesc": "Telegram botunu etkinleştirir.",
"telegramBotLanguage": "Telegram Bot Dili",
"telegramChatId": "Yönetici Sohbet Kimliği",
"telegramChatIdDesc": "Telegram Yönetici Sohbet Kimliği (Chat ID). Birden fazla ise virgülle ayırın. ({'@'}userinfobot'tan alabilirsiniz veya botta '/id' komutunu kullanabilirsiniz.)",
"telegramNotifyTime": "Bildirim Zamanı",
"telegramNotifyTimeDesc": "Telegram botunun periyodik raporları gönderme sıklığı. Hazır bir aralık seçin veya bir crontab ifadesi girmek için Özel'i seçin.",
"telegramToken": "Telegram Token",
"telegramTokenConfigured": "Yapılandırıldı; mevcut belirteci korumak için boş bırakın.",
"telegramTokenDesc": "'{'@'}BotFather'dan alınan Telegram bot token.",
"telegramTokenPlaceholder": "Yapılandırıldı - değiştirmek için yeni bir belirteç girin",
"testTgBot": "Test Mesajı Gönder",
"tgBotNotEnabled": "Telegram botu etkin değil",
"tgBotNotRunning": "Telegram botu çalışmıyor",
"tgEventBusNotify": "Telegram Olay Bildirimleri",
"tgEventBusNotifyDesc": "Hangi olayların Telegram bildirimi tetikleyeceğini seçin",
"tgNotifyBackup": "Veritabanı Yedeği",
"tgNotifyBackupDesc": "Bir rapor ile birlikte veritabanı yedek dosyasını gönderir.",
"tgTestFailed": "Telegram testi başarısız oldu",
"tgTestSuccess": "Test mesajı Telegram'a gönderildi"
},
"xray": {
"save": "Kaydet",
@@ -1657,7 +1695,182 @@
}
},
"tgbot": {
"noIpRecord": "❗ IP Kaydı Yok!"
"noIpRecord": "❗ IP Kaydı Yok!",
"keyboardClosed": "❌ Klavye kapatıldı!",
"noResult": "❗ Sonuç yok!",
"noQuery": "❌ Sorgu bulunamadı! Lütfen komutu tekrar kullanın!",
"wentWrong": "❌ Bir şeyler yanlış gitti!",
"noInbounds": "❗ Gelen bağlantı bulunamadı!",
"unlimited": "♾ Sınırsız (Sıfırla)",
"add": "Ekle",
"month": "Ay",
"months": "Aylar",
"days": "Günler",
"hours": "Saatler",
"minutes": "Dakikalar",
"unknown": "Bilinmeyen",
"inbounds": "Gelen Bağlantılar",
"clients": "Kullanıcılar",
"offline": "🔴 Çevrimdışı",
"online": "🟢 Çevrimiçi",
"commands": {
"unknown": "❗ Bilinmeyen komut.",
"pleaseChoose": "👇 Lütfen seçin:\r\n",
"help": "🤖 Bu bota hoş geldiniz! Web panelinden belirli verileri sunmak ve gerektiğinde değişiklik yapmanıza olanak tanımak için tasarlanmıştır.\r\n\r\n",
"start": "👋 Merhaba <i>{{ .Firstname }}</i>.\r\n",
"welcome": "🤖 <b>{{ .Hostname }}</b> yönetim botuna hoş geldiniz.\r\n",
"status": "✅ Bot çalışıyor!",
"usage": "❗ Lütfen aramak için bir metin sağlayın!",
"getID": "🆔 Kimliğiniz: <code>{{ .ID }}</code>",
"helpAdminCommands": "Xray Core'u yeniden başlatmak için:\r\n<code>/restart</code>\r\n\r\nBir kullanıcının istatistiklerini aramak için:\r\n<code>/usage [E-posta]</code>\r\n\r\nGelen bağlantılarnı aramak için (kullanıcı istatistikleri ile):\r\n<code>/inbound [Açıklama]</code>\r\n\r\nTelegram Sohbet Kimliği (Chat ID):\r\n<code>/id</code>",
"helpClientCommands": "İstatistiklerinizi görmek için şu komutu kullanın:\r\n\r\n<code>/usage [E-posta]</code>\r\n\r\nTelegram Sohbet Kimliği:\r\n<code>/id</code>",
"restartUsage": "<code>/restart</code>",
"restartSuccess": "✅ İşlem başarılı!",
"restartFailed": "❗ İşlem hatası.\r\n\r\n<code>Hata: {{ .Error }}</code>.",
"xrayNotRunning": "❗ Xray Core çalışmıyor.",
"startDesc": "Ana menüyü göster",
"helpDesc": "Bot yardımı",
"statusDesc": "Bot durumunu kontrol et",
"idDesc": "Telegram Kimliğinizi gösterir",
"usageDesc": "İstemci kullanımını göster: /usage e-posta",
"inboundDesc": "Gelenleri ara: /inbound ad (yönetici)",
"restartDesc": "Xray çekirdeğini yeniden başlat (yönetici)",
"clearallDesc": "Tüm istemcilerin trafiğini sıfırla (yönetici)"
},
"messages": {
"cpuThreshold": "CPU Yükü ({{ .Percent }}%), {{ .Threshold }}% eşiğini aşıyor",
"selectUserFailed": "❌ Kullanıcı seçiminde hata!",
"userSaved": "✅ Telegram Kullanıcısı kaydedildi.",
"loginSuccess": "✅ Panele başarıyla giriş yapıldı.\r\n",
"loginFailed": "❗️Panele giriş denemesi başarısız oldu.\r\n",
"report": "🕰 Planlanmış Raporlar: {{ .RunTime }}\r\n",
"datetime": "⏰ Tarih ve Saat: {{ .DateTime }}\r\n",
"hostname": "💻 Sunucu: {{ .Hostname }}",
"version": "🚀 3X-UI Sürümü: {{ .Version }}\r\n",
"xrayVersion": "📡 Xray Sürümü: {{ .XrayVersion }}\r\n",
"ipv6": "🌐 IPv6: {{ .IPv6 }}",
"ipv4": "🌐 IPv4: {{ .IPv4 }}",
"ip": "🌐 IP: {{ .IP }}",
"ips": "🔢 IP'ler:\r\n{{ .IPs }}\r\n",
"serverUpTime": "⏳ Çalışma Süresi: {{ .UpTime }} {{ .Unit }}\r\n",
"serverLoad": "📈 Sistem Yükü: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n",
"serverMemory": "📋 RAM: {{ .Current }}/{{ .Total }}",
"tcpCount": "🔹 TCP: {{ .Count }}",
"udpCount": "🔸 UDP: {{ .Count }}",
"traffic": "🚦 Trafik: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n",
"xrayStatus": "ℹ️ Durum: {{ .State }}\r\n",
"username": "👤 Kullanıcı Adı: {{ .Username }}\r\n",
"reason": "❗️ Sebep: {{ .Reason }}\r\n",
"time": "⏰ Zaman: {{ .Time }}\r\n",
"inbound": "📍 Gelen Bağlantı: {{ .Remark }}\r\n",
"port": "🔌 Bağlantı Noktası: {{ .Port }}",
"expire": "📅 Son Kullanma Tarihi: {{ .Time }}\r\n",
"expireIn": "📅 Sona Erecek: {{ .Time }}\r\n",
"active": "💡 Aktif: {{ .Enable }}\r\n",
"enabled": "🚨 Etkin: {{ .Enable }}\r\n",
"online": "🌐 Bağlantı durumu: {{ .Status }}\r\n",
"lastOnline": "🔙 Son Çevrimiçi: {{ .Time }}\r\n",
"email": "📧 E-posta: {{ .Email }}\r\n",
"upload": "🔼 Yükleme: ↑{{ .Upload }}\r\n",
"download": "🔽 İndirme: ↓{{ .Download }}\r\n",
"total": "📊 Toplam: ↑↓{{ .UpDown }} / {{ .Total }}\r\n",
"TGUser": "👤 Telegram Kullanıcısı: {{ .TelegramID }}\r\n",
"exhaustedCount": "🚨 Limiti Dolan {{ .Type }} sayısı:\r\n",
"onlinesCount": "🌐 Çevrimiçi Kullanıcılar: {{ .Count }}\r\n",
"disabled": "🛑 Devre Dışı: {{ .Disabled }}\r\n",
"depleteSoon": "🔜 Kotası Dolmak Üzere: {{ .Deplete }}\r\n\r\n",
"backupTime": "🗄 Yedekleme Zamanı: {{ .Time }}\r\n",
"refreshedOn": "\r\n📋🔄 Yenilendi: {{ .Time }}\r\n\r\n",
"yes": "✅ Evet",
"no": "❌ Hayır",
"received_email": "📧📥 E-posta güncellendi.",
"received_comment": "💬📥 Yorum güncellendi.",
"email_prompt": "📧 Varsayılan E-posta: {{ .ClientEmail }}\n\nE-postanızı girin.",
"comment_prompt": "💬 Varsayılan Yorum: {{ .ClientComment }}\n\nYorumunuzu girin.",
"cancel": "❌ İşlem iptal edildi! \n\nİstediğiniz zaman /start ile yeniden başlayabilirsiniz. 🔄",
"error_add_client": "⚠️ Hata:\n\n {{ .error }}",
"using_default_value": "Tamam, varsayılan değeri kullanacağım. 😊",
"incorrect_input": "Geçersiz değer girdiniz.\nİfadeler arasında boşluk olmamalıdır.\nDoğru örnek: aaaaaa\nYanlış örnek: aaa aaa 🚫",
"AreYouSure": "Emin misiniz? 🤔",
"SuccessResetTraffic": "📧 E-posta: {{ .ClientEmail }}\n🏁 Sonuç: ✅ Başarılı",
"FailedResetTraffic": "📧 E-posta: {{ .ClientEmail }}\n🏁 Sonuç: ❌ Başarısız \n\n🛠️ Hata: [ {{ .ErrorMessage }} ]",
"FinishProcess": "🔚 Tüm kullanıcılar için trafik sıfırlama işlemi tamamlandı.",
"eventOutboundDown": "{{ .Tag }} giden bağlantısı ÇEVRİMDIŞI",
"eventOutboundUp": "{{ .Tag }} giden bağlantısı ÇEVRİMİÇİ",
"eventErrorDetail": "Hata: {{ .Error }}",
"eventDelayDetail": "Gecikme: {{ .Delay }}ms",
"eventXrayCrash": "Xray ÇÖKTÜ",
"eventXrayCrashError": "Hata: {{ .Error }}",
"eventNodeDown": "{{ .Name }} düğümü ÇEVRİMDIŞI",
"eventNodeUp": "{{ .Name }} düğümü ÇEVRİMİÇİ",
"eventLoginFallback": "{{ .Source }} adresinden oturum açma başarısız",
"memoryThreshold": "Bellek kullanımı {{ .Percent }}% eşiği {{ .Threshold }}% aşıyor"
},
"buttons": {
"closeKeyboard": "❌ Klavyeyi Kapat",
"cancel": "❌ İptal",
"cancelReset": "❌ Sıfırlamayı İptal Et",
"cancelIpLimit": "❌ IP Limitini İptal Et",
"confirmResetTraffic": "✅ Trafiği Sıfırlamayı Onayla?",
"confirmClearIps": "✅ IP'leri Temizlemeyi Onayla?",
"confirmRemoveTGUser": "✅ Telegram Kullanıcısını Kaldırmayı Onayla?",
"confirmToggle": "✅ Kullanıcıyı Etkinleştirme/Devre Dışı Bırakmayı Onayla?",
"dbBackup": "Veritabanı Yedeği Al",
"serverUsage": "Sunucu Kullanımı",
"getInbounds": "Gelen Bağlantılarnı Al",
"depleteSoon": "Kotası Dolmak Üzere",
"clientUsage": "Kullanıcı İstatistikleri",
"onlines": "Çevrimiçi Kullanıcılar",
"commands": "Komutlar",
"refresh": "🔄 Yenile",
"clearIPs": "❌ IP'leri Temizle",
"removeTGUser": "❌ Telegram Kullanıcısını Kaldır",
"selectTGUser": "👤 Telegram Kullanıcısını Seç",
"selectOneTGUser": "👤 Bir Telegram Kullanıcısını Seçin:",
"resetTraffic": "📈 Trafiği Sıfırla",
"resetExpire": "📅 Son Kullanma Tarihini Değiştir",
"ipLog": "🔢 IP Günlüğü",
"ipLimit": "🔢 IP Limiti",
"setTGUser": "👤 Telegram Kullanıcısını Ayarla",
"toggle": "🔘 Etkinleştir / Devre Dışı Bırak",
"custom": "🔢 Özel",
"confirmNumber": "✅ Onayla: {{ .Num }}",
"confirmNumberAdd": "✅ Ekleme onayı: {{ .Num }}",
"limitTraffic": "🚧 Trafik Sınırı",
"getBanLogs": "Yasak Günlüklerini Al",
"allClients": "Tüm Kullanıcılar",
"addClient": "Kullanıcı Ekle",
"submitDisable": "Devre Dışı Olarak Gönder ☑️",
"submitEnable": "Etkin Olarak Gönder ✅",
"use_default": "🏷️ Varsayılanı Kullan",
"change_email": "⚙️📧 E-posta",
"change_comment": "⚙️💬 Yorum",
"ResetAllTraffics": "Tüm Trafikleri Sıfırla",
"SortedTrafficUsageReport": "Sıralı Trafik Kullanım Raporu"
},
"answers": {
"successfulOperation": "✅ İşlem başarılı!",
"errorOperation": "❗ İşlemde hata.",
"getInboundsFailed": "❌ Gelen Bağlantılar alınamadı.",
"getClientsFailed": "❌ Kullanıcılar alınamadı.",
"canceled": "❌ {{ .Email }}: İşlem iptal edildi.",
"clientRefreshSuccess": "✅ {{ .Email }}: Kullanıcı başarıyla yenilendi.",
"IpRefreshSuccess": "✅ {{ .Email }}: IP'ler başarıyla yenilendi.",
"TGIdRefreshSuccess": "✅ {{ .Email }}: Kullanıcının Telegram Kimliği başarıyla yenilendi.",
"resetTrafficSuccess": "✅ {{ .Email }}: Trafik başarıyla sıfırlandı.",
"setTrafficLimitSuccess": "✅ {{ .Email }}: Trafik limiti başarıyla kaydedildi.",
"expireResetSuccess": "✅ {{ .Email }}: Son kullanma günleri başarıyla sıfırlandı.",
"resetIpSuccess": "✅ {{ .Email }}: IP limiti {{ .Count }} başarıyla kaydedildi.",
"clearIpSuccess": "✅ {{ .Email }}: IP'ler başarıyla temizlendi.",
"getIpLog": "✅ {{ .Email }}: IP Günlüğü alındı.",
"getUserInfo": "✅ {{ .Email }}: Telegram Kullanıcı Bilgisi alındı.",
"removedTGUserSuccess": "✅ {{ .Email }}: Telegram Kullanıcısı başarıyla kaldırıldı.",
"enableSuccess": "✅ {{ .Email }}: Başarıyla etkinleştirildi.",
"disableSuccess": "✅ {{ .Email }}: Başarıyla devre dışı bırakıldı.",
"askToAddUserId": "Yapılandırmanız bulunamadı!\r\nLütfen yöneticinizden Telegram Chat ID'nizi yapılandırmanıza eklemesini isteyin.\r\n\r\nSizin Chat ID'niz: <code>{{ .TgUserID }}</code>",
"chooseClient": "Gelen Bağlantı {{ .Inbound }} için bir Kullanıcı Seçin",
"chooseInbound": "Bir Gelen Bağlantı Seçin"
}
},
"email": {}
}