diff --git a/cmd/picoclaw/internal/gateway/helpers.go b/cmd/picoclaw/internal/gateway/helpers.go index 98262d5ae..a73ad5e4b 100644 --- a/cmd/picoclaw/internal/gateway/helpers.go +++ b/cmd/picoclaw/internal/gateway/helpers.go @@ -15,9 +15,17 @@ import ( "github.com/sipeed/picoclaw/pkg/agent" "github.com/sipeed/picoclaw/pkg/bus" "github.com/sipeed/picoclaw/pkg/channels" + _ "github.com/sipeed/picoclaw/pkg/channels/dingtalk" dch "github.com/sipeed/picoclaw/pkg/channels/discord" + _ "github.com/sipeed/picoclaw/pkg/channels/feishu" + _ "github.com/sipeed/picoclaw/pkg/channels/line" + _ "github.com/sipeed/picoclaw/pkg/channels/maixcam" + _ "github.com/sipeed/picoclaw/pkg/channels/onebot" + _ "github.com/sipeed/picoclaw/pkg/channels/qq" slackch "github.com/sipeed/picoclaw/pkg/channels/slack" - tgram "github.com/sipeed/picoclaw/pkg/channels/telegram" + tgramch "github.com/sipeed/picoclaw/pkg/channels/telegram" + _ "github.com/sipeed/picoclaw/pkg/channels/wecom" + _ "github.com/sipeed/picoclaw/pkg/channels/whatsapp" "github.com/sipeed/picoclaw/pkg/config" "github.com/sipeed/picoclaw/pkg/cron" "github.com/sipeed/picoclaw/pkg/devices" @@ -28,16 +36,6 @@ import ( "github.com/sipeed/picoclaw/pkg/state" "github.com/sipeed/picoclaw/pkg/tools" "github.com/sipeed/picoclaw/pkg/voice" - - // Channel factory registrations (blank imports trigger init()) - _ "github.com/sipeed/picoclaw/pkg/channels/dingtalk" - _ "github.com/sipeed/picoclaw/pkg/channels/feishu" - _ "github.com/sipeed/picoclaw/pkg/channels/line" - _ "github.com/sipeed/picoclaw/pkg/channels/maixcam" - _ "github.com/sipeed/picoclaw/pkg/channels/onebot" - _ "github.com/sipeed/picoclaw/pkg/channels/qq" - _ "github.com/sipeed/picoclaw/pkg/channels/wecom" - _ "github.com/sipeed/picoclaw/pkg/channels/whatsapp" ) func gatewayCmd(debug bool) error { @@ -143,7 +141,7 @@ func gatewayCmd(debug bool) error { if transcriber != nil { if telegramChannel, ok := channelManager.GetChannel("telegram"); ok { - if tc, ok := telegramChannel.(*tgram.TelegramChannel); ok { + if tc, ok := telegramChannel.(*tgramch.TelegramChannel); ok { tc.SetTranscriber(transcriber) logger.InfoC("voice", "Groq transcription attached to Telegram channel") } diff --git a/pkg/channels/dingtalk/dingtalk.go b/pkg/channels/dingtalk/dingtalk.go index 0edb0023c..afc0de47f 100644 --- a/pkg/channels/dingtalk/dingtalk.go +++ b/pkg/channels/dingtalk/dingtalk.go @@ -10,6 +10,7 @@ import ( "github.com/open-dingtalk/dingtalk-stream-sdk-go/chatbot" "github.com/open-dingtalk/dingtalk-stream-sdk-go/client" + "github.com/sipeed/picoclaw/pkg/bus" "github.com/sipeed/picoclaw/pkg/channels" "github.com/sipeed/picoclaw/pkg/config" @@ -109,7 +110,7 @@ func (c *DingTalkChannel) Send(ctx context.Context, msg bus.OutboundMessage) err return fmt.Errorf("invalid session_webhook type for chat %s", msg.ChatID) } - logger.DebugCF("dingtalk", "Sending message", map[string]interface{}{ + logger.DebugCF("dingtalk", "Sending message", map[string]any{ "chat_id": msg.ChatID, "preview": utils.Truncate(msg.Content, 100), }) @@ -121,12 +122,15 @@ func (c *DingTalkChannel) Send(ctx context.Context, msg bus.OutboundMessage) err // onChatBotMessageReceived implements the IChatBotMessageHandler function signature // This is called by the Stream SDK when a new message arrives // IChatBotMessageHandler is: func(c context.Context, data *chatbot.BotCallbackDataModel) ([]byte, error) -func (c *DingTalkChannel) onChatBotMessageReceived(ctx context.Context, data *chatbot.BotCallbackDataModel) ([]byte, error) { +func (c *DingTalkChannel) onChatBotMessageReceived( + ctx context.Context, + data *chatbot.BotCallbackDataModel, +) ([]byte, error) { // Extract message content from Text field content := data.Text.Content if content == "" { // Try to extract from Content interface{} if Text is empty - if contentMap, ok := data.Content.(map[string]interface{}); ok { + if contentMap, ok := data.Content.(map[string]any); ok { if textContent, ok := contentMap["content"].(string); ok { content = textContent } @@ -164,7 +168,7 @@ func (c *DingTalkChannel) onChatBotMessageReceived(ctx context.Context, data *ch metadata["peer_id"] = data.ConversationId } - logger.DebugCF("dingtalk", "Received message", map[string]interface{}{ + logger.DebugCF("dingtalk", "Received message", map[string]any{ "sender_nick": senderNick, "sender_id": senderID, "preview": utils.Truncate(content, 50), @@ -193,7 +197,6 @@ func (c *DingTalkChannel) SendDirectReply(ctx context.Context, sessionWebhook, c titleBytes, contentBytes, ) - if err != nil { return fmt.Errorf("failed to send reply: %w", err) } diff --git a/pkg/channels/discord/discord.go b/pkg/channels/discord/discord.go index 6c4efd87c..b83ac28fd 100644 --- a/pkg/channels/discord/discord.go +++ b/pkg/channels/discord/discord.go @@ -9,6 +9,7 @@ import ( "time" "github.com/bwmarrin/discordgo" + "github.com/sipeed/picoclaw/pkg/bus" "github.com/sipeed/picoclaw/pkg/channels" "github.com/sipeed/picoclaw/pkg/config" @@ -322,7 +323,7 @@ func (c *DiscordChannel) startTyping(chatID string) { go func() { if err := c.session.ChannelTyping(chatID); err != nil { - logger.DebugCF("discord", "ChannelTyping error", map[string]interface{}{"chatID": chatID, "err": err}) + logger.DebugCF("discord", "ChannelTyping error", map[string]any{"chatID": chatID, "err": err}) } ticker := time.NewTicker(8 * time.Second) defer ticker.Stop() @@ -337,7 +338,7 @@ func (c *DiscordChannel) startTyping(chatID string) { return case <-ticker.C: if err := c.session.ChannelTyping(chatID); err != nil { - logger.DebugCF("discord", "ChannelTyping error", map[string]interface{}{"chatID": chatID, "err": err}) + logger.DebugCF("discord", "ChannelTyping error", map[string]any{"chatID": chatID, "err": err}) } } } diff --git a/pkg/channels/feishu/feishu_64.go b/pkg/channels/feishu/feishu_64.go index a49ee34cb..aa4e141c4 100644 --- a/pkg/channels/feishu/feishu_64.go +++ b/pkg/channels/feishu/feishu_64.go @@ -66,7 +66,7 @@ func (c *FeishuChannel) Start(ctx context.Context) error { go func() { if err := wsClient.Start(runCtx); err != nil { - logger.ErrorCF("feishu", "Feishu websocket stopped with error", map[string]interface{}{ + logger.ErrorCF("feishu", "Feishu websocket stopped with error", map[string]any{ "error": err.Error(), }) } @@ -122,7 +122,7 @@ func (c *FeishuChannel) Send(ctx context.Context, msg bus.OutboundMessage) error return fmt.Errorf("feishu api error: code=%d msg=%s", resp.Code, resp.Msg) } - logger.DebugCF("feishu", "Feishu message sent", map[string]interface{}{ + logger.DebugCF("feishu", "Feishu message sent", map[string]any{ "chat_id": msg.ChatID, }) @@ -175,7 +175,7 @@ func (c *FeishuChannel) handleMessageReceive(_ context.Context, event *larkim.P2 metadata["peer_id"] = chatID } - logger.InfoCF("feishu", "Feishu message received", map[string]interface{}{ + logger.InfoCF("feishu", "Feishu message received", map[string]any{ "sender_id": senderID, "chat_id": chatID, "preview": utils.Truncate(content, 80), diff --git a/pkg/channels/line/line.go b/pkg/channels/line/line.go index 7df0491d9..4e1d0dfd3 100644 --- a/pkg/channels/line/line.go +++ b/pkg/channels/line/line.go @@ -76,11 +76,11 @@ func (c *LINEChannel) Start(ctx context.Context) error { // Fetch bot profile to get bot's userId for mention detection if err := c.fetchBotInfo(); err != nil { - logger.WarnCF("line", "Failed to fetch bot info (mention detection disabled)", map[string]interface{}{ + logger.WarnCF("line", "Failed to fetch bot info (mention detection disabled)", map[string]any{ "error": err.Error(), }) } else { - logger.InfoCF("line", "Bot info fetched", map[string]interface{}{ + logger.InfoCF("line", "Bot info fetched", map[string]any{ "bot_user_id": c.botUserID, "basic_id": c.botBasicID, "display_name": c.botDisplayName, @@ -101,12 +101,12 @@ func (c *LINEChannel) Start(ctx context.Context) error { } go func() { - logger.InfoCF("line", "LINE webhook server listening", map[string]interface{}{ + logger.InfoCF("line", "LINE webhook server listening", map[string]any{ "addr": addr, "path": path, }) if err := c.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { - logger.ErrorCF("line", "Webhook server error", map[string]interface{}{ + logger.ErrorCF("line", "Webhook server error", map[string]any{ "error": err.Error(), }) } @@ -163,7 +163,7 @@ func (c *LINEChannel) Stop(ctx context.Context) error { shutdownCtx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() if err := c.httpServer.Shutdown(shutdownCtx); err != nil { - logger.ErrorCF("line", "Webhook server shutdown error", map[string]interface{}{ + logger.ErrorCF("line", "Webhook server shutdown error", map[string]any{ "error": err.Error(), }) } @@ -183,7 +183,7 @@ func (c *LINEChannel) webhookHandler(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { - logger.ErrorCF("line", "Failed to read request body", map[string]interface{}{ + logger.ErrorCF("line", "Failed to read request body", map[string]any{ "error": err.Error(), }) http.Error(w, "Bad request", http.StatusBadRequest) @@ -201,7 +201,7 @@ func (c *LINEChannel) webhookHandler(w http.ResponseWriter, r *http.Request) { Events []lineEvent `json:"events"` } if err := json.Unmarshal(body, &payload); err != nil { - logger.ErrorCF("line", "Failed to parse webhook payload", map[string]interface{}{ + logger.ErrorCF("line", "Failed to parse webhook payload", map[string]any{ "error": err.Error(), }) http.Error(w, "Bad request", http.StatusBadRequest) @@ -267,7 +267,7 @@ type lineMentionee struct { func (c *LINEChannel) processEvent(event lineEvent) { if event.Type != "message" { - logger.DebugCF("line", "Ignoring non-message event", map[string]interface{}{ + logger.DebugCF("line", "Ignoring non-message event", map[string]any{ "type": event.Type, }) return @@ -279,7 +279,7 @@ func (c *LINEChannel) processEvent(event lineEvent) { var msg lineMessage if err := json.Unmarshal(event.Message, &msg); err != nil { - logger.ErrorCF("line", "Failed to parse message", map[string]interface{}{ + logger.ErrorCF("line", "Failed to parse message", map[string]any{ "error": err.Error(), }) return @@ -287,7 +287,7 @@ func (c *LINEChannel) processEvent(event lineEvent) { // In group chats, only respond when the bot is mentioned if isGroup && !c.isBotMentioned(msg) { - logger.DebugCF("line", "Ignoring group message without mention", map[string]interface{}{ + logger.DebugCF("line", "Ignoring group message without mention", map[string]any{ "chat_id": chatID, }) return @@ -313,7 +313,7 @@ func (c *LINEChannel) processEvent(event lineEvent) { defer func() { for _, file := range localFiles { if err := os.Remove(file); err != nil { - logger.DebugCF("line", "Failed to cleanup temp file", map[string]interface{}{ + logger.DebugCF("line", "Failed to cleanup temp file", map[string]any{ "file": file, "error": err.Error(), }) @@ -375,7 +375,7 @@ func (c *LINEChannel) processEvent(event lineEvent) { metadata["peer_id"] = senderID } - logger.DebugCF("line", "Received message", map[string]interface{}{ + logger.DebugCF("line", "Received message", map[string]any{ "sender_id": senderID, "chat_id": chatID, "message_type": msg.Type, @@ -506,7 +506,7 @@ func (c *LINEChannel) Send(ctx context.Context, msg bus.OutboundMessage) error { tokenEntry := entry.(replyTokenEntry) if time.Since(tokenEntry.timestamp) < lineReplyTokenMaxAge { if err := c.sendReply(ctx, tokenEntry.token, msg.Content, quoteToken); err == nil { - logger.DebugCF("line", "Message sent via Reply API", map[string]interface{}{ + logger.DebugCF("line", "Message sent via Reply API", map[string]any{ "chat_id": msg.ChatID, "quoted": quoteToken != "", }) @@ -534,7 +534,7 @@ func buildTextMessage(content, quoteToken string) map[string]string { // sendReply sends a message using the LINE Reply API. func (c *LINEChannel) sendReply(ctx context.Context, replyToken, content, quoteToken string) error { - payload := map[string]interface{}{ + payload := map[string]any{ "replyToken": replyToken, "messages": []map[string]string{buildTextMessage(content, quoteToken)}, } @@ -544,7 +544,7 @@ func (c *LINEChannel) sendReply(ctx context.Context, replyToken, content, quoteT // sendPush sends a message using the LINE Push API. func (c *LINEChannel) sendPush(ctx context.Context, to, content, quoteToken string) error { - payload := map[string]interface{}{ + payload := map[string]any{ "to": to, "messages": []map[string]string{buildTextMessage(content, quoteToken)}, } @@ -554,19 +554,19 @@ func (c *LINEChannel) sendPush(ctx context.Context, to, content, quoteToken stri // sendLoading sends a loading animation indicator to the chat. func (c *LINEChannel) sendLoading(chatID string) { - payload := map[string]interface{}{ + payload := map[string]any{ "chatId": chatID, "loadingSeconds": 60, } if err := c.callAPI(c.ctx, lineLoadingEndpoint, payload); err != nil { - logger.DebugCF("line", "Failed to send loading indicator", map[string]interface{}{ + logger.DebugCF("line", "Failed to send loading indicator", map[string]any{ "error": err.Error(), }) } } // callAPI makes an authenticated POST request to the LINE API. -func (c *LINEChannel) callAPI(ctx context.Context, endpoint string, payload interface{}) error { +func (c *LINEChannel) callAPI(ctx context.Context, endpoint string, payload any) error { body, err := json.Marshal(payload) if err != nil { return fmt.Errorf("failed to marshal payload: %w", err) diff --git a/pkg/channels/maixcam/maixcam.go b/pkg/channels/maixcam/maixcam.go index d3c6662d7..a7bff55e0 100644 --- a/pkg/channels/maixcam/maixcam.go +++ b/pkg/channels/maixcam/maixcam.go @@ -22,10 +22,10 @@ type MaixCamChannel struct { } type MaixCamMessage struct { - Type string `json:"type"` - Tips string `json:"tips"` - Timestamp float64 `json:"timestamp"` - Data map[string]interface{} `json:"data"` + Type string `json:"type"` + Tips string `json:"tips"` + Timestamp float64 `json:"timestamp"` + Data map[string]any `json:"data"` } func NewMaixCamChannel(cfg config.MaixCamConfig, bus *bus.MessageBus) (*MaixCamChannel, error) { @@ -50,7 +50,7 @@ func (c *MaixCamChannel) Start(ctx context.Context) error { c.listener = listener c.SetRunning(true) - logger.InfoCF("maixcam", "MaixCam server listening", map[string]interface{}{ + logger.InfoCF("maixcam", "MaixCam server listening", map[string]any{ "host": c.config.Host, "port": c.config.Port, }) @@ -72,14 +72,14 @@ func (c *MaixCamChannel) acceptConnections(ctx context.Context) { conn, err := c.listener.Accept() if err != nil { if c.IsRunning() { - logger.ErrorCF("maixcam", "Failed to accept connection", map[string]interface{}{ + logger.ErrorCF("maixcam", "Failed to accept connection", map[string]any{ "error": err.Error(), }) } return } - logger.InfoCF("maixcam", "New connection from MaixCam device", map[string]interface{}{ + logger.InfoCF("maixcam", "New connection from MaixCam device", map[string]any{ "remote_addr": conn.RemoteAddr().String(), }) @@ -113,7 +113,7 @@ func (c *MaixCamChannel) handleConnection(conn net.Conn, ctx context.Context) { var msg MaixCamMessage if err := decoder.Decode(&msg); err != nil { if err.Error() != "EOF" { - logger.ErrorCF("maixcam", "Failed to decode message", map[string]interface{}{ + logger.ErrorCF("maixcam", "Failed to decode message", map[string]any{ "error": err.Error(), }) } @@ -134,14 +134,14 @@ func (c *MaixCamChannel) processMessage(msg MaixCamMessage, conn net.Conn) { case "status": c.handleStatusUpdate(msg) default: - logger.WarnCF("maixcam", "Unknown message type", map[string]interface{}{ + logger.WarnCF("maixcam", "Unknown message type", map[string]any{ "type": msg.Type, }) } } func (c *MaixCamChannel) handlePersonDetection(msg MaixCamMessage) { - logger.InfoCF("maixcam", "", map[string]interface{}{ + logger.InfoCF("maixcam", "", map[string]any{ "timestamp": msg.Timestamp, "data": msg.Data, }) @@ -179,7 +179,7 @@ func (c *MaixCamChannel) handlePersonDetection(msg MaixCamMessage) { } func (c *MaixCamChannel) handleStatusUpdate(msg MaixCamMessage) { - logger.InfoCF("maixcam", "Status update from MaixCam", map[string]interface{}{ + logger.InfoCF("maixcam", "Status update from MaixCam", map[string]any{ "status": msg.Data, }) } @@ -217,7 +217,7 @@ func (c *MaixCamChannel) Send(ctx context.Context, msg bus.OutboundMessage) erro return fmt.Errorf("no connected MaixCam devices") } - response := map[string]interface{}{ + response := map[string]any{ "type": "command", "timestamp": float64(0), "message": msg.Content, @@ -232,7 +232,7 @@ func (c *MaixCamChannel) Send(ctx context.Context, msg bus.OutboundMessage) erro var sendErr error for conn := range c.clients { if _, err := conn.Write(data); err != nil { - logger.ErrorCF("maixcam", "Failed to send to client", map[string]interface{}{ + logger.ErrorCF("maixcam", "Failed to send to client", map[string]any{ "client": conn.RemoteAddr().String(), "error": err.Error(), }) diff --git a/pkg/channels/manager.go b/pkg/channels/manager.go index 091982282..7baef058c 100644 --- a/pkg/channels/manager.go +++ b/pkg/channels/manager.go @@ -47,23 +47,23 @@ func NewManager(cfg *config.Config, messageBus *bus.MessageBus) (*Manager, error func (m *Manager) initChannel(name, displayName string) { f, ok := getFactory(name) if !ok { - logger.WarnCF("channels", "Factory not registered", map[string]interface{}{ + logger.WarnCF("channels", "Factory not registered", map[string]any{ "channel": displayName, }) return } - logger.DebugCF("channels", "Attempting to initialize channel", map[string]interface{}{ + logger.DebugCF("channels", "Attempting to initialize channel", map[string]any{ "channel": displayName, }) ch, err := f(m.config, m.bus) if err != nil { - logger.ErrorCF("channels", "Failed to initialize channel", map[string]interface{}{ + logger.ErrorCF("channels", "Failed to initialize channel", map[string]any{ "channel": displayName, "error": err.Error(), }) } else { m.channels[name] = ch - logger.InfoCF("channels", "Channel enabled successfully", map[string]interface{}{ + logger.InfoCF("channels", "Channel enabled successfully", map[string]any{ "channel": displayName, }) } @@ -120,7 +120,7 @@ func (m *Manager) initChannels() error { m.initChannel("wecom_app", "WeCom App") } - logger.InfoCF("channels", "Channel initialization completed", map[string]interface{}{ + logger.InfoCF("channels", "Channel initialization completed", map[string]any{ "enabled_channels": len(m.channels), }) @@ -144,11 +144,11 @@ func (m *Manager) StartAll(ctx context.Context) error { go m.dispatchOutbound(dispatchCtx) for name, channel := range m.channels { - logger.InfoCF("channels", "Starting channel", map[string]interface{}{ + logger.InfoCF("channels", "Starting channel", map[string]any{ "channel": name, }) if err := channel.Start(ctx); err != nil { - logger.ErrorCF("channels", "Failed to start channel", map[string]interface{}{ + logger.ErrorCF("channels", "Failed to start channel", map[string]any{ "channel": name, "error": err.Error(), }) @@ -171,11 +171,11 @@ func (m *Manager) StopAll(ctx context.Context) error { } for name, channel := range m.channels { - logger.InfoCF("channels", "Stopping channel", map[string]interface{}{ + logger.InfoCF("channels", "Stopping channel", map[string]any{ "channel": name, }) if err := channel.Stop(ctx); err != nil { - logger.ErrorCF("channels", "Error stopping channel", map[string]interface{}{ + logger.ErrorCF("channels", "Error stopping channel", map[string]any{ "channel": name, "error": err.Error(), }) @@ -210,14 +210,14 @@ func (m *Manager) dispatchOutbound(ctx context.Context) { m.mu.RUnlock() if !exists { - logger.WarnCF("channels", "Unknown channel for outbound message", map[string]interface{}{ + logger.WarnCF("channels", "Unknown channel for outbound message", map[string]any{ "channel": msg.Channel, }) continue } if err := channel.Send(ctx, msg); err != nil { - logger.ErrorCF("channels", "Error sending message to channel", map[string]interface{}{ + logger.ErrorCF("channels", "Error sending message to channel", map[string]any{ "channel": msg.Channel, "error": err.Error(), }) @@ -233,13 +233,13 @@ func (m *Manager) GetChannel(name string) (Channel, bool) { return channel, ok } -func (m *Manager) GetStatus() map[string]interface{} { +func (m *Manager) GetStatus() map[string]any { m.mu.RLock() defer m.mu.RUnlock() - status := make(map[string]interface{}) + status := make(map[string]any) for name, channel := range m.channels { - status[name] = map[string]interface{}{ + status[name] = map[string]any{ "enabled": true, "running": channel.IsRunning(), } diff --git a/pkg/channels/onebot/onebot.go b/pkg/channels/onebot/onebot.go index 209f2dc00..3d2e64e2a 100644 --- a/pkg/channels/onebot/onebot.go +++ b/pkg/channels/onebot/onebot.go @@ -88,14 +88,14 @@ type oneBotSender struct { } type oneBotAPIRequest struct { - Action string `json:"action"` - Params interface{} `json:"params"` - Echo string `json:"echo,omitempty"` + Action string `json:"action"` + Params any `json:"params"` + Echo string `json:"echo,omitempty"` } type oneBotMessageSegment struct { - Type string `json:"type"` - Data map[string]interface{} `json:"data"` + Type string `json:"type"` + Data map[string]any `json:"data"` } func NewOneBotChannel(cfg config.OneBotConfig, messageBus *bus.MessageBus) (*OneBotChannel, error) { @@ -118,13 +118,13 @@ func (c *OneBotChannel) SetTranscriber(transcriber *voice.GroqTranscriber) { func (c *OneBotChannel) setMsgEmojiLike(messageID string, emojiID int, set bool) { go func() { - _, err := c.sendAPIRequest("set_msg_emoji_like", map[string]interface{}{ + _, err := c.sendAPIRequest("set_msg_emoji_like", map[string]any{ "message_id": messageID, "emoji_id": emojiID, "set": set, }, 5*time.Second) if err != nil { - logger.DebugCF("onebot", "Failed to set emoji like", map[string]interface{}{ + logger.DebugCF("onebot", "Failed to set emoji like", map[string]any{ "message_id": messageID, "error": err.Error(), }) @@ -137,14 +137,14 @@ func (c *OneBotChannel) Start(ctx context.Context) error { return fmt.Errorf("OneBot ws_url not configured") } - logger.InfoCF("onebot", "Starting OneBot channel", map[string]interface{}{ + logger.InfoCF("onebot", "Starting OneBot channel", map[string]any{ "ws_url": c.config.WSUrl, }) c.ctx, c.cancel = context.WithCancel(ctx) if err := c.connect(); err != nil { - logger.WarnCF("onebot", "Initial connection failed, will retry in background", map[string]interface{}{ + logger.WarnCF("onebot", "Initial connection failed, will retry in background", map[string]any{ "error": err.Error(), }) } else { @@ -209,7 +209,7 @@ func (c *OneBotChannel) pinger(conn *websocket.Conn) { err := conn.WriteMessage(websocket.PingMessage, nil) c.writeMu.Unlock() if err != nil { - logger.DebugCF("onebot", "Ping write failed, stopping pinger", map[string]interface{}{ + logger.DebugCF("onebot", "Ping write failed, stopping pinger", map[string]any{ "error": err.Error(), }) return @@ -221,7 +221,7 @@ func (c *OneBotChannel) pinger(conn *websocket.Conn) { func (c *OneBotChannel) fetchSelfID() { resp, err := c.sendAPIRequest("get_login_info", nil, 5*time.Second) if err != nil { - logger.WarnCF("onebot", "Failed to get_login_info", map[string]interface{}{ + logger.WarnCF("onebot", "Failed to get_login_info", map[string]any{ "error": err.Error(), }) return @@ -251,7 +251,7 @@ func (c *OneBotChannel) fetchSelfID() { } if uid, err := parseJSONInt64(info.UserID); err == nil && uid > 0 { atomic.StoreInt64(&c.selfID, uid) - logger.InfoCF("onebot", "Bot self ID retrieved", map[string]interface{}{ + logger.InfoCF("onebot", "Bot self ID retrieved", map[string]any{ "self_id": uid, "nickname": info.Nickname, }) @@ -259,12 +259,12 @@ func (c *OneBotChannel) fetchSelfID() { } } - logger.WarnCF("onebot", "Could not parse self ID from get_login_info response", map[string]interface{}{ + logger.WarnCF("onebot", "Could not parse self ID from get_login_info response", map[string]any{ "response": string(resp), }) } -func (c *OneBotChannel) sendAPIRequest(action string, params interface{}, timeout time.Duration) (json.RawMessage, error) { +func (c *OneBotChannel) sendAPIRequest(action string, params any, timeout time.Duration) (json.RawMessage, error) { c.mu.Lock() conn := c.conn c.mu.Unlock() @@ -333,7 +333,7 @@ func (c *OneBotChannel) reconnectLoop() { if conn == nil { logger.InfoC("onebot", "Attempting to reconnect...") if err := c.connect(); err != nil { - logger.ErrorCF("onebot", "Reconnect failed", map[string]interface{}{ + logger.ErrorCF("onebot", "Reconnect failed", map[string]any{ "error": err.Error(), }) } else { @@ -406,7 +406,7 @@ func (c *OneBotChannel) Send(ctx context.Context, msg bus.OutboundMessage) error c.writeMu.Unlock() if err != nil { - logger.ErrorCF("onebot", "Failed to send message", map[string]interface{}{ + logger.ErrorCF("onebot", "Failed to send message", map[string]any{ "error": err.Error(), }) return err @@ -428,20 +428,20 @@ func (c *OneBotChannel) buildMessageSegments(chatID, content string) []oneBotMes if msgID, ok := lastMsgID.(string); ok && msgID != "" { segments = append(segments, oneBotMessageSegment{ Type: "reply", - Data: map[string]interface{}{"id": msgID}, + Data: map[string]any{"id": msgID}, }) } } segments = append(segments, oneBotMessageSegment{ Type: "text", - Data: map[string]interface{}{"text": content}, + Data: map[string]any{"text": content}, }) return segments } -func (c *OneBotChannel) buildSendRequest(msg bus.OutboundMessage) (string, interface{}, error) { +func (c *OneBotChannel) buildSendRequest(msg bus.OutboundMessage) (string, any, error) { chatID := msg.ChatID segments := c.buildMessageSegments(chatID, msg.Content) @@ -459,7 +459,7 @@ func (c *OneBotChannel) buildSendRequest(msg bus.OutboundMessage) (string, inter if err != nil { return "", nil, fmt.Errorf("invalid %s in chatID: %s", idKey, chatID) } - return action, map[string]interface{}{idKey: id, "message": segments}, nil + return action, map[string]any{idKey: id, "message": segments}, nil } func (c *OneBotChannel) listen() { @@ -479,7 +479,7 @@ func (c *OneBotChannel) listen() { default: _, message, err := conn.ReadMessage() if err != nil { - logger.ErrorCF("onebot", "WebSocket read error", map[string]interface{}{ + logger.ErrorCF("onebot", "WebSocket read error", map[string]any{ "error": err.Error(), }) c.mu.Lock() @@ -495,14 +495,14 @@ func (c *OneBotChannel) listen() { var raw oneBotRawEvent if err := json.Unmarshal(message, &raw); err != nil { - logger.WarnCF("onebot", "Failed to unmarshal raw event", map[string]interface{}{ + logger.WarnCF("onebot", "Failed to unmarshal raw event", map[string]any{ "error": err.Error(), "payload": string(message), }) continue } - logger.DebugCF("onebot", "WebSocket event", map[string]interface{}{ + logger.DebugCF("onebot", "WebSocket event", map[string]any{ "length": len(message), "post_type": raw.PostType, "sub_type": raw.SubType, @@ -519,7 +519,7 @@ func (c *OneBotChannel) listen() { default: } } else { - logger.DebugCF("onebot", "Received API response (no waiter)", map[string]interface{}{ + logger.DebugCF("onebot", "Received API response (no waiter)", map[string]any{ "echo": raw.Echo, "status": string(raw.Status), }) @@ -528,7 +528,7 @@ func (c *OneBotChannel) listen() { } if isAPIResponse(raw.Status) { - logger.DebugCF("onebot", "Received API response without echo, skipping", map[string]interface{}{ + logger.DebugCF("onebot", "Received API response without echo, skipping", map[string]any{ "status": string(raw.Status), }) continue @@ -595,7 +595,7 @@ func (c *OneBotChannel) parseMessageSegments(raw json.RawMessage, selfID int64) return parseMessageResult{Text: s, IsBotMentioned: mentioned} } - var segments []map[string]interface{} + var segments []map[string]any if err := json.Unmarshal(raw, &segments); err != nil { return parseMessageResult{} } @@ -609,7 +609,7 @@ func (c *OneBotChannel) parseMessageSegments(raw json.RawMessage, selfID int64) for _, seg := range segments { segType, _ := seg["type"].(string) - data, _ := seg["data"].(map[string]interface{}) + data, _ := seg["data"].(map[string]any) switch segType { case "text": @@ -663,7 +663,7 @@ func (c *OneBotChannel) parseMessageSegments(raw json.RawMessage, selfID int64) result, err := c.transcriber.Transcribe(tctx, localPath) tcancel() if err != nil { - logger.WarnCF("onebot", "Voice transcription failed", map[string]interface{}{ + logger.WarnCF("onebot", "Voice transcription failed", map[string]any{ "error": err.Error(), }) textParts = append(textParts, "[voice (transcription failed)]") @@ -714,7 +714,7 @@ func (c *OneBotChannel) handleRawEvent(raw *oneBotRawEvent) { case "message": if userID, err := parseJSONInt64(raw.UserID); err == nil && userID > 0 { if !c.IsAllowed(strconv.FormatInt(userID, 10)) { - logger.DebugCF("onebot", "Message rejected by allowlist", map[string]interface{}{ + logger.DebugCF("onebot", "Message rejected by allowlist", map[string]any{ "user_id": userID, }) return @@ -723,7 +723,7 @@ func (c *OneBotChannel) handleRawEvent(raw *oneBotRawEvent) { c.handleMessage(raw) case "message_sent": - logger.DebugCF("onebot", "Bot sent message event", map[string]interface{}{ + logger.DebugCF("onebot", "Bot sent message event", map[string]any{ "message_type": raw.MessageType, "message_id": parseJSONString(raw.MessageID), }) @@ -735,18 +735,18 @@ func (c *OneBotChannel) handleRawEvent(raw *oneBotRawEvent) { c.handleNoticeEvent(raw) case "request": - logger.DebugCF("onebot", "Request event received", map[string]interface{}{ + logger.DebugCF("onebot", "Request event received", map[string]any{ "sub_type": raw.SubType, }) case "": - logger.DebugCF("onebot", "Event with empty post_type (possibly API response)", map[string]interface{}{ + logger.DebugCF("onebot", "Event with empty post_type (possibly API response)", map[string]any{ "echo": raw.Echo, "status": raw.Status, }) default: - logger.DebugCF("onebot", "Unknown post_type", map[string]interface{}{ + logger.DebugCF("onebot", "Unknown post_type", map[string]any{ "post_type": raw.PostType, }) } @@ -754,14 +754,14 @@ func (c *OneBotChannel) handleRawEvent(raw *oneBotRawEvent) { func (c *OneBotChannel) handleMetaEvent(raw *oneBotRawEvent) { if raw.MetaEventType == "lifecycle" { - logger.InfoCF("onebot", "Lifecycle event", map[string]interface{}{"sub_type": raw.SubType}) + logger.InfoCF("onebot", "Lifecycle event", map[string]any{"sub_type": raw.SubType}) } else if raw.MetaEventType != "heartbeat" { logger.DebugCF("onebot", "Meta event: "+raw.MetaEventType, nil) } } func (c *OneBotChannel) handleNoticeEvent(raw *oneBotRawEvent) { - fields := map[string]interface{}{ + fields := map[string]any{ "notice_type": raw.NoticeType, "sub_type": raw.SubType, "group_id": parseJSONString(raw.GroupID), @@ -781,7 +781,7 @@ func (c *OneBotChannel) handleMessage(raw *oneBotRawEvent) { // Parse fields from raw event userID, err := parseJSONInt64(raw.UserID) if err != nil { - logger.WarnCF("onebot", "Failed to parse user_id", map[string]interface{}{ + logger.WarnCF("onebot", "Failed to parse user_id", map[string]any{ "error": err.Error(), "raw": string(raw.UserID), }) @@ -818,7 +818,7 @@ func (c *OneBotChannel) handleMessage(raw *oneBotRawEvent) { var sender oneBotSender if len(raw.Sender) > 0 { if err := json.Unmarshal(raw.Sender, &sender); err != nil { - logger.WarnCF("onebot", "Failed to parse sender", map[string]interface{}{ + logger.WarnCF("onebot", "Failed to parse sender", map[string]any{ "error": err.Error(), "sender": string(raw.Sender), }) @@ -830,7 +830,7 @@ func (c *OneBotChannel) handleMessage(raw *oneBotRawEvent) { defer func() { for _, f := range parsed.LocalFiles { if err := os.Remove(f); err != nil { - logger.DebugCF("onebot", "Failed to remove temp file", map[string]interface{}{ + logger.DebugCF("onebot", "Failed to remove temp file", map[string]any{ "path": f, "error": err.Error(), }) @@ -840,14 +840,14 @@ func (c *OneBotChannel) handleMessage(raw *oneBotRawEvent) { } if c.isDuplicate(messageID) { - logger.DebugCF("onebot", "Duplicate message, skipping", map[string]interface{}{ + logger.DebugCF("onebot", "Duplicate message, skipping", map[string]any{ "message_id": messageID, }) return } if content == "" { - logger.DebugCF("onebot", "Received empty message, ignoring", map[string]interface{}{ + logger.DebugCF("onebot", "Received empty message, ignoring", map[string]any{ "message_id": messageID, }) return @@ -890,7 +890,7 @@ func (c *OneBotChannel) handleMessage(raw *oneBotRawEvent) { triggered, strippedContent := c.checkGroupTrigger(content, isBotMentioned) if !triggered { - logger.DebugCF("onebot", "Group message ignored (no trigger)", map[string]interface{}{ + logger.DebugCF("onebot", "Group message ignored (no trigger)", map[string]any{ "sender": senderID, "group": groupIDStr, "is_mentioned": isBotMentioned, @@ -901,7 +901,7 @@ func (c *OneBotChannel) handleMessage(raw *oneBotRawEvent) { content = strippedContent default: - logger.WarnCF("onebot", "Unknown message type, cannot route", map[string]interface{}{ + logger.WarnCF("onebot", "Unknown message type, cannot route", map[string]any{ "type": raw.MessageType, "message_id": messageID, "user_id": userID, @@ -909,7 +909,7 @@ func (c *OneBotChannel) handleMessage(raw *oneBotRawEvent) { return } - logger.InfoCF("onebot", "Received "+raw.MessageType+" message", map[string]interface{}{ + logger.InfoCF("onebot", "Received "+raw.MessageType+" message", map[string]any{ "sender": senderID, "chat_id": chatID, "message_id": messageID, @@ -962,7 +962,10 @@ func truncate(s string, n int) string { return string(runes[:n]) + "..." } -func (c *OneBotChannel) checkGroupTrigger(content string, isBotMentioned bool) (triggered bool, strippedContent string) { +func (c *OneBotChannel) checkGroupTrigger( + content string, + isBotMentioned bool, +) (triggered bool, strippedContent string) { if isBotMentioned { return true, strings.TrimSpace(content) } diff --git a/pkg/channels/qq/qq.go b/pkg/channels/qq/qq.go index 9b07be0cc..2a95bbd06 100644 --- a/pkg/channels/qq/qq.go +++ b/pkg/channels/qq/qq.go @@ -78,7 +78,7 @@ func (c *QQChannel) Start(ctx context.Context) error { return fmt.Errorf("failed to get websocket info: %w", err) } - logger.InfoCF("qq", "Got WebSocket info", map[string]interface{}{ + logger.InfoCF("qq", "Got WebSocket info", map[string]any{ "shards": wsInfo.Shards, }) @@ -88,7 +88,7 @@ func (c *QQChannel) Start(ctx context.Context) error { // 在 goroutine 中启动 WebSocket 连接,避免阻塞 go func() { if err := c.sessionManager.Start(wsInfo, c.tokenSource, &intent); err != nil { - logger.ErrorCF("qq", "WebSocket session error", map[string]interface{}{ + logger.ErrorCF("qq", "WebSocket session error", map[string]any{ "error": err.Error(), }) c.SetRunning(false) @@ -125,7 +125,7 @@ func (c *QQChannel) Send(ctx context.Context, msg bus.OutboundMessage) error { // C2C 消息发送 _, err := c.api.PostC2CMessage(ctx, msg.ChatID, msgToCreate) if err != nil { - logger.ErrorCF("qq", "Failed to send C2C message", map[string]interface{}{ + logger.ErrorCF("qq", "Failed to send C2C message", map[string]any{ "error": err.Error(), }) return err @@ -158,7 +158,7 @@ func (c *QQChannel) handleC2CMessage() event.C2CMessageEventHandler { return nil } - logger.InfoCF("qq", "Received C2C message", map[string]interface{}{ + logger.InfoCF("qq", "Received C2C message", map[string]any{ "sender": senderID, "length": len(content), }) @@ -200,7 +200,7 @@ func (c *QQChannel) handleGroupATMessage() event.GroupATMessageEventHandler { return nil } - logger.InfoCF("qq", "Received group AT message", map[string]interface{}{ + logger.InfoCF("qq", "Received group AT message", map[string]any{ "sender": senderID, "group": data.GroupID, "length": len(content), diff --git a/pkg/channels/slack/slack.go b/pkg/channels/slack/slack.go index dc5190fc9..cafe53103 100644 --- a/pkg/channels/slack/slack.go +++ b/pkg/channels/slack/slack.go @@ -76,7 +76,7 @@ func (c *SlackChannel) Start(ctx context.Context) error { c.botUserID = authResp.UserID c.teamID = authResp.TeamID - logger.InfoCF("slack", "Slack bot connected", map[string]interface{}{ + logger.InfoCF("slack", "Slack bot connected", map[string]any{ "bot_user_id": c.botUserID, "team": authResp.Team, }) @@ -86,7 +86,7 @@ func (c *SlackChannel) Start(ctx context.Context) error { go func() { if err := c.socketClient.RunContext(c.ctx); err != nil { if c.ctx.Err() == nil { - logger.ErrorCF("slack", "Socket Mode connection error", map[string]interface{}{ + logger.ErrorCF("slack", "Socket Mode connection error", map[string]any{ "error": err.Error(), }) } @@ -141,7 +141,7 @@ func (c *SlackChannel) Send(ctx context.Context, msg bus.OutboundMessage) error }) } - logger.DebugCF("slack", "Message sent", map[string]interface{}{ + logger.DebugCF("slack", "Message sent", map[string]any{ "channel_id": channelID, "thread_ts": threadTS, }) @@ -203,7 +203,7 @@ func (c *SlackChannel) handleMessageEvent(ev *slackevents.MessageEvent) { // 检查白名单,避免为被拒绝的用户下载附件 if !c.IsAllowed(ev.User) { - logger.DebugCF("slack", "Message rejected by allowlist", map[string]interface{}{ + logger.DebugCF("slack", "Message rejected by allowlist", map[string]any{ "user_id": ev.User, }) return @@ -239,7 +239,7 @@ func (c *SlackChannel) handleMessageEvent(ev *slackevents.MessageEvent) { defer func() { for _, file := range localFiles { if err := os.Remove(file); err != nil { - logger.DebugCF("slack", "Failed to cleanup temp file", map[string]interface{}{ + logger.DebugCF("slack", "Failed to cleanup temp file", map[string]any{ "file": file, "error": err.Error(), }) @@ -262,7 +262,7 @@ func (c *SlackChannel) handleMessageEvent(ev *slackevents.MessageEvent) { result, err := c.transcriber.Transcribe(ctx, localPath) if err != nil { - logger.ErrorCF("slack", "Voice transcription failed", map[string]interface{}{"error": err.Error()}) + logger.ErrorCF("slack", "Voice transcription failed", map[string]any{"error": err.Error()}) content += fmt.Sprintf("\n[audio: %s (transcription failed)]", file.Name) } else { content += fmt.Sprintf("\n[voice transcription: %s]", result.Text) @@ -294,7 +294,7 @@ func (c *SlackChannel) handleMessageEvent(ev *slackevents.MessageEvent) { "team_id": c.teamID, } - logger.DebugCF("slack", "Received message", map[string]interface{}{ + logger.DebugCF("slack", "Received message", map[string]any{ "sender_id": senderID, "chat_id": chatID, "preview": utils.Truncate(content, 50), @@ -310,7 +310,7 @@ func (c *SlackChannel) handleAppMention(ev *slackevents.AppMentionEvent) { } if !c.IsAllowed(ev.User) { - logger.DebugCF("slack", "Mention rejected by allowlist", map[string]interface{}{ + logger.DebugCF("slack", "Mention rejected by allowlist", map[string]any{ "user_id": ev.User, }) return @@ -376,7 +376,7 @@ func (c *SlackChannel) handleSlashCommand(event socketmode.Event) { } if !c.IsAllowed(cmd.UserID) { - logger.DebugCF("slack", "Slash command rejected by allowlist", map[string]interface{}{ + logger.DebugCF("slack", "Slash command rejected by allowlist", map[string]any{ "user_id": cmd.UserID, }) return @@ -401,7 +401,7 @@ func (c *SlackChannel) handleSlashCommand(event socketmode.Event) { "team_id": c.teamID, } - logger.DebugCF("slack", "Slash command received", map[string]interface{}{ + logger.DebugCF("slack", "Slash command received", map[string]any{ "sender_id": senderID, "command": cmd.Command, "text": utils.Truncate(content, 50), @@ -416,7 +416,7 @@ func (c *SlackChannel) downloadSlackFile(file slack.File) string { downloadURL = file.URLPrivate } if downloadURL == "" { - logger.ErrorCF("slack", "No download URL for file", map[string]interface{}{"file_id": file.ID}) + logger.ErrorCF("slack", "No download URL for file", map[string]any{"file_id": file.ID}) return "" } diff --git a/pkg/channels/telegram/telegram.go b/pkg/channels/telegram/telegram.go index f4c5108df..7619440e2 100644 --- a/pkg/channels/telegram/telegram.go +++ b/pkg/channels/telegram/telegram.go @@ -11,10 +11,9 @@ import ( "sync" "time" - th "github.com/mymmrac/telego/telegohandler" - "github.com/mymmrac/telego" "github.com/mymmrac/telego/telegohandler" + th "github.com/mymmrac/telego/telegohandler" tu "github.com/mymmrac/telego/telegoutil" "github.com/sipeed/picoclaw/pkg/bus" @@ -128,7 +127,7 @@ func (c *TelegramChannel) Start(ctx context.Context) error { }, th.AnyMessage()) c.SetRunning(true) - logger.InfoCF("telegram", "Telegram bot connected", map[string]interface{}{ + logger.InfoCF("telegram", "Telegram bot connected", map[string]any{ "username": c.bot.Username(), }) @@ -141,6 +140,7 @@ func (c *TelegramChannel) Start(ctx context.Context) error { return nil } + func (c *TelegramChannel) Stop(ctx context.Context) error { logger.InfoC("telegram", "Stopping Telegram bot...") c.SetRunning(false) @@ -183,7 +183,7 @@ func (c *TelegramChannel) Send(ctx context.Context, msg bus.OutboundMessage) err tgMsg.ParseMode = telego.ModeHTML if _, err = c.bot.SendMessage(ctx, tgMsg); err != nil { - logger.ErrorCF("telegram", "HTML parse failed, falling back to plain text", map[string]interface{}{ + logger.ErrorCF("telegram", "HTML parse failed, falling back to plain text", map[string]any{ "error": err.Error(), }) tgMsg.ParseMode = "" @@ -211,7 +211,7 @@ func (c *TelegramChannel) handleMessage(ctx context.Context, message *telego.Mes // 检查白名单,避免为被拒绝的用户下载附件 if !c.IsAllowed(senderID) { - logger.DebugCF("telegram", "Message rejected by allowlist", map[string]interface{}{ + logger.DebugCF("telegram", "Message rejected by allowlist", map[string]any{ "user_id": senderID, }) return nil @@ -228,7 +228,7 @@ func (c *TelegramChannel) handleMessage(ctx context.Context, message *telego.Mes defer func() { for _, file := range localFiles { if err := os.Remove(file); err != nil { - logger.DebugCF("telegram", "Failed to cleanup temp file", map[string]interface{}{ + logger.DebugCF("telegram", "Failed to cleanup temp file", map[string]any{ "file": file, "error": err.Error(), }) @@ -268,19 +268,19 @@ func (c *TelegramChannel) handleMessage(ctx context.Context, message *telego.Mes transcribedText := "" if c.transcriber != nil && c.transcriber.IsAvailable() { - ctx, cancel := context.WithTimeout(ctx, 30*time.Second) + transcriberCtx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() - result, err := c.transcriber.Transcribe(ctx, voicePath) + result, err := c.transcriber.Transcribe(transcriberCtx, voicePath) if err != nil { - logger.ErrorCF("telegram", "Voice transcription failed", map[string]interface{}{ + logger.ErrorCF("telegram", "Voice transcription failed", map[string]any{ "error": err.Error(), "path": voicePath, }) transcribedText = "[voice (transcription failed)]" } else { transcribedText = fmt.Sprintf("[voice transcription: %s]", result.Text) - logger.InfoCF("telegram", "Voice transcribed successfully", map[string]interface{}{ + logger.InfoCF("telegram", "Voice transcribed successfully", map[string]any{ "text": result.Text, }) } @@ -323,7 +323,7 @@ func (c *TelegramChannel) handleMessage(ctx context.Context, message *telego.Mes content = "[empty message]" } - logger.DebugCF("telegram", "Received message", map[string]interface{}{ + logger.DebugCF("telegram", "Received message", map[string]any{ "sender_id": senderID, "chat_id": fmt.Sprintf("%d", chatID), "preview": utils.Truncate(content, 50), @@ -332,7 +332,7 @@ func (c *TelegramChannel) handleMessage(ctx context.Context, message *telego.Mes // Thinking indicator err := c.bot.SendChatAction(ctx, tu.ChatAction(tu.ID(chatID), telego.ChatActionTyping)) if err != nil { - logger.ErrorCF("telegram", "Failed to send chat action", map[string]interface{}{ + logger.ErrorCF("telegram", "Failed to send chat action", map[string]any{ "error": err.Error(), }) } @@ -379,7 +379,7 @@ func (c *TelegramChannel) handleMessage(ctx context.Context, message *telego.Mes func (c *TelegramChannel) downloadPhoto(ctx context.Context, fileID string) string { file, err := c.bot.GetFile(ctx, &telego.GetFileParams{FileID: fileID}) if err != nil { - logger.ErrorCF("telegram", "Failed to get photo file", map[string]interface{}{ + logger.ErrorCF("telegram", "Failed to get photo file", map[string]any{ "error": err.Error(), }) return "" @@ -394,7 +394,7 @@ func (c *TelegramChannel) downloadFileWithInfo(file *telego.File, ext string) st } url := c.bot.FileDownloadURL(file.FilePath) - logger.DebugCF("telegram", "File URL", map[string]interface{}{"url": url}) + logger.DebugCF("telegram", "File URL", map[string]any{"url": url}) // Use FilePath as filename for better identification filename := file.FilePath + ext @@ -406,7 +406,7 @@ func (c *TelegramChannel) downloadFileWithInfo(file *telego.File, ext string) st func (c *TelegramChannel) downloadFile(ctx context.Context, fileID, ext string) string { file, err := c.bot.GetFile(ctx, &telego.GetFileParams{FileID: fileID}) if err != nil { - logger.ErrorCF("telegram", "Failed to get file", map[string]interface{}{ + logger.ErrorCF("telegram", "Failed to get file", map[string]any{ "error": err.Error(), }) return "" @@ -464,7 +464,11 @@ func markdownToTelegramHTML(text string) string { for i, code := range codeBlocks.codes { escaped := escapeHTML(code) - text = strings.ReplaceAll(text, fmt.Sprintf("\x00CB%d\x00", i), fmt.Sprintf("
%s", escaped))
+ text = strings.ReplaceAll(
+ text,
+ fmt.Sprintf("\x00CB%d\x00", i),
+ fmt.Sprintf("%s", escaped),
+ )
}
return text
diff --git a/pkg/channels/telegram/telegram_commands.go b/pkg/channels/telegram/telegram_commands.go
index 4bf1b3aff..f17912260 100644
--- a/pkg/channels/telegram/telegram_commands.go
+++ b/pkg/channels/telegram/telegram_commands.go
@@ -6,6 +6,7 @@ import (
"strings"
"github.com/mymmrac/telego"
+
"github.com/sipeed/picoclaw/pkg/config"
)
@@ -35,6 +36,7 @@ func commandArgs(text string) string {
}
return strings.TrimSpace(parts[1])
}
+
func (c *cmd) Help(ctx context.Context, message telego.Message) error {
msg := `/start - Start the bot
/help - Show this help message
@@ -96,6 +98,7 @@ func (c *cmd) Show(ctx context.Context, message telego.Message) error {
})
return err
}
+
func (c *cmd) List(ctx context.Context, message telego.Message) error {
args := commandArgs(message.Text)
if args == "" {
diff --git a/pkg/channels/wecom/app.go b/pkg/channels/wecom/app.go
index 85c017958..f3557d60f 100644
--- a/pkg/channels/wecom/app.go
+++ b/pkg/channels/wecom/app.go
@@ -142,7 +142,7 @@ func (c *WeComAppChannel) Start(ctx context.Context) error {
// Get initial access token
if err := c.refreshAccessToken(); err != nil {
- logger.WarnCF("wecom_app", "Failed to get initial access token", map[string]interface{}{
+ logger.WarnCF("wecom_app", "Failed to get initial access token", map[string]any{
"error": err.Error(),
})
}
@@ -168,7 +168,7 @@ func (c *WeComAppChannel) Start(ctx context.Context) error {
}
c.SetRunning(true)
- logger.InfoCF("wecom_app", "WeCom App channel started", map[string]interface{}{
+ logger.InfoCF("wecom_app", "WeCom App channel started", map[string]any{
"address": addr,
"path": webhookPath,
})
@@ -176,7 +176,7 @@ func (c *WeComAppChannel) Start(ctx context.Context) error {
// Start server in goroutine
go func() {
if err := c.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
- logger.ErrorCF("wecom_app", "HTTP server error", map[string]interface{}{
+ logger.ErrorCF("wecom_app", "HTTP server error", map[string]any{
"error": err.Error(),
})
}
@@ -215,7 +215,7 @@ func (c *WeComAppChannel) Send(ctx context.Context, msg bus.OutboundMessage) err
return fmt.Errorf("no valid access token available")
}
- logger.DebugCF("wecom_app", "Sending message", map[string]interface{}{
+ logger.DebugCF("wecom_app", "Sending message", map[string]any{
"chat_id": msg.ChatID,
"preview": utils.Truncate(msg.Content, 100),
})
@@ -228,7 +228,7 @@ func (c *WeComAppChannel) handleWebhook(w http.ResponseWriter, r *http.Request)
ctx := r.Context()
// Log all incoming requests for debugging
- logger.DebugCF("wecom_app", "Received webhook request", map[string]interface{}{
+ logger.DebugCF("wecom_app", "Received webhook request", map[string]any{
"method": r.Method,
"url": r.URL.String(),
"path": r.URL.Path,
@@ -247,7 +247,7 @@ func (c *WeComAppChannel) handleWebhook(w http.ResponseWriter, r *http.Request)
return
}
- logger.WarnCF("wecom_app", "Method not allowed", map[string]interface{}{
+ logger.WarnCF("wecom_app", "Method not allowed", map[string]any{
"method": r.Method,
})
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
@@ -261,7 +261,7 @@ func (c *WeComAppChannel) handleVerification(ctx context.Context, w http.Respons
nonce := query.Get("nonce")
echostr := query.Get("echostr")
- logger.DebugCF("wecom_app", "Handling verification request", map[string]interface{}{
+ logger.DebugCF("wecom_app", "Handling verification request", map[string]any{
"msg_signature": msgSignature,
"timestamp": timestamp,
"nonce": nonce,
@@ -277,7 +277,7 @@ func (c *WeComAppChannel) handleVerification(ctx context.Context, w http.Respons
// Verify signature
if !verifySignature(c.config.Token, msgSignature, timestamp, nonce, echostr) {
- logger.WarnCF("wecom_app", "Signature verification failed", map[string]interface{}{
+ logger.WarnCF("wecom_app", "Signature verification failed", map[string]any{
"token": c.config.Token,
"msg_signature": msgSignature,
"timestamp": timestamp,
@@ -291,13 +291,13 @@ func (c *WeComAppChannel) handleVerification(ctx context.Context, w http.Respons
// Decrypt echostr with CorpID verification
// For WeCom App (自建应用), receiveid should be corp_id
- logger.DebugCF("wecom_app", "Attempting to decrypt echostr", map[string]interface{}{
+ logger.DebugCF("wecom_app", "Attempting to decrypt echostr", map[string]any{
"encoding_aes_key": c.config.EncodingAESKey,
"corp_id": c.config.CorpID,
})
decryptedEchoStr, err := decryptMessageWithVerify(echostr, c.config.EncodingAESKey, c.config.CorpID)
if err != nil {
- logger.ErrorCF("wecom_app", "Failed to decrypt echostr", map[string]interface{}{
+ logger.ErrorCF("wecom_app", "Failed to decrypt echostr", map[string]any{
"error": err.Error(),
"encoding_aes_key": c.config.EncodingAESKey,
"corp_id": c.config.CorpID,
@@ -306,7 +306,7 @@ func (c *WeComAppChannel) handleVerification(ctx context.Context, w http.Respons
return
}
- logger.DebugCF("wecom_app", "Successfully decrypted echostr", map[string]interface{}{
+ logger.DebugCF("wecom_app", "Successfully decrypted echostr", map[string]any{
"decrypted": decryptedEchoStr,
})
@@ -345,8 +345,8 @@ func (c *WeComAppChannel) handleMessageCallback(ctx context.Context, w http.Resp
AgentID string `xml:"AgentID"`
}
- if err := xml.Unmarshal(body, &encryptedMsg); err != nil {
- logger.ErrorCF("wecom_app", "Failed to parse XML", map[string]interface{}{
+ if err = xml.Unmarshal(body, &encryptedMsg); err != nil {
+ logger.ErrorCF("wecom_app", "Failed to parse XML", map[string]any{
"error": err.Error(),
})
http.Error(w, "Invalid XML", http.StatusBadRequest)
@@ -364,7 +364,7 @@ func (c *WeComAppChannel) handleMessageCallback(ctx context.Context, w http.Resp
// For WeCom App (自建应用), receiveid should be corp_id
decryptedMsg, err := decryptMessageWithVerify(encryptedMsg.Encrypt, c.config.EncodingAESKey, c.config.CorpID)
if err != nil {
- logger.ErrorCF("wecom_app", "Failed to decrypt message", map[string]interface{}{
+ logger.ErrorCF("wecom_app", "Failed to decrypt message", map[string]any{
"error": err.Error(),
})
http.Error(w, "Decryption failed", http.StatusInternalServerError)
@@ -374,7 +374,7 @@ func (c *WeComAppChannel) handleMessageCallback(ctx context.Context, w http.Resp
// Parse decrypted XML message
var msg WeComXMLMessage
if err := xml.Unmarshal([]byte(decryptedMsg), &msg); err != nil {
- logger.ErrorCF("wecom_app", "Failed to parse decrypted message", map[string]interface{}{
+ logger.ErrorCF("wecom_app", "Failed to parse decrypted message", map[string]any{
"error": err.Error(),
})
http.Error(w, "Invalid message format", http.StatusBadRequest)
@@ -393,7 +393,7 @@ func (c *WeComAppChannel) handleMessageCallback(ctx context.Context, w http.Resp
func (c *WeComAppChannel) processMessage(ctx context.Context, msg WeComXMLMessage) {
// Skip non-text messages for now (can be extended)
if msg.MsgType != "text" && msg.MsgType != "image" && msg.MsgType != "voice" {
- logger.DebugCF("wecom_app", "Skipping non-supported message type", map[string]interface{}{
+ logger.DebugCF("wecom_app", "Skipping non-supported message type", map[string]any{
"msg_type": msg.MsgType,
})
return
@@ -405,7 +405,7 @@ func (c *WeComAppChannel) processMessage(ctx context.Context, msg WeComXMLMessag
c.msgMu.Lock()
if c.processedMsgs[msgID] {
c.msgMu.Unlock()
- logger.DebugCF("wecom_app", "Skipping duplicate message", map[string]interface{}{
+ logger.DebugCF("wecom_app", "Skipping duplicate message", map[string]any{
"msg_id": msgID,
})
return
@@ -438,7 +438,7 @@ func (c *WeComAppChannel) processMessage(ctx context.Context, msg WeComXMLMessag
content := msg.Content
- logger.DebugCF("wecom_app", "Received message", map[string]interface{}{
+ logger.DebugCF("wecom_app", "Received message", map[string]any{
"sender_id": senderID,
"msg_type": msg.MsgType,
"preview": utils.Truncate(content, 50),
@@ -459,7 +459,7 @@ func (c *WeComAppChannel) tokenRefreshLoop() {
return
case <-ticker.C:
if err := c.refreshAccessToken(); err != nil {
- logger.ErrorCF("wecom_app", "Failed to refresh access token", map[string]interface{}{
+ logger.ErrorCF("wecom_app", "Failed to refresh access token", map[string]any{
"error": err.Error(),
})
}
@@ -625,7 +625,7 @@ func (c *WeComAppChannel) sendMarkdownMessage(ctx context.Context, accessToken,
// handleHealth handles health check requests
func (c *WeComAppChannel) handleHealth(w http.ResponseWriter, r *http.Request) {
- status := map[string]interface{}{
+ status := map[string]any{
"status": "ok",
"running": c.IsRunning(),
"has_token": c.getAccessToken() != "",
diff --git a/pkg/channels/wecom/app_test.go b/pkg/channels/wecom/app_test.go
index d9817fd49..5420949de 100644
--- a/pkg/channels/wecom/app_test.go
+++ b/pkg/channels/wecom/app_test.go
@@ -396,7 +396,11 @@ func TestWeComAppHandleVerification(t *testing.T) {
nonce := "test_nonce"
signature := generateSignatureApp("test_token", timestamp, nonce, encryptedEchostr)
- req := httptest.NewRequest(http.MethodGet, "/webhook/wecom-app?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce+"&echostr="+encryptedEchostr, nil)
+ req := httptest.NewRequest(
+ http.MethodGet,
+ "/webhook/wecom-app?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce+"&echostr="+encryptedEchostr,
+ nil,
+ )
w := httptest.NewRecorder()
ch.handleVerification(context.Background(), w, req)
@@ -426,7 +430,11 @@ func TestWeComAppHandleVerification(t *testing.T) {
timestamp := "1234567890"
nonce := "test_nonce"
- req := httptest.NewRequest(http.MethodGet, "/webhook/wecom-app?msg_signature=invalid_sig×tamp="+timestamp+"&nonce="+nonce+"&echostr="+encryptedEchostr, nil)
+ req := httptest.NewRequest(
+ http.MethodGet,
+ "/webhook/wecom-app?msg_signature=invalid_sig×tamp="+timestamp+"&nonce="+nonce+"&echostr="+encryptedEchostr,
+ nil,
+ )
w := httptest.NewRecorder()
ch.handleVerification(context.Background(), w, req)
@@ -478,7 +486,11 @@ func TestWeComAppHandleMessageCallback(t *testing.T) {
nonce := "test_nonce"
signature := generateSignatureApp("test_token", timestamp, nonce, encrypted)
- req := httptest.NewRequest(http.MethodPost, "/webhook/wecom-app?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce, bytes.NewReader(wrapperData))
+ req := httptest.NewRequest(
+ http.MethodPost,
+ "/webhook/wecom-app?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce,
+ bytes.NewReader(wrapperData),
+ )
w := httptest.NewRecorder()
ch.handleMessageCallback(context.Background(), w, req)
@@ -507,7 +519,11 @@ func TestWeComAppHandleMessageCallback(t *testing.T) {
nonce := "test_nonce"
signature := generateSignatureApp("test_token", timestamp, nonce, "")
- req := httptest.NewRequest(http.MethodPost, "/webhook/wecom-app?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce, strings.NewReader("invalid xml"))
+ req := httptest.NewRequest(
+ http.MethodPost,
+ "/webhook/wecom-app?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce,
+ strings.NewReader("invalid xml"),
+ )
w := httptest.NewRecorder()
ch.handleMessageCallback(context.Background(), w, req)
@@ -529,7 +545,11 @@ func TestWeComAppHandleMessageCallback(t *testing.T) {
timestamp := "1234567890"
nonce := "test_nonce"
- req := httptest.NewRequest(http.MethodPost, "/webhook/wecom-app?msg_signature=invalid_sig×tamp="+timestamp+"&nonce="+nonce, bytes.NewReader(wrapperData))
+ req := httptest.NewRequest(
+ http.MethodPost,
+ "/webhook/wecom-app?msg_signature=invalid_sig×tamp="+timestamp+"&nonce="+nonce,
+ bytes.NewReader(wrapperData),
+ )
w := httptest.NewRecorder()
ch.handleMessageCallback(context.Background(), w, req)
@@ -643,7 +663,11 @@ func TestWeComAppHandleWebhook(t *testing.T) {
nonce := "test_nonce"
signature := generateSignatureApp("test_token", timestamp, nonce, encoded)
- req := httptest.NewRequest(http.MethodGet, "/webhook/wecom-app?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce+"&echostr="+encoded, nil)
+ req := httptest.NewRequest(
+ http.MethodGet,
+ "/webhook/wecom-app?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce+"&echostr="+encoded,
+ nil,
+ )
w := httptest.NewRecorder()
ch.handleWebhook(w, req)
@@ -666,7 +690,11 @@ func TestWeComAppHandleWebhook(t *testing.T) {
nonce := "test_nonce"
signature := generateSignatureApp("test_token", timestamp, nonce, encryptedWrapper.Encrypt)
- req := httptest.NewRequest(http.MethodPost, "/webhook/wecom-app?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce, bytes.NewReader(wrapperData))
+ req := httptest.NewRequest(
+ http.MethodPost,
+ "/webhook/wecom-app?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce,
+ bytes.NewReader(wrapperData),
+ )
w := httptest.NewRecorder()
ch.handleWebhook(w, req)
@@ -832,15 +860,24 @@ func TestWeComAppMessageStructures(t *testing.T) {
if msg.Image.MediaID != "media_123456" {
t.Errorf("Image.MediaID = %q, want %q", msg.Image.MediaID, "media_123456")
}
+ if msg.ToUser != "user123" {
+ t.Errorf("ToUser = %q, want %q", msg.ToUser, "user123")
+ }
+ if msg.MsgType != "image" {
+ t.Errorf("MsgType = %q, want %q", msg.MsgType, "image")
+ }
+ if msg.AgentID != 1000002 {
+ t.Errorf("AgentID = %d, want %d", msg.AgentID, 1000002)
+ }
})
t.Run("WeComAccessTokenResponse structure", func(t *testing.T) {
jsonData := `{
- "errcode": 0,
- "errmsg": "ok",
- "access_token": "test_access_token",
- "expires_in": 7200
- }`
+ "errcode": 0,
+ "errmsg": "ok",
+ "access_token": "test_access_token",
+ "expires_in": 7200
+ }`
var resp WeComAccessTokenResponse
err := json.Unmarshal([]byte(jsonData), &resp)
@@ -864,12 +901,12 @@ func TestWeComAppMessageStructures(t *testing.T) {
t.Run("WeComSendMessageResponse structure", func(t *testing.T) {
jsonData := `{
- "errcode": 0,
- "errmsg": "ok",
- "invaliduser": "",
- "invalidparty": "",
- "invalidtag": ""
- }`
+ "errcode": 0,
+ "errmsg": "ok",
+ "invaliduser": "",
+ "invalidparty": "",
+ "invalidtag": ""
+ }`
var resp WeComSendMessageResponse
err := json.Unmarshal([]byte(jsonData), &resp)
diff --git a/pkg/channels/wecom/bot.go b/pkg/channels/wecom/bot.go
index 9683a308f..17ee2107f 100644
--- a/pkg/channels/wecom/bot.go
+++ b/pkg/channels/wecom/bot.go
@@ -125,7 +125,7 @@ func (c *WeComBotChannel) Start(ctx context.Context) error {
}
c.SetRunning(true)
- logger.InfoCF("wecom", "WeCom Bot channel started", map[string]interface{}{
+ logger.InfoCF("wecom", "WeCom Bot channel started", map[string]any{
"address": addr,
"path": webhookPath,
})
@@ -133,7 +133,7 @@ func (c *WeComBotChannel) Start(ctx context.Context) error {
// Start server in goroutine
go func() {
if err := c.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
- logger.ErrorCF("wecom", "HTTP server error", map[string]interface{}{
+ logger.ErrorCF("wecom", "HTTP server error", map[string]any{
"error": err.Error(),
})
}
@@ -169,7 +169,7 @@ func (c *WeComBotChannel) Send(ctx context.Context, msg bus.OutboundMessage) err
return fmt.Errorf("wecom channel not running")
}
- logger.DebugCF("wecom", "Sending message via webhook", map[string]interface{}{
+ logger.DebugCF("wecom", "Sending message via webhook", map[string]any{
"chat_id": msg.ChatID,
"preview": utils.Truncate(msg.Content, 100),
})
@@ -221,7 +221,7 @@ func (c *WeComBotChannel) handleVerification(ctx context.Context, w http.Respons
// Reference: https://developer.work.weixin.qq.com/document/path/101033
decryptedEchoStr, err := decryptMessageWithVerify(echostr, c.config.EncodingAESKey, "")
if err != nil {
- logger.ErrorCF("wecom", "Failed to decrypt echostr", map[string]interface{}{
+ logger.ErrorCF("wecom", "Failed to decrypt echostr", map[string]any{
"error": err.Error(),
})
http.Error(w, "Decryption failed", http.StatusInternalServerError)
@@ -263,8 +263,8 @@ func (c *WeComBotChannel) handleMessageCallback(ctx context.Context, w http.Resp
AgentID string `xml:"AgentID"`
}
- if err := xml.Unmarshal(body, &encryptedMsg); err != nil {
- logger.ErrorCF("wecom", "Failed to parse XML", map[string]interface{}{
+ if err = xml.Unmarshal(body, &encryptedMsg); err != nil {
+ logger.ErrorCF("wecom", "Failed to parse XML", map[string]any{
"error": err.Error(),
})
http.Error(w, "Invalid XML", http.StatusBadRequest)
@@ -283,7 +283,7 @@ func (c *WeComBotChannel) handleMessageCallback(ctx context.Context, w http.Resp
// Reference: https://developer.work.weixin.qq.com/document/path/101033
decryptedMsg, err := decryptMessageWithVerify(encryptedMsg.Encrypt, c.config.EncodingAESKey, "")
if err != nil {
- logger.ErrorCF("wecom", "Failed to decrypt message", map[string]interface{}{
+ logger.ErrorCF("wecom", "Failed to decrypt message", map[string]any{
"error": err.Error(),
})
http.Error(w, "Decryption failed", http.StatusInternalServerError)
@@ -293,7 +293,7 @@ func (c *WeComBotChannel) handleMessageCallback(ctx context.Context, w http.Resp
// Parse decrypted JSON message (AIBOT uses JSON format)
var msg WeComBotMessage
if err := json.Unmarshal([]byte(decryptedMsg), &msg); err != nil {
- logger.ErrorCF("wecom", "Failed to parse decrypted message", map[string]interface{}{
+ logger.ErrorCF("wecom", "Failed to parse decrypted message", map[string]any{
"error": err.Error(),
})
http.Error(w, "Invalid message format", http.StatusBadRequest)
@@ -311,8 +311,9 @@ func (c *WeComBotChannel) handleMessageCallback(ctx context.Context, w http.Resp
// processMessage processes the received message
func (c *WeComBotChannel) processMessage(ctx context.Context, msg WeComBotMessage) {
// Skip unsupported message types
- if msg.MsgType != "text" && msg.MsgType != "image" && msg.MsgType != "voice" && msg.MsgType != "file" && msg.MsgType != "mixed" {
- logger.DebugCF("wecom", "Skipping non-supported message type", map[string]interface{}{
+ if msg.MsgType != "text" && msg.MsgType != "image" && msg.MsgType != "voice" && msg.MsgType != "file" &&
+ msg.MsgType != "mixed" {
+ logger.DebugCF("wecom", "Skipping non-supported message type", map[string]any{
"msg_type": msg.MsgType,
})
return
@@ -323,7 +324,7 @@ func (c *WeComBotChannel) processMessage(ctx context.Context, msg WeComBotMessag
c.msgMu.Lock()
if c.processedMsgs[msgID] {
c.msgMu.Unlock()
- logger.DebugCF("wecom", "Skipping duplicate message", map[string]interface{}{
+ logger.DebugCF("wecom", "Skipping duplicate message", map[string]any{
"msg_id": msgID,
})
return
@@ -390,7 +391,7 @@ func (c *WeComBotChannel) processMessage(ctx context.Context, msg WeComBotMessag
metadata["sender_id"] = senderID
}
- logger.DebugCF("wecom", "Received message", map[string]interface{}{
+ logger.DebugCF("wecom", "Received message", map[string]any{
"sender_id": senderID,
"msg_type": msg.MsgType,
"peer_kind": peerKind,
@@ -459,7 +460,7 @@ func (c *WeComBotChannel) sendWebhookReply(ctx context.Context, userID, content
// handleHealth handles health check requests
func (c *WeComBotChannel) handleHealth(w http.ResponseWriter, r *http.Request) {
- status := map[string]interface{}{
+ status := map[string]any{
"status": "ok",
"running": c.IsRunning(),
}
diff --git a/pkg/channels/wecom/bot_test.go b/pkg/channels/wecom/bot_test.go
index 460e0058f..328b145c2 100644
--- a/pkg/channels/wecom/bot_test.go
+++ b/pkg/channels/wecom/bot_test.go
@@ -18,7 +18,6 @@ import (
"testing"
"github.com/sipeed/picoclaw/pkg/bus"
- "github.com/sipeed/picoclaw/pkg/channels"
"github.com/sipeed/picoclaw/pkg/config"
)
@@ -196,10 +195,8 @@ func TestWeComBotVerifySignature(t *testing.T) {
Token: "",
WebhookURL: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=test",
}
- base := channels.NewBaseChannel("wecom", cfgEmpty, msgBus, cfgEmpty.AllowFrom)
chEmpty := &WeComBotChannel{
- BaseChannel: base,
- config: cfgEmpty,
+ config: cfgEmpty,
}
if !verifySignature(chEmpty.config.Token, "any_sig", "any_ts", "any_nonce", "any_msg") {
@@ -356,7 +353,11 @@ func TestWeComBotHandleVerification(t *testing.T) {
nonce := "test_nonce"
signature := generateSignature("test_token", timestamp, nonce, encryptedEchostr)
- req := httptest.NewRequest(http.MethodGet, "/webhook/wecom?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce+"&echostr="+encryptedEchostr, nil)
+ req := httptest.NewRequest(
+ http.MethodGet,
+ "/webhook/wecom?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce+"&echostr="+encryptedEchostr,
+ nil,
+ )
w := httptest.NewRecorder()
ch.handleVerification(context.Background(), w, req)
@@ -386,7 +387,11 @@ func TestWeComBotHandleVerification(t *testing.T) {
timestamp := "1234567890"
nonce := "test_nonce"
- req := httptest.NewRequest(http.MethodGet, "/webhook/wecom?msg_signature=invalid_sig×tamp="+timestamp+"&nonce="+nonce+"&echostr="+encryptedEchostr, nil)
+ req := httptest.NewRequest(
+ http.MethodGet,
+ "/webhook/wecom?msg_signature=invalid_sig×tamp="+timestamp+"&nonce="+nonce+"&echostr="+encryptedEchostr,
+ nil,
+ )
w := httptest.NewRecorder()
ch.handleVerification(context.Background(), w, req)
@@ -410,14 +415,14 @@ func TestWeComBotHandleMessageCallback(t *testing.T) {
t.Run("valid direct message callback", func(t *testing.T) {
// Create JSON message for direct chat (single)
jsonMsg := `{
- "msgid": "test_msg_id_123",
- "aibotid": "test_aibot_id",
- "chattype": "single",
- "from": {"userid": "user123"},
- "response_url": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=test",
- "msgtype": "text",
- "text": {"content": "Hello World"}
- }`
+ "msgid": "test_msg_id_123",
+ "aibotid": "test_aibot_id",
+ "chattype": "single",
+ "from": {"userid": "user123"},
+ "response_url": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=test",
+ "msgtype": "text",
+ "text": {"content": "Hello World"}
+ }`
// Encrypt message
encrypted, _ := encryptTestMessage(jsonMsg, aesKey)
@@ -435,7 +440,11 @@ func TestWeComBotHandleMessageCallback(t *testing.T) {
nonce := "test_nonce"
signature := generateSignature("test_token", timestamp, nonce, encrypted)
- req := httptest.NewRequest(http.MethodPost, "/webhook/wecom?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce, bytes.NewReader(wrapperData))
+ req := httptest.NewRequest(
+ http.MethodPost,
+ "/webhook/wecom?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce,
+ bytes.NewReader(wrapperData),
+ )
w := httptest.NewRecorder()
ch.handleMessageCallback(context.Background(), w, req)
@@ -451,15 +460,15 @@ func TestWeComBotHandleMessageCallback(t *testing.T) {
t.Run("valid group message callback", func(t *testing.T) {
// Create JSON message for group chat
jsonMsg := `{
- "msgid": "test_msg_id_456",
- "aibotid": "test_aibot_id",
- "chatid": "group_chat_id_123",
- "chattype": "group",
- "from": {"userid": "user456"},
- "response_url": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=test",
- "msgtype": "text",
- "text": {"content": "Hello Group"}
- }`
+ "msgid": "test_msg_id_456",
+ "aibotid": "test_aibot_id",
+ "chatid": "group_chat_id_123",
+ "chattype": "group",
+ "from": {"userid": "user456"},
+ "response_url": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=test",
+ "msgtype": "text",
+ "text": {"content": "Hello Group"}
+ }`
// Encrypt message
encrypted, _ := encryptTestMessage(jsonMsg, aesKey)
@@ -477,7 +486,11 @@ func TestWeComBotHandleMessageCallback(t *testing.T) {
nonce := "test_nonce"
signature := generateSignature("test_token", timestamp, nonce, encrypted)
- req := httptest.NewRequest(http.MethodPost, "/webhook/wecom?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce, bytes.NewReader(wrapperData))
+ req := httptest.NewRequest(
+ http.MethodPost,
+ "/webhook/wecom?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce,
+ bytes.NewReader(wrapperData),
+ )
w := httptest.NewRecorder()
ch.handleMessageCallback(context.Background(), w, req)
@@ -506,7 +519,11 @@ func TestWeComBotHandleMessageCallback(t *testing.T) {
nonce := "test_nonce"
signature := generateSignature("test_token", timestamp, nonce, "")
- req := httptest.NewRequest(http.MethodPost, "/webhook/wecom?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce, strings.NewReader("invalid xml"))
+ req := httptest.NewRequest(
+ http.MethodPost,
+ "/webhook/wecom?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce,
+ strings.NewReader("invalid xml"),
+ )
w := httptest.NewRecorder()
ch.handleMessageCallback(context.Background(), w, req)
@@ -528,7 +545,11 @@ func TestWeComBotHandleMessageCallback(t *testing.T) {
timestamp := "1234567890"
nonce := "test_nonce"
- req := httptest.NewRequest(http.MethodPost, "/webhook/wecom?msg_signature=invalid_sig×tamp="+timestamp+"&nonce="+nonce, bytes.NewReader(wrapperData))
+ req := httptest.NewRequest(
+ http.MethodPost,
+ "/webhook/wecom?msg_signature=invalid_sig×tamp="+timestamp+"&nonce="+nonce,
+ bytes.NewReader(wrapperData),
+ )
w := httptest.NewRecorder()
ch.handleMessageCallback(context.Background(), w, req)
@@ -623,7 +644,11 @@ func TestWeComBotHandleWebhook(t *testing.T) {
nonce := "test_nonce"
signature := generateSignature("test_token", timestamp, nonce, encoded)
- req := httptest.NewRequest(http.MethodGet, "/webhook/wecom?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce+"&echostr="+encoded, nil)
+ req := httptest.NewRequest(
+ http.MethodGet,
+ "/webhook/wecom?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce+"&echostr="+encoded,
+ nil,
+ )
w := httptest.NewRecorder()
ch.handleWebhook(w, req)
@@ -646,7 +671,11 @@ func TestWeComBotHandleWebhook(t *testing.T) {
nonce := "test_nonce"
signature := generateSignature("test_token", timestamp, nonce, encryptedWrapper.Encrypt)
- req := httptest.NewRequest(http.MethodPost, "/webhook/wecom?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce, bytes.NewReader(wrapperData))
+ req := httptest.NewRequest(
+ http.MethodPost,
+ "/webhook/wecom?msg_signature="+signature+"×tamp="+timestamp+"&nonce="+nonce,
+ bytes.NewReader(wrapperData),
+ )
w := httptest.NewRecorder()
ch.handleWebhook(w, req)
@@ -713,15 +742,15 @@ func TestWeComBotReplyMessage(t *testing.T) {
func TestWeComBotMessageStructure(t *testing.T) {
jsonData := `{
- "msgid": "test_msg_id_123",
- "aibotid": "test_aibot_id",
- "chatid": "group_chat_id_123",
- "chattype": "group",
- "from": {"userid": "user123"},
- "response_url": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=test",
- "msgtype": "text",
- "text": {"content": "Hello World"}
- }`
+ "msgid": "test_msg_id_123",
+ "aibotid": "test_aibot_id",
+ "chatid": "group_chat_id_123",
+ "chattype": "group",
+ "from": {"userid": "user123"},
+ "response_url": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=test",
+ "msgtype": "text",
+ "text": {"content": "Hello World"}
+ }`
var msg WeComBotMessage
err := json.Unmarshal([]byte(jsonData), &msg)
diff --git a/pkg/channels/whatsapp/whatsapp.go b/pkg/channels/whatsapp/whatsapp.go
index 1ac256766..7e8f13ab6 100644
--- a/pkg/channels/whatsapp/whatsapp.go
+++ b/pkg/channels/whatsapp/whatsapp.go
@@ -87,7 +87,7 @@ func (c *WhatsAppChannel) Send(ctx context.Context, msg bus.OutboundMessage) err
return fmt.Errorf("whatsapp connection not established")
}
- payload := map[string]interface{}{
+ payload := map[string]any{
"type": "message",
"to": msg.ChatID,
"content": msg.Content,
@@ -127,7 +127,7 @@ func (c *WhatsAppChannel) listen(ctx context.Context) {
continue
}
- var msg map[string]interface{}
+ var msg map[string]any
if err := json.Unmarshal(message, &msg); err != nil {
log.Printf("Failed to unmarshal WhatsApp message: %v", err)
continue
@@ -145,7 +145,7 @@ func (c *WhatsAppChannel) listen(ctx context.Context) {
}
}
-func (c *WhatsAppChannel) handleIncomingMessage(msg map[string]interface{}) {
+func (c *WhatsAppChannel) handleIncomingMessage(msg map[string]any) {
senderID, ok := msg["from"].(string)
if !ok {
return
@@ -162,7 +162,7 @@ func (c *WhatsAppChannel) handleIncomingMessage(msg map[string]interface{}) {
}
var mediaPaths []string
- if mediaData, ok := msg["media"].([]interface{}); ok {
+ if mediaData, ok := msg["media"].([]any); ok {
mediaPaths = make([]string, 0, len(mediaData))
for _, m := range mediaData {
if path, ok := m.(string); ok {