Ver código fonte

fix(tgbot): reload bot on settings save so a new token takes effect without a panel restart

The Telegram bot was only started at panel boot, so saving a token or toggling tgBotEnable persisted to the DB but never reached the running bot until a full restart, making it look like the token did not save (issue #5539). The settings/update controller now reconciles the bot the same way panelOutbound reconciles Xray: when tgBotEnable, the token, chat ID, or API server change, it stops/(re)starts the bot and updates the event-bus subscription.
MHSanaei 5 horas atrás
pai
commit
93ff60e568
2 arquivos alterados com 41 adições e 0 exclusões
  1. 19 0
      internal/web/controller/setting.go
  2. 22 0
      internal/web/web.go

+ 19 - 0
internal/web/controller/setting.go

@@ -88,6 +88,10 @@ func (a *SettingController) updateSetting(c *gin.Context) {
 	}
 	oldTwoFactor, twoFactorErr := a.settingService.GetTwoFactorEnable()
 	oldPanelOutbound, _ := a.settingService.GetPanelOutbound()
+	oldTgEnable, _ := a.settingService.GetTgbotEnabled()
+	oldTgToken, _ := a.settingService.GetTgBotToken()
+	oldTgChatId, _ := a.settingService.GetTgBotChatId()
+	oldTgAPIServer, _ := a.settingService.GetTgBotAPIServer()
 	err := a.settingService.UpdateAllSetting(allSetting)
 	if err == nil && twoFactorErr == nil && !oldTwoFactor && allSetting.TwoFactorEnable {
 		if bumpErr := a.userService.BumpLoginEpoch(); bumpErr != nil {
@@ -102,6 +106,16 @@ func (a *SettingController) updateSetting(c *gin.Context) {
 			logger.Warning("apply panel outbound change failed:", applyErr)
 		}
 	}
+	// UpdateAllSetting already restored a redacted-blank token, so allSetting.TgBotToken is the effective value to compare.
+	if err == nil && reloadTgbotFunc != nil {
+		tgChanged := oldTgEnable != allSetting.TgBotEnable ||
+			(allSetting.TgBotEnable && (oldTgToken != allSetting.TgBotToken ||
+				oldTgChatId != allSetting.TgBotChatId ||
+				oldTgAPIServer != allSetting.TgBotAPIServer))
+		if tgChanged {
+			reloadTgbotFunc()
+		}
+	}
 	jsonMsg(c, I18nWeb(c, "pages.settings.toasts.modifySettings"), err)
 }
 
@@ -252,6 +266,11 @@ var testTgFunc func() error
 // SetTestTgFunc registers the function used to test Telegram sending.
 func SetTestTgFunc(fn func() error) { testTgFunc = fn }
 
+// reloadTgbotFunc is wired from the web layer; importing tgbot here would be a circular dependency.
+var reloadTgbotFunc func()
+
+func SetReloadTgbotFunc(fn func()) { reloadTgbotFunc = fn }
+
 // emailService is set from web layer.
 var emailService *email.EmailService
 

+ 22 - 0
internal/web/web.go

@@ -619,6 +619,28 @@ func (s *Server) start(restartXray bool, startTgBot bool) (err error) {
 		return nil
 	})
 
+	controller.SetReloadTgbotFunc(func() {
+		enabled, err := s.settingService.GetTgbotEnabled()
+		if err != nil || !enabled {
+			if s.tgbotService.IsRunning() {
+				s.tgbotService.Stop()
+			}
+			if s.bus != nil {
+				s.bus.Unsubscribe("tg-notifier")
+			}
+			return
+		}
+		// Start() stops any previous receiver first, so it is safe whether or not the bot is already running.
+		tgBot := s.tgbotService.NewTgbot()
+		if startErr := tgBot.Start(i18nFS); startErr != nil {
+			logger.Warning("reload Telegram bot failed:", startErr)
+			return
+		}
+		if s.bus != nil {
+			s.bus.Subscribe("tg-notifier", s.tgbotService.HandleEvent)
+		}
+	})
+
 	s.startTask(restartXray)
 
 	if startTgBot {