From e0bcabf29bb6aa178d278fb8e8b5a66855e6b5cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E6=98=8E?= <83812544+Ed1s0nZ@users.noreply.github.com> Date: Sun, 19 Apr 2026 01:21:03 +0800 Subject: [PATCH] Delete robot directory --- robot/conn.go | 6 --- robot/ding.go | 137 -------------------------------------------------- robot/lark.go | 111 ---------------------------------------- 3 files changed, 254 deletions(-) delete mode 100644 robot/conn.go delete mode 100644 robot/ding.go delete mode 100644 robot/lark.go diff --git a/robot/conn.go b/robot/conn.go deleted file mode 100644 index d57e361d..00000000 --- a/robot/conn.go +++ /dev/null @@ -1,6 +0,0 @@ -package robot - -// MessageHandler 供飞书/钉钉长连接调用的消息处理接口(由 handler.RobotHandler 实现) -type MessageHandler interface { - HandleMessage(platform, userID, text string) string -} diff --git a/robot/ding.go b/robot/ding.go deleted file mode 100644 index eefebf66..00000000 --- a/robot/ding.go +++ /dev/null @@ -1,137 +0,0 @@ -package robot - -import ( - "bytes" - "context" - "encoding/json" - "net/http" - "strings" - "time" - - "cyberstrike-ai/internal/config" - - "github.com/open-dingtalk/dingtalk-stream-sdk-go/chatbot" - "github.com/open-dingtalk/dingtalk-stream-sdk-go/client" - dingutils "github.com/open-dingtalk/dingtalk-stream-sdk-go/utils" - "go.uber.org/zap" -) - -const ( - dingReconnectInitial = 5 * time.Second // 首次重连间隔 - dingReconnectMax = 60 * time.Second // 最大重连间隔 -) - -// StartDing 启动钉钉 Stream 长连接(无需公网),收到消息后调用 handler 并通过 SessionWebhook 回复。 -// 断线(如笔记本睡眠、网络中断)后会自动重连;ctx 被取消时退出,便于配置变更时重启。 -func StartDing(ctx context.Context, cfg config.RobotDingtalkConfig, h MessageHandler, logger *zap.Logger) { - if !cfg.Enabled || cfg.ClientID == "" || cfg.ClientSecret == "" { - return - } - go runDingLoop(ctx, cfg, h, logger) -} - -// runDingLoop 循环维持钉钉长连接:断开且 ctx 未取消时按退避间隔重连。 -func runDingLoop(ctx context.Context, cfg config.RobotDingtalkConfig, h MessageHandler, logger *zap.Logger) { - backoff := dingReconnectInitial - for { - streamClient := client.NewStreamClient( - client.WithAppCredential(client.NewAppCredentialConfig(cfg.ClientID, cfg.ClientSecret)), - client.WithSubscription(dingutils.SubscriptionTypeKCallback, "/v1.0/im/bot/messages/get", - chatbot.NewDefaultChatBotFrameHandler(func(ctx context.Context, msg *chatbot.BotCallbackDataModel) ([]byte, error) { - go handleDingMessage(ctx, msg, h, logger) - return nil, nil - }).OnEventReceived), - ) - logger.Info("钉钉 Stream 正在连接…", zap.String("client_id", cfg.ClientID)) - err := streamClient.Start(ctx) - if ctx.Err() != nil { - logger.Info("钉钉 Stream 已按配置重启关闭") - return - } - if err != nil { - logger.Warn("钉钉 Stream 长连接断开(如睡眠/断网),将自动重连", zap.Error(err), zap.Duration("retry_after", backoff)) - } - select { - case <-ctx.Done(): - return - case <-time.After(backoff): - // 下次重连间隔递增,上限 60 秒,避免频繁重试 - if backoff < dingReconnectMax { - backoff *= 2 - if backoff > dingReconnectMax { - backoff = dingReconnectMax - } - } - } - } -} - -func handleDingMessage(ctx context.Context, msg *chatbot.BotCallbackDataModel, h MessageHandler, logger *zap.Logger) { - if msg == nil || msg.SessionWebhook == "" { - return - } - content := "" - if msg.Text.Content != "" { - content = strings.TrimSpace(msg.Text.Content) - } - if content == "" && msg.Msgtype == "richText" { - if cMap, ok := msg.Content.(map[string]interface{}); ok { - if rich, ok := cMap["richText"].([]interface{}); ok { - for _, c := range rich { - if m, ok := c.(map[string]interface{}); ok { - if txt, ok := m["text"].(string); ok { - content = strings.TrimSpace(txt) - break - } - } - } - } - } - } - if content == "" { - logger.Debug("钉钉消息内容为空,已忽略", zap.String("msgtype", msg.Msgtype)) - return - } - logger.Info("钉钉收到消息", zap.String("sender", msg.SenderId), zap.String("content", content)) - userID := msg.SenderId - if userID == "" { - userID = msg.ConversationId - } - reply := h.HandleMessage("dingtalk", userID, content) - // 使用 markdown 类型以便正确展示标题、列表、代码块等格式 - title := reply - if idx := strings.IndexAny(reply, "\n"); idx > 0 { - title = strings.TrimSpace(reply[:idx]) - } - if len(title) > 50 { - title = title[:50] + "…" - } - if title == "" { - title = "回复" - } - body := map[string]interface{}{ - "msgtype": "markdown", - "markdown": map[string]string{ - "title": title, - "text": reply, - }, - } - bodyBytes, _ := json.Marshal(body) - req, err := http.NewRequestWithContext(ctx, http.MethodPost, msg.SessionWebhook, bytes.NewReader(bodyBytes)) - if err != nil { - logger.Warn("钉钉构造回复请求失败", zap.Error(err)) - return - } - req.Header.Set("Content-Type", "application/json") - resp, err := http.DefaultClient.Do(req) - if err != nil { - logger.Warn("钉钉回复请求失败", zap.Error(err)) - return - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - logger.Warn("钉钉回复非 200", zap.Int("status", resp.StatusCode)) - return - } - logger.Debug("钉钉回复成功", zap.String("content_preview", reply)) -} diff --git a/robot/lark.go b/robot/lark.go deleted file mode 100644 index 9e70af0a..00000000 --- a/robot/lark.go +++ /dev/null @@ -1,111 +0,0 @@ -package robot - -import ( - "context" - "encoding/json" - "strings" - "time" - - "cyberstrike-ai/internal/config" - - lark "github.com/larksuite/oapi-sdk-go/v3" - larkcore "github.com/larksuite/oapi-sdk-go/v3/core" - "github.com/larksuite/oapi-sdk-go/v3/event/dispatcher" - larkim "github.com/larksuite/oapi-sdk-go/v3/service/im/v1" - larkws "github.com/larksuite/oapi-sdk-go/v3/ws" - "go.uber.org/zap" -) - -const ( - larkReconnectInitial = 5 * time.Second // 首次重连间隔 - larkReconnectMax = 60 * time.Second // 最大重连间隔 -) - -type larkTextContent struct { - Text string `json:"text"` -} - -// StartLark 启动飞书长连接(无需公网),收到消息后调用 handler 并回复。 -// 断线(如笔记本睡眠、网络中断)后会自动重连;ctx 被取消时退出,便于配置变更时重启。 -func StartLark(ctx context.Context, cfg config.RobotLarkConfig, h MessageHandler, logger *zap.Logger) { - if !cfg.Enabled || cfg.AppID == "" || cfg.AppSecret == "" { - return - } - go runLarkLoop(ctx, cfg, h, logger) -} - -// runLarkLoop 循环维持飞书长连接:断开且 ctx 未取消时按退避间隔重连。 -func runLarkLoop(ctx context.Context, cfg config.RobotLarkConfig, h MessageHandler, logger *zap.Logger) { - backoff := larkReconnectInitial - for { - larkClient := lark.NewClient(cfg.AppID, cfg.AppSecret) - eventHandler := dispatcher.NewEventDispatcher("", "").OnP2MessageReceiveV1(func(ctx context.Context, event *larkim.P2MessageReceiveV1) error { - go handleLarkMessage(ctx, event, h, larkClient, logger) - return nil - }) - wsClient := larkws.NewClient(cfg.AppID, cfg.AppSecret, - larkws.WithEventHandler(eventHandler), - larkws.WithLogLevel(larkcore.LogLevelInfo), - ) - logger.Info("飞书长连接正在连接…", zap.String("app_id", cfg.AppID)) - err := wsClient.Start(ctx) - if ctx.Err() != nil { - logger.Info("飞书长连接已按配置重启关闭") - return - } - if err != nil { - logger.Warn("飞书长连接断开(如睡眠/断网),将自动重连", zap.Error(err), zap.Duration("retry_after", backoff)) - } - select { - case <-ctx.Done(): - return - case <-time.After(backoff): - if backoff < larkReconnectMax { - backoff *= 2 - if backoff > larkReconnectMax { - backoff = larkReconnectMax - } - } - } - } -} - -func handleLarkMessage(ctx context.Context, event *larkim.P2MessageReceiveV1, h MessageHandler, client *lark.Client, logger *zap.Logger) { - if event == nil || event.Event == nil || event.Event.Message == nil || event.Event.Sender == nil || event.Event.Sender.SenderId == nil { - return - } - msg := event.Event.Message - msgType := larkcore.StringValue(msg.MessageType) - if msgType != larkim.MsgTypeText { - logger.Debug("飞书暂仅处理文本消息", zap.String("msg_type", msgType)) - return - } - var textBody larkTextContent - if err := json.Unmarshal([]byte(larkcore.StringValue(msg.Content)), &textBody); err != nil { - logger.Warn("飞书消息 Content 解析失败", zap.Error(err)) - return - } - text := strings.TrimSpace(textBody.Text) - if text == "" { - return - } - userID := "" - if event.Event.Sender.SenderId.UserId != nil { - userID = *event.Event.Sender.SenderId.UserId - } - messageID := larkcore.StringValue(msg.MessageId) - reply := h.HandleMessage("lark", userID, text) - contentBytes, _ := json.Marshal(larkTextContent{Text: reply}) - _, err := client.Im.Message.Reply(ctx, larkim.NewReplyMessageReqBuilder(). - MessageId(messageID). - Body(larkim.NewReplyMessageReqBodyBuilder(). - MsgType(larkim.MsgTypeText). - Content(string(contentBytes)). - Build()). - Build()) - if err != nil { - logger.Warn("飞书回复失败", zap.String("message_id", messageID), zap.Error(err)) - return - } - logger.Debug("飞书已回复", zap.String("message_id", messageID)) -}