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": "Sin caducidad",
|
||||
"copyAllConfigs": "Copiar Todas las Configuraciones",
|
||||
"copyAllConfigsCopied": "Todas las configuraciones copiadas",
|
||||
"email": "Email"
|
||||
"email": "Email",
|
||||
"individualLinks": "Enlaces individuales"
|
||||
},
|
||||
"menu": {
|
||||
"theme": "Tema",
|
||||
@@ -1168,7 +1169,44 @@
|
||||
"httpMethodDesc": "Método usado para las solicitudes de probe.",
|
||||
"note": "Los balanceadores leastPing/leastLoad siempre llevan un burstObservatory. Este interruptor personaliza sus parámetros de probe — apágalo para usar los valores predeterminados integrados. Los cambios se aplican tras reiniciar el panel."
|
||||
}
|
||||
}
|
||||
},
|
||||
"TGBotSettings": "Bot de Telegram",
|
||||
"notifications": "Notificaciones",
|
||||
"notifyTime": {
|
||||
"custom": "Personalizado (crontab)",
|
||||
"daily": "@daily — cada día a las 00:00",
|
||||
"every": "@every — repetir en un intervalo",
|
||||
"hourly": "@hourly — cada hora",
|
||||
"hours": "Horas",
|
||||
"interval": "Intervalo",
|
||||
"minutes": "Minutos",
|
||||
"monthly": "@monthly — cada mes",
|
||||
"seconds": "Segundos",
|
||||
"unit": "Unidad",
|
||||
"weekly": "@weekly — cada semana"
|
||||
},
|
||||
"telegramAPIServer": "Servidor API de Telegram",
|
||||
"telegramAPIServerDesc": "El servidor API de Telegram a utilizar. Déjelo en blanco para utilizar el servidor predeterminado.",
|
||||
"telegramBotEnable": "Habilitar bot de Telegram",
|
||||
"telegramBotEnableDesc": "Conéctese a las funciones de este panel a través del bot de Telegram.",
|
||||
"telegramBotLanguage": "Idioma del Bot de Telegram",
|
||||
"telegramChatId": "IDs de Chat de Telegram para Administradores",
|
||||
"telegramChatIdDesc": "IDs de Chat múltiples separados por comas. Use {'@'}userinfobot o use el comando '/id' en el bot para obtener sus IDs de Chat.",
|
||||
"telegramNotifyTime": "Hora de Notificación del Bot de Telegram",
|
||||
"telegramNotifyTimeDesc": "Con qué frecuencia el bot de Telegram envía informes periódicos. Elige un intervalo predefinido o selecciona Personalizado para introducir una expresión crontab.",
|
||||
"telegramToken": "Token de Telegram",
|
||||
"telegramTokenConfigured": "Configurado; deje en blanco para mantener el token actual.",
|
||||
"telegramTokenDesc": "Debe obtener el token del administrador de bots de Telegram {'@'}botfather.",
|
||||
"telegramTokenPlaceholder": "Configurado: introduzca un nuevo token para reemplazarlo",
|
||||
"testTgBot": "Enviar mensaje de prueba",
|
||||
"tgBotNotEnabled": "El bot de Telegram no está activado",
|
||||
"tgBotNotRunning": "El bot de Telegram no está en ejecución",
|
||||
"tgEventBusNotify": "Notificaciones de Telegram de eventos",
|
||||
"tgEventBusNotifyDesc": "Seleccione qué eventos generan notificaciones de Telegram",
|
||||
"tgNotifyBackup": "Respaldo de Base de Datos",
|
||||
"tgNotifyBackupDesc": "Incluir archivo de respaldo de base de datos con notificación de informe.",
|
||||
"tgTestFailed": "La prueba de Telegram falló",
|
||||
"tgTestSuccess": "Mensaje de prueba enviado a Telegram"
|
||||
},
|
||||
"xray": {
|
||||
"save": "Guardar configuración",
|
||||
@@ -1657,7 +1695,182 @@
|
||||
}
|
||||
},
|
||||
"tgbot": {
|
||||
"noIpRecord": "❗ ¡No hay registro de IP!"
|
||||
"noIpRecord": "❗ ¡No hay registro de IP!",
|
||||
"keyboardClosed": "❌ Teclado cerrado!",
|
||||
"noResult": "❗ ¡Sin resultados!",
|
||||
"noQuery": "❌ ¡Consulta no encontrada! ¡Por favor, use el comando nuevamente!",
|
||||
"wentWrong": "❌ ¡Algo salió mal!",
|
||||
"noInbounds": "❗ ¡No se encontraron entradas!",
|
||||
"unlimited": "♾ Ilimitado (Restablecer)",
|
||||
"add": "Añadir",
|
||||
"month": "Mes",
|
||||
"months": "Meses",
|
||||
"days": "Días",
|
||||
"hours": "Horas",
|
||||
"minutes": "Minutos",
|
||||
"unknown": "Desconocido",
|
||||
"inbounds": "Entradas",
|
||||
"clients": "Clientes",
|
||||
"offline": "🔴 Sin conexión",
|
||||
"online": "🟢 En línea",
|
||||
"commands": {
|
||||
"unknown": "❗ Comando desconocido",
|
||||
"pleaseChoose": "👇 Por favor elige:\r\n",
|
||||
"help": "🤖 ¡Bienvenido a este bot! Está diseñado para ofrecerte datos específicos del servidor y te permite hacer modificaciones según sea necesario.\r\n\r\n",
|
||||
"start": "👋 Hola <i>{{ .Firstname }}</i>.\r\n",
|
||||
"welcome": "🤖 Bienvenido al bot de gestión de <b>{{ .Hostname }}</b>.\r\n",
|
||||
"status": "✅ ¡El bot está bien!",
|
||||
"usage": "❗ ¡Por favor proporciona un texto para buscar!",
|
||||
"getID": "🆔 Tu ID: <code>{{ .ID }}</code>",
|
||||
"helpAdminCommands": "Para reiniciar Xray Core:\r\n<code>/restart</code>\r\n\r\nPara buscar un correo electrónico de cliente:\r\n<code>/usage [Correo electrónico]</code>\r\n\r\nPara buscar entradas (con estadísticas de cliente):\r\n<code>/inbound [Observación]</code>\r\n\r\nID de Chat de Telegram:\r\n<code>/id</code>",
|
||||
"helpClientCommands": "Para buscar estadísticas, utiliza el siguiente comando:\r\n<code>/usage [Correo electrónico]</code>\r\n\r\nID de Chat de Telegram:\r\n<code>/id</code>",
|
||||
"restartUsage": "\r\n\r\n<code>/restart</code>",
|
||||
"restartSuccess": "✅ ¡Operación exitosa!",
|
||||
"restartFailed": "❗ Error en la operación.\r\n\r\n<code>Error: {{ .Error }}</code>.",
|
||||
"xrayNotRunning": "❗ Xray Core no está en ejecución.",
|
||||
"startDesc": "Mostrar el menú principal",
|
||||
"helpDesc": "Ayuda del bot",
|
||||
"statusDesc": "Comprobar el estado del bot",
|
||||
"idDesc": "Mostrar tu ID de Telegram",
|
||||
"usageDesc": "Ver el uso del cliente: /usage correo",
|
||||
"inboundDesc": "Buscar entradas: /inbound nombre (admin)",
|
||||
"restartDesc": "Reiniciar el núcleo de Xray (admin)",
|
||||
"clearallDesc": "Restablecer el tráfico de todos los clientes (admin)"
|
||||
},
|
||||
"messages": {
|
||||
"cpuThreshold": "El uso de CPU {{ .Percent }}% es mayor que el umbral {{ .Threshold }}%",
|
||||
"selectUserFailed": "❌ ¡Error al seleccionar usuario!",
|
||||
"userSaved": "✅ Usuario de Telegram guardado.",
|
||||
"loginSuccess": "✅ Has iniciado sesión en el panel con éxito.\r\n",
|
||||
"loginFailed": "❗️ Falló el inicio de sesión en el panel.\r\n",
|
||||
"report": "🕰 Informes programados: {{ .RunTime }}\r\n",
|
||||
"datetime": "⏰ Fecha y Hora: {{ .DateTime }}\r\n",
|
||||
"hostname": "💻 Host: {{ .Hostname }}\r\n",
|
||||
"version": "🚀 Versión de X-UI: {{ .Version }}\r\n",
|
||||
"xrayVersion": "📡 Versión de Xray: {{ .XrayVersion }}\r\n",
|
||||
"ipv6": "🌐 IPv6: {{ .IPv6 }}\r\n",
|
||||
"ipv4": "🌐 IPv4: {{ .IPv4 }}\r\n",
|
||||
"ip": "🌐 IP: {{ .IP }}\r\n",
|
||||
"ips": "🔢 IPs:\r\n{{ .IPs }}\r\n",
|
||||
"serverUpTime": "⏳ Tiempo de actividad del servidor: {{ .UpTime }} {{ .Unit }}\r\n",
|
||||
"serverLoad": "📈 Carga del servidor: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n",
|
||||
"serverMemory": "📋 RAM: {{ .Current }}/{{ .Total }}\r\n",
|
||||
"tcpCount": "🔹 TCP: {{ .Count }}\r\n",
|
||||
"udpCount": "🔸 UDP: {{ .Count }}\r\n",
|
||||
"traffic": "🚦 Tráfico: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n",
|
||||
"xrayStatus": "ℹ️ Estado: {{ .State }}\r\n",
|
||||
"username": "👤 Nombre de usuario: {{ .Username }}\r\n",
|
||||
"reason": "❗️ Motivo: {{ .Reason }}\r\n",
|
||||
"time": "⏰ Hora: {{ .Time }}\r\n",
|
||||
"inbound": "📍 Entrada: {{ .Remark }}\r\n",
|
||||
"port": "🔌 Puerto: {{ .Port }}\r\n",
|
||||
"expire": "📅 Fecha de Vencimiento: {{ .Time }}\r\n",
|
||||
"expireIn": "📅 Vence en: {{ .Time }}\r\n",
|
||||
"active": "💡 Activo: {{ .Enable }}\r\n",
|
||||
"enabled": "🚨 Habilitado: {{ .Enable }}\r\n",
|
||||
"online": "🌐 Estado de conexión: {{ .Status }}\r\n",
|
||||
"lastOnline": "🔙 Última conexión: {{ .Time }}\r\n",
|
||||
"email": "📧 Email: {{ .Email }}\r\n",
|
||||
"upload": "🔼 Subida: ↑{{ .Upload }}\r\n",
|
||||
"download": "🔽 Descarga: ↓{{ .Download }}\r\n",
|
||||
"total": "📊 Total: ↑↓{{ .UpDown }} / {{ .Total }}\r\n",
|
||||
"TGUser": "👤 Usuario de Telegram: {{ .TelegramID }}\r\n",
|
||||
"exhaustedCount": "🚨 Cantidad de Agotados {{ .Type }}:\r\n",
|
||||
"onlinesCount": "🌐 Clientes en línea: {{ .Count }}\r\n",
|
||||
"disabled": "🛑 Desactivado: {{ .Disabled }}\r\n",
|
||||
"depleteSoon": "🔜 Se agotará pronto: {{ .Deplete }}\r\n\r\n",
|
||||
"backupTime": "🗄 Hora de la Copia de Seguridad: {{ .Time }}\r\n",
|
||||
"refreshedOn": "\r\n📋🔄 Actualizado en: {{ .Time }}\r\n\r\n",
|
||||
"yes": "✅ Sí",
|
||||
"no": "❌ No",
|
||||
"received_email": "📧📥 Correo electrónico actualizado.",
|
||||
"received_comment": "💬📥 Comentario actualizado.",
|
||||
"email_prompt": "📧 Correo electrónico predeterminado: {{ .ClientEmail }}\n\nIntroduce tu correo electrónico.",
|
||||
"comment_prompt": "💬 Comentario predeterminado: {{ .ClientComment }}\n\nIntroduce tu comentario.",
|
||||
"cancel": "❌ ¡Proceso cancelado! \n\nPuedes /start de nuevo en cualquier momento. 🔄",
|
||||
"error_add_client": "⚠️ Error:\n\n {{ .error }}",
|
||||
"using_default_value": "Está bien, me quedaré con el valor predeterminado. 😊",
|
||||
"incorrect_input": "Tu entrada no es válida.\nLas frases deben ser continuas sin espacios.\nEjemplo correcto: aaaaaa\nEjemplo incorrecto: aaa aaa 🚫",
|
||||
"AreYouSure": "¿Estás seguro? 🤔",
|
||||
"SuccessResetTraffic": "📧 Correo: {{ .ClientEmail }}\n🏁 Resultado: ✅ Éxito",
|
||||
"FailedResetTraffic": "📧 Correo: {{ .ClientEmail }}\n🏁 Resultado: ❌ Fallido \n\n🛠️ Error: [ {{ .ErrorMessage }} ]",
|
||||
"FinishProcess": "🔚 Proceso de reinicio de tráfico finalizado para todos los clientes.",
|
||||
"eventOutboundDown": "El saliente {{ .Tag }} está CAÍDO",
|
||||
"eventOutboundUp": "El saliente {{ .Tag }} está ACTIVO",
|
||||
"eventErrorDetail": "Error: {{ .Error }}",
|
||||
"eventDelayDetail": "Retardo: {{ .Delay }} ms",
|
||||
"eventXrayCrash": "Xray se ha BLOQUEADO",
|
||||
"eventXrayCrashError": "Error: {{ .Error }}",
|
||||
"eventNodeDown": "El nodo {{ .Name }} está CAÍDO",
|
||||
"eventNodeUp": "El nodo {{ .Name }} está ACTIVO",
|
||||
"eventLoginFallback": "Inicio de sesión fallido desde {{ .Source }}",
|
||||
"memoryThreshold": "Uso de memoria {{ .Percent }}% supera el umbral de {{ .Threshold }}%"
|
||||
},
|
||||
"buttons": {
|
||||
"closeKeyboard": "❌ Cerrar Teclado",
|
||||
"cancel": "❌ Cancelar",
|
||||
"cancelReset": "❌ Cancelar Reinicio",
|
||||
"cancelIpLimit": "❌ Cancelar Límite de IP",
|
||||
"confirmResetTraffic": "✅ ¿Confirmar Reinicio de Tráfico?",
|
||||
"confirmClearIps": "✅ ¿Confirmar Limpiar IPs?",
|
||||
"confirmRemoveTGUser": "✅ ¿Confirmar Eliminar Usuario de Telegram?",
|
||||
"confirmToggle": "✅ ¿Confirmar habilitar/deshabilitar usuario?",
|
||||
"dbBackup": "Obtener Copia de Seguridad de BD",
|
||||
"serverUsage": "Uso del Servidor",
|
||||
"getInbounds": "Obtener Entradas",
|
||||
"depleteSoon": "Pronto se Agotará",
|
||||
"clientUsage": "Obtener Uso",
|
||||
"onlines": "Clientes en línea",
|
||||
"commands": "Comandos",
|
||||
"refresh": "🔄 Actualizar",
|
||||
"clearIPs": "❌ Limpiar IPs",
|
||||
"removeTGUser": "❌ Eliminar Usuario de Telegram",
|
||||
"selectTGUser": "👤 Seleccionar Usuario de Telegram",
|
||||
"selectOneTGUser": "👤 Selecciona un usuario de telegram:",
|
||||
"resetTraffic": "📈 Reiniciar Tráfico",
|
||||
"resetExpire": "📅 Cambiar fecha de Vencimiento",
|
||||
"ipLog": "🔢 Registro de IP",
|
||||
"ipLimit": "🔢 Límite de IP",
|
||||
"setTGUser": "👤 Establecer Usuario de Telegram",
|
||||
"toggle": "🔘 Habilitar / Deshabilitar",
|
||||
"custom": "🔢 Personalizado",
|
||||
"confirmNumber": "✅ Confirmar: {{ .Num }}",
|
||||
"confirmNumberAdd": "✅ Confirmar agregando: {{ .Num }}",
|
||||
"limitTraffic": "🚧 Límite de tráfico",
|
||||
"getBanLogs": "Registros de prohibición",
|
||||
"allClients": "Todos los Clientes",
|
||||
"addClient": "Añadir cliente",
|
||||
"submitDisable": "Enviar como deshabilitado ☑️",
|
||||
"submitEnable": "Enviar como habilitado ✅",
|
||||
"use_default": "🏷️ Usar por defecto",
|
||||
"change_email": "⚙️📧 Email",
|
||||
"change_comment": "⚙️💬 Comentario",
|
||||
"ResetAllTraffics": "Reiniciar todo el tráfico",
|
||||
"SortedTrafficUsageReport": "Informe de uso de tráfico ordenado"
|
||||
},
|
||||
"answers": {
|
||||
"successfulOperation": "✅ ¡Exitosa!",
|
||||
"errorOperation": "❗ Error en la Operación.",
|
||||
"getInboundsFailed": "❌ Error al obtener las entradas",
|
||||
"getClientsFailed": "❌ No se pudo obtener los clientes.",
|
||||
"canceled": "❌ {{ .Email }} : Operación cancelada.",
|
||||
"clientRefreshSuccess": "✅ {{ .Email }} : Cliente actualizado exitosamente.",
|
||||
"IpRefreshSuccess": "✅ {{ .Email }} : IPs actualizadas exitosamente.",
|
||||
"TGIdRefreshSuccess": "✅ {{ .Email }} : Usuario de Telegram del cliente actualizado exitosamente.",
|
||||
"resetTrafficSuccess": "✅ {{ .Email }} : Tráfico reiniciado exitosamente.",
|
||||
"setTrafficLimitSuccess": "✅ {{ .Email }} : Límite de Tráfico guardado exitosamente.",
|
||||
"expireResetSuccess": "✅ {{ .Email }} : Días de vencimiento reiniciados exitosamente.",
|
||||
"resetIpSuccess": "✅ {{ .Email }} : Límite de IP {{ .Count }} guardado exitosamente.",
|
||||
"clearIpSuccess": "✅ {{ .Email }} : IPs limpiadas exitosamente.",
|
||||
"getIpLog": "✅ {{ .Email }} : Obtener Registro de IP.",
|
||||
"getUserInfo": "✅ {{ .Email }} : Obtener Información de Usuario de Telegram.",
|
||||
"removedTGUserSuccess": "✅ {{ .Email }} : Usuario de Telegram eliminado exitosamente.",
|
||||
"enableSuccess": "✅ {{ .Email }} : Habilitado exitosamente.",
|
||||
"disableSuccess": "✅ {{ .Email }} : Deshabilitado exitosamente.",
|
||||
"askToAddUserId": "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su ChatID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu ChatID de usuario: <code>{{ .TgUserID }}</code>",
|
||||
"chooseClient": "Elige un Cliente para Inbound {{ .Inbound }}",
|
||||
"chooseInbound": "Elige un Inbound"
|
||||
}
|
||||
},
|
||||
"email": {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user