feat(tgbot): rebuild the Telegram bot around editable screens and inline lists (#6760)

The bot answered every action with a new message, so a chat filled with stale
menus whose buttons still acted, and long client or inbound lists were paged
through dozens of buttons. Each chat now has one live screen that is edited in
place: the menu is grouped into Server, Clients, Traffic and Maintenance, lists
open through inline search, the add-client flow starts from a draft card, QR
codes arrive as one labelled sheet, and repeated notices collapse into one with
a counter. A client's /start and /usage show only that client's own links and
usage, groups are refused, and the settings page warns when the bot's inline
mode is off. Admin topics, the indexed Telegram lookup and every locale,
Slovak included, carry over.
This commit is contained in:
DIMFLIX
2026-10-24 13:39:56 +03:00
committed by GitHub
parent bc1b750e00
commit 15fee2a035
62 changed files with 7449 additions and 1945 deletions
+82 -31
View File
@@ -85,7 +85,6 @@
"uploaded": "Subido",
"expiry": "Caducidad",
"totalQuota": "Cuota total",
"individualLinks": "Enlaces individuales",
"active": "Activo",
"inactive": "Inactivo",
"unlimited": "Ilimitado",
@@ -1830,6 +1829,11 @@
"subIncyResolveDnsIp": "IP del servidor DoH",
"subIncyResolveDnsIpDesc": "IP del servidor DoH para el arranque.",
"subIncyGroupRouting": "Enrutado",
"tgInlineMissing": "Inline mode is disabled",
"tgInlineMissingDesc": "Lists in the bot open through inline search. Enable it in @BotFather with /setinline, otherwise tapping a search button does nothing.",
"tgInlineRecheck": "Check again",
"tgPrivacyOn": "La privacidad de grupos está activada",
"tgPrivacyOnDesc": "Este bot responde solo en el chat privado, así que ignora los grupos a propósito. La privacidad de grupos solo decide si el bot puede leer los mensajes del grupo, cosa que no usa.",
"subHostSortMode": "Orden de nodos de suscripción",
"subHostSortInbound": "Orden de entradas (predeterminado)",
"subHostSortHosts": "Orden de hosts",
@@ -2486,7 +2490,6 @@
}
},
"tgbot": {
"keyboardClosed": "❌ Teclado cerrado!",
"noResult": "❗ ¡Sin resultados!",
"noQuery": "❌ ¡Consulta no encontrada! ¡Por favor, use el comando nuevamente!",
"wentWrong": "❌ ¡Algo salió mal!",
@@ -2508,10 +2511,6 @@
"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>",
@@ -2520,14 +2519,7 @@
"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)",
"broadcastDesc": "Enviar un mensaje a todos los clientes (admin)",
"clearallDesc": "Restablecer el tráfico de todos los clientes (admin)"
"statusOk": "✅ ¡El bot funciona!"
},
"messages": {
"cpuThreshold": "El uso de CPU {{ .Percent }}% es mayor que el umbral {{ .Threshold }}%",
@@ -2571,7 +2563,6 @@
"inviteInvalid": "❗ Este enlace de invitación no es válido o ya se ha utilizado.\r\n",
"inviteRateLimited": "⏳ Demasiados intentos de invitación. Inténtalo más tarde.\r\n",
"inviteRateLimitedAdmin": "⚠️ Intentos de invitación bloqueados para {{ .User }} (<code>{{ .ID }}</code>): más de {{ .Limit }} intentos en una hora. Alguien podría estar adivinando IDs de suscripción.",
"inviteLink": "🔗 Enlace de invitación para <code>{{ .Email }}</code>:\r\n<code>{{ .Link }}</code>\r\n\r\nLa primera persona que lo abra quedará vinculada a este cliente.\r\n",
"exhaustedCount": "🚨 Cantidad de Agotados {{ .Type }}:\r\n",
"onlinesCount": "🌐 Clientes en línea: {{ .Count }}\r\n",
"disabled": "🛑 Desactivado: {{ .Disabled }}\r\n",
@@ -2580,13 +2571,11 @@
"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.",
"tgid_prompt": "🆔 Usuario TG predeterminado: {{ .ClientTGID }}\n\nEnvía el id numérico del usuario para vincularlo a este cliente, o envía - / none para borrarlo.",
"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 🚫",
"broadcastAskText": "📤 Difusión: envíame el mensaje para entregarlo a tus clientes — texto, foto, vídeo, archivo o un álbum completo.",
"broadcastAlreadyRunning": "❗ Ya hay una difusión en curso, espera a que termine.",
@@ -2610,7 +2599,23 @@
"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 }}%"
"memoryThreshold": "Uso de memoria {{ .Percent }}% supera el umbral de {{ .Threshold }}%",
"inboundsCount": "🔌 Inbounds: {{ .Count }}\r\n",
"clientsCount": "👥 Clients: {{ .Count }}\r\n",
"category_server": "🖥 Servidor",
"category_clients": "👥 Clientes",
"category_traffic": "🚦 Tráfico",
"category_maintenance": "⚙️ Mantenimiento",
"inlineHint": "\r\n🔍 The list opens through search: tap Find and start typing.",
"subURL": "🔗 Subscription: <code>{{ .URL }}</code>\r\n",
"jsonURL": "🧩 JSON: <code>{{ .URL }}</code>\r\n",
"inviteHint": "Send this link to the client: the first Telegram account to open it is bound to the client.",
"qrLegendHeader": "🔳 Códigos QR:",
"qrLegendSubscription": "{{ .Code }}. Suscripción",
"qrLegendJSON": "{{ .Code }}. Suscripción JSON",
"qrLegendLink": "{{ .Code }}. Enlace de la app {{ .App }}",
"backupFailed": "No se pudo leer la base de datos; no se envió ninguna copia de seguridad.",
"privateOnly": "🤖 Este bot solo funciona en el chat privado. Ábrelo en un mensaje directo."
},
"buttons": {
"closeKeyboard": "❌ Cerrar Teclado",
@@ -2622,13 +2627,15 @@
"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",
"dbBackup": "💾 Obtener Copia de Seguridad de BD",
"serverUsage": "📊 Uso del Servidor",
"getInbounds": "🔌 Obtener Entradas",
"depleteSoon": "⚠️ Pronto se Agotará",
"onlines": "🟢 Clientes en línea",
"catServer": "🖥 Servidor",
"catClients": "👥 Clientes",
"catTraffic": "🚦 Tráfico",
"catMaintenance": "⚙️ Mantenimiento",
"refresh": "🔄 Actualizar",
"clearIPs": "❌ Limpiar IPs",
"removeTGUser": "❌ Eliminar Usuario de Telegram",
@@ -2645,16 +2652,30 @@
"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",
"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"
"ResetAllTraffics": "♻️ Reiniciar todo el tráfico",
"SortedTrafficUsageReport": "📈 Informe de uso de tráfico ordenado",
"restartXray": "♻️ Restart Xray",
"back": "⬅️ Back",
"hide": "🙈 Hide",
"page": "Page {{ .Page }} of {{ .Pages }}",
"searchInbounds": "🔍 Find inbound…",
"searchClients": "🔍 Find client…",
"inlineDisabled": "⚠️ Inline mode is off",
"clientCard": "⬅️ Client card",
"links": "🔗 Links",
"individualLinks": "🔗 Individual links",
"qrCode": "🔳 QR code",
"broadcast": "📢 Difusión",
"attachInbound": "Adjuntar entrada ({{ .Count }})",
"attachDone": "✅ Listo"
},
"answers": {
"broadcastStarted": "🚀 Difusión iniciada.",
@@ -2682,6 +2703,36 @@
"chooseClient": "Elige un Cliente para Inbound {{ .Inbound }}",
"chooseInbound": "Elige un Inbound",
"qrCodeForClient": "Código QR para el cliente {{ .Email }}:"
},
"screens": {
"panel": "Panel",
"main": "Main",
"status": "Server",
"inbounds": "Inbounds",
"clients": "Clients",
"client": "Client",
"links": "Links",
"qr": "QR code",
"wizard": "New client",
"reports": "Report",
"backup": "Backup",
"onlines": "Online",
"deplete": "Running out",
"commands": "Commands",
"broadcast": "Broadcast"
},
"inline": {
"noneTitle": "Nothing found",
"noneDesc": "Use @BotFather /setinline to enable the list.",
"offTitle": "⚠️ Inline mode is disabled for this bot",
"offBody": "Lists are browsed in inline mode. Enable it in @BotFather: /setinline, then tap Check again.",
"recheck": "Check again",
"expired": "caducado",
"fromFirstUse": "desde el primer uso"
},
"noValue": "—",
"notices": {
"repeat": "\r\n\r\n🔁 repeated {{ .Count }}× · last {{ .Time }}"
}
},
"discord": {