optimize check client logic show network message at pinned message determine the user by database id track user's username use strings.Builder to combine messages
195 lines
5.0 KiB
Go
195 lines
5.0 KiB
Go
package teamspeak
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/go-telegram/bot"
|
|
"github.com/go-telegram/bot/models"
|
|
"github.com/rs/zerolog"
|
|
"trle5.xyz/go-ts3"
|
|
"trle5.xyz/trbot/utils"
|
|
"trle5.xyz/trbot/utils/flaterr"
|
|
)
|
|
|
|
// Connect 检查配置并尝试连接到 TeamSpeak 服务器
|
|
func (sc *ServerConfig) Connect(ctx context.Context) error {
|
|
// 仅在能立即锁定时继续操作,否则直接退出
|
|
if sc.rw.TryLock() {
|
|
defer sc.rw.Unlock()
|
|
} else {
|
|
return fmt.Errorf("another goroutine is connecting")
|
|
}
|
|
|
|
logger := zerolog.Ctx(ctx).
|
|
With().
|
|
Str("pluginName", "teamspeak3").
|
|
Str(utils.GetCurrentFuncName()).
|
|
Int64("GroupID", sc.GroupID).
|
|
Logger()
|
|
|
|
// 如果服务器地址为空不允许重新启动
|
|
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")
|
|
}
|
|
|
|
sc.c = ts3.NewClient(ts3.Config{
|
|
BaseURL: sc.URL,
|
|
APIKey: sc.API,
|
|
Timeout: time.Second * 10,
|
|
})
|
|
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
|
|
|
|
// 检查要设定通知的群组 ID 是否存在
|
|
if sc.GroupID == 0 {
|
|
logger.Error().
|
|
Str("path", tsConfigPath).
|
|
Msg("No GroupID in config")
|
|
return fmt.Errorf("no GroupID in config")
|
|
}
|
|
|
|
// 显示服务端版本测试一下连接
|
|
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()
|
|
|
|
updatePinnedMsg := func() {
|
|
sc.s.CheckCount = 60 // 设定检查次数让置顶消息在下次检查时更新
|
|
_, err := botInstance.EditMessageText(ctx, &bot.EditMessageTextParams{
|
|
ChatID: sc.GroupID,
|
|
MessageID: sc.PinnedMessageID,
|
|
Text: fmt.Sprintf("%s | 已恢复与服务器的连接", time.Now().Format("15:04")),
|
|
ParseMode: models.ParseModeHTML,
|
|
})
|
|
if err != nil {
|
|
logger.Error().
|
|
Err(err).
|
|
Int64("chatID", sc.GroupID).
|
|
Str("content", "teamspeak pinned message server reconnected").
|
|
Msg(flaterr.EditMessageText.Str())
|
|
}
|
|
}
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-sc.s.ResetTicker:
|
|
sc.ResumeCheckClientTask(ctx)
|
|
logger.Info().Msg("reconnect by command success")
|
|
if sc.SendMessageMode {
|
|
// 这里不发消息,因为靠用户命令重新连接不需要再发一条消息
|
|
sc.s.OldMessageID = append(sc.s.OldMessageID, []OldMessageID{
|
|
{ Date: int(time.Now().Unix()), ID: sc.s.RetryMsgID },
|
|
}...)
|
|
}
|
|
if sc.PinMessageMode && sc.PinnedMessageID != 0 {
|
|
updatePinnedMsg()
|
|
}
|
|
return
|
|
case <-retryTicker.C:
|
|
err := sc.Connect(ctx)
|
|
if err != nil {
|
|
sc.s.RetryCount++
|
|
if sc.s.RetryCount < 16 {
|
|
// 最大间隔 15*20 = 300 秒,即 5 分钟
|
|
retryTicker.Reset(time.Duration(sc.s.RetryCount * 20) * time.Second)
|
|
}
|
|
|
|
logger.Warn().Int("retryCount", sc.s.RetryCount).Msg("reconnect failed")
|
|
} else {
|
|
// 重新初始化成功则恢复查询任务
|
|
sc.ResumeCheckClientTask(ctx)
|
|
logger.Info().Int("retryCount", sc.s.RetryCount).Msg("reconnect success")
|
|
if sc.SendMessageMode {
|
|
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(sc.s.OldMessageID, []OldMessageID{
|
|
{ Date: int(time.Now().Unix()), ID: botMessage.ID },
|
|
{ Date: int(time.Now().Unix()), ID: sc.s.RetryMsgID },
|
|
}...)
|
|
}
|
|
}
|
|
if sc.PinMessageMode && sc.PinnedMessageID != 0 {
|
|
updatePinnedMsg()
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|