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
+37
View File
@@ -80,6 +80,7 @@ func (a *SettingController) initRouter(g *gin.RouterGroup) {
g.POST("/apiTokens/setEnabled/:id", a.setApiTokenEnabled)
g.POST("/testSmtp", a.testSmtp)
g.POST("/testTgBot", a.testTgBot)
g.POST("/tgBotCapabilities", a.tgBotCapabilities)
g.POST("/testDiscord", a.testDiscord)
}
@@ -348,6 +349,42 @@ func (a *SettingController) testTgBot(c *gin.Context) {
jsonMsg(c, I18nWeb(c, "pages.settings.tgBotNotRunning"), errors.New("bot not started"))
}
// tgBotCapabilities reports what the bot can do. Inline mode is a BotFather
// switch the API can read but never set, so the panel warns when lists can't open.
func (a *SettingController) tgBotCapabilities(c *gin.Context) {
enabled, err := a.settingService.GetTgbotEnabled()
if err != nil || !enabled {
jsonMsg(c, I18nWeb(c, "pages.settings.tgBotNotEnabled"), errors.New("telegram bot disabled"))
return
}
if tgCapsFunc == nil {
jsonMsg(c, I18nWeb(c, "pages.settings.tgBotNotRunning"), errors.New("bot not started"))
return
}
caps, err := tgCapsFunc()
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.settings.tgTestFailed")+": "+err.Error(), err)
return
}
jsonObj(c, caps, nil)
}
// TgBotCapabilities is the browser-safe shape of the bot's own capabilities.
type TgBotCapabilities struct {
InlineEnabled bool `json:"inlineEnabled" example:"true"`
// GroupPrivacy off means the bot reads every group message; when it is on,
// a bare /start in a group is invisible to the bot.
GroupPrivacy bool `json:"groupPrivacy" example:"false"`
Username string `json:"username" example:"xui_bot"`
Running bool `json:"running" example:"true"`
}
// tgCapsFunc is wired from the web layer; importing tgbot here is circular.
var tgCapsFunc func() (TgBotCapabilities, error)
// SetTgBotCapsFunc registers the capability probe.
func SetTgBotCapsFunc(fn func() (TgBotCapabilities, error)) { tgCapsFunc = fn }
// testTgFunc is set from web layer to test Telegram sending without circular imports.
var testTgFunc func() error