mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-07-07 21:17:58 +02:00
122 lines
3.2 KiB
Go
122 lines
3.2 KiB
Go
package robot
|
||
|
||
import (
|
||
"context"
|
||
"strings"
|
||
|
||
"cyberstrike-ai/internal/config"
|
||
|
||
"github.com/bwmarrin/discordgo"
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
const (
|
||
discordPlatform = "discord"
|
||
discordMaxMessageRunes = 2000
|
||
)
|
||
|
||
// StartDiscord 启动 Discord Gateway(WebSocket,无需公网回调)。
|
||
func StartDiscord(ctx context.Context, robotsCfg config.RobotsConfig, h MessageHandler, logger *zap.Logger) {
|
||
cfg := robotsCfg.Discord
|
||
if !cfg.Enabled || strings.TrimSpace(cfg.BotToken) == "" {
|
||
return
|
||
}
|
||
go runDiscordLoop(ctx, cfg, h, logger)
|
||
}
|
||
|
||
func runDiscordLoop(ctx context.Context, cfg config.RobotDiscordConfig, h MessageHandler, logger *zap.Logger) {
|
||
backoff := reconnectInitial
|
||
for {
|
||
err := runDiscordSession(ctx, cfg, h, logger)
|
||
if ctx.Err() != nil {
|
||
logger.Info("Discord Gateway 已按配置关闭")
|
||
return
|
||
}
|
||
if err != nil {
|
||
logger.Warn("Discord Gateway 异常,将自动重连", zap.Error(err), zap.Duration("retry_after", backoff))
|
||
}
|
||
if !waitReconnect(ctx, &backoff) {
|
||
return
|
||
}
|
||
}
|
||
}
|
||
|
||
func runDiscordSession(ctx context.Context, cfg config.RobotDiscordConfig, h MessageHandler, logger *zap.Logger) error {
|
||
token := strings.TrimSpace(cfg.BotToken)
|
||
if !strings.HasPrefix(token, "Bot ") {
|
||
token = "Bot " + token
|
||
}
|
||
session, err := discordgo.New(token)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
session.Identify.Intents = discordgo.IntentsGuildMessages |
|
||
discordgo.IntentsDirectMessages |
|
||
discordgo.IntentMessageContent
|
||
|
||
session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||
if m == nil || m.Author == nil || m.Author.Bot {
|
||
return
|
||
}
|
||
text := strings.TrimSpace(m.Content)
|
||
if text == "" {
|
||
return
|
||
}
|
||
if m.GuildID != "" {
|
||
if !cfg.AllowGuildMessages {
|
||
return
|
||
}
|
||
if s.State.User == nil || !discordMentionsBot(m, s.State.User.ID) {
|
||
return
|
||
}
|
||
}
|
||
userID := discordSessionKey(m.GuildID, m.Author.ID)
|
||
logger.Info("Discord 收到消息", zap.String("from", userID), zap.String("content", text))
|
||
reply := h.HandleMessage(discordPlatform, userID, text)
|
||
discordPostReply(s, m.ChannelID, reply, logger)
|
||
})
|
||
|
||
if err := session.Open(); err != nil {
|
||
return err
|
||
}
|
||
logger.Info("Discord Gateway 已连接,等待收消息")
|
||
defer session.Close()
|
||
|
||
<-ctx.Done()
|
||
return ctx.Err()
|
||
}
|
||
|
||
func discordMentionsBot(m *discordgo.MessageCreate, botUserID string) bool {
|
||
if m == nil || botUserID == "" {
|
||
return false
|
||
}
|
||
for _, mention := range m.Mentions {
|
||
if mention != nil && mention.ID == botUserID {
|
||
return true
|
||
}
|
||
}
|
||
return strings.Contains(m.Content, "<@"+botUserID+">") || strings.Contains(m.Content, "<@!"+botUserID+">")
|
||
}
|
||
|
||
func discordSessionKey(guildID, userID string) string {
|
||
guildID = strings.TrimSpace(guildID)
|
||
userID = strings.TrimSpace(userID)
|
||
if guildID == "" {
|
||
return "u:" + userID
|
||
}
|
||
return "g:" + guildID + "|u:" + userID
|
||
}
|
||
|
||
func discordPostReply(s *discordgo.Session, channelID, reply string, logger *zap.Logger) {
|
||
reply = trimReply(reply)
|
||
if reply == "" {
|
||
return
|
||
}
|
||
for _, chunk := range splitTextChunks(reply, discordMaxMessageRunes) {
|
||
if _, err := s.ChannelMessageSend(channelID, chunk); err != nil {
|
||
logger.Warn("Discord 发送回复失败", zap.String("channel", channelID), zap.Error(err))
|
||
return
|
||
}
|
||
}
|
||
}
|