package teamspeak import ( "context" "fmt" "time" "github.com/go-telegram/bot" ts3 "github.com/jkoenig134/go-ts3" "github.com/rs/zerolog" "trle5.xyz/trbot/utils" "trle5.xyz/trbot/utils/flaterr" ) // Connect 检查配置并尝试连接到 TeamSpeak 服务器 func (sc *ServerConfig) Connect(ctx context.Context) error { logger := zerolog.Ctx(ctx). With(). Str("pluginName", "teamspeak3"). Str(utils.GetCurrentFuncName()). Int64("GroupID", sc.GroupID). Logger() var err error sc.rw.Lock() defer sc.rw.Unlock() // 如果服务器地址为空不允许重新启动 if sc.URL == "" || sc.API == "" { logger.Error(). Str("path", tsConfigPath). Msg("No URL or API in config") return fmt.Errorf("no URL API in config") } logger.Info().Msg("Starting client...") sc.c = ts3.NewClient(ts3.NewConfig(sc.URL, sc.API)) info, err := sc.c.Whoami() if err != nil { logger.Error(). Err(err). Str("path", tsConfigPath). Msg("Failed to connect to server") return fmt.Errorf("failed to connnect to server: %w", err) } botNickName = info.ClientUniqueIdentifier logger.Info().Msg("Checking Group ID...") // 检查要设定通知的群组 ID 是否存在 if sc.GroupID == 0 { logger.Error(). Str("path", tsConfigPath). Msg("No GroupID in config") return fmt.Errorf("no GroupID in config") } logger.Info().Msg("Testing connection...") // 显示服务端版本测试一下连接 v, err := sc.c.Version() if err != nil { logger.Error(). Err(err). Str("path", tsConfigPath). Msg("Failed to get server version") return fmt.Errorf("failed to get server version: %w", err) } logger.Info(). Str("version", v.Version). Str("platform", v.Platform). Str("build", v.Build). Msg("TeamSpeak server connected") if !sc.s.IsCheckClientTaskScheduled { err = sc.ScheduleCheckClientTask(ctx) if err != nil { logger.Error(). Err(err). Msg("Failed to schedule check client task") return fmt.Errorf("failed to schedule check client task: %w", err) } } if !sc.s.IsDeleteMessageTaskScheduled { err = sc.ScheduleDeleteMessageTask(ctx) if err != nil { logger.Error(). Err(err). Msg("Failed to schedule delete message task") return fmt.Errorf("failed to schedule delete message task: %w", err) } } return nil } // RetryLoop 将循环尝试连接到服务器,直到成功为止 func (sc *ServerConfig) RetryLoop(ctx context.Context) { logger := zerolog.Ctx(ctx). With(). Str("pluginName", "teamspeak3"). Str(utils.GetCurrentFuncName()). Logger() sc.s.IsInRetryLoop = true defer func() { sc.s.IsInRetryLoop = false logger.Info().Msg("reconnect loop exited") }() sc.s.RetryCount = 0 retryTicker := time.NewTicker(time.Second * 5) defer retryTicker.Stop() for { select { case <-ctx.Done(): return case <-sc.s.ResetTicker: sc.ResumeCheckClientTask(ctx) logger.Info().Msg("reconnect by command success") return case <-retryTicker.C: logger.Info(). Int("retryCount", sc.s.RetryCount). Msg("try reconnect...") // 实际上并不生效... timeoutCtx, cancel := context.WithTimeout(ctx, time.Second * 30) defer cancel() err := sc.Connect(timeoutCtx) if err != nil { // 出现错误时,先降低 ticker 速度,然后尝试重新初始化 // 无法成功则等待下一个周期继续尝试 if sc.s.RetryCount < 15 { sc.s.RetryCount++ retryTicker.Reset(time.Duration(sc.s.RetryCount * 20) * time.Second) } logger.Warn(). Int("retryCount", sc.s.RetryCount). Time("nextRetry", time.Now().Add(time.Duration(sc.s.RetryCount * 20) * time.Second)). Msg("reconnect failed") } else { // 重新初始化成功则恢复查询任务 sc.ResumeCheckClientTask(ctx) logger.Info().Msg("reconnect success") botMessage, err := botInstance.SendMessage(ctx, &bot.SendMessageParams{ ChatID: sc.GroupID, Text: "已成功与服务器重新建立连接", }) if err != nil { logger.Error(). Err(err). Int64("chatID", sc.GroupID). Str("content", "success reconnect to server notice"). Msg(flaterr.SendMessage.Str()) } else { sc.s.OldMessageID = append(tsConfig.s.OldMessageID, OldMessageID{ Date: int(time.Now().Unix()), ID: botMessage.ID, }, OldMessageID{ Date: int(time.Now().Unix()), ID: sc.s.RetryMsgID, }, ) } return } } } }