From 08a9d99cd84fd0cbe54a13a8a74b246cb08ce232 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 23 Feb 2026 20:48:01 +0800 Subject: [PATCH] improve channel allowlist and feishu send diagnostics --- pkg/channels/base.go | 7 +++++++ pkg/channels/base_test.go | 39 ++++++++++++++++++++++++++++++++++++++- pkg/channels/feishu_64.go | 9 +++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/pkg/channels/base.go b/pkg/channels/base.go index cd6419ebb..84855de0d 100644 --- a/pkg/channels/base.go +++ b/pkg/channels/base.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/sipeed/picoclaw/pkg/bus" + "github.com/sipeed/picoclaw/pkg/logger" ) type Channel interface { @@ -83,6 +84,12 @@ func (c *BaseChannel) IsAllowed(senderID string) bool { func (c *BaseChannel) HandleMessage(senderID, chatID, content string, media []string, metadata map[string]string) { if !c.IsAllowed(senderID) { + logger.WarnCF("channels", "Inbound message blocked by allow_from", map[string]any{ + "channel": c.name, + "sender_id": senderID, + "chat_id": chatID, + "allow_from_count": len(c.allowList), + }) return } diff --git a/pkg/channels/base_test.go b/pkg/channels/base_test.go index 78c6d1d66..2cdc8a9c9 100644 --- a/pkg/channels/base_test.go +++ b/pkg/channels/base_test.go @@ -1,6 +1,12 @@ package channels -import "testing" +import ( + "context" + "testing" + "time" + + "github.com/sipeed/picoclaw/pkg/bus" +) func TestBaseChannelIsAllowed(t *testing.T) { tests := []struct { @@ -50,3 +56,34 @@ func TestBaseChannelIsAllowed(t *testing.T) { }) } } + +func TestBaseChannelHandleMessageAllowList(t *testing.T) { + msgBus := bus.NewMessageBus() + ch := NewBaseChannel("test", nil, msgBus, []string{"allowed"}) + + ch.HandleMessage("blocked", "chat-1", "denied", nil, nil) + + deniedCtx, deniedCancel := context.WithTimeout(context.Background(), 20*time.Millisecond) + defer deniedCancel() + if msg, ok := msgBus.ConsumeInbound(deniedCtx); ok { + t.Fatalf("expected denied sender to be dropped, got message: %+v", msg) + } + + ch.HandleMessage("allowed", "chat-1", "accepted", []string{"m1"}, map[string]string{"k": "v"}) + + allowedCtx, allowedCancel := context.WithTimeout(context.Background(), time.Second) + defer allowedCancel() + msg, ok := msgBus.ConsumeInbound(allowedCtx) + if !ok { + t.Fatal("expected allowed sender message to be published") + } + if msg.Channel != "test" || msg.SenderID != "allowed" || msg.ChatID != "chat-1" || msg.Content != "accepted" { + t.Fatalf("unexpected inbound message: %+v", msg) + } + if len(msg.Media) != 1 || msg.Media[0] != "m1" { + t.Fatalf("unexpected media payload: %+v", msg.Media) + } + if msg.Metadata["k"] != "v" { + t.Fatalf("unexpected metadata: %+v", msg.Metadata) + } +} diff --git a/pkg/channels/feishu_64.go b/pkg/channels/feishu_64.go index 1eb9310d0..b6dcd375e 100644 --- a/pkg/channels/feishu_64.go +++ b/pkg/channels/feishu_64.go @@ -121,6 +121,15 @@ func (c *FeishuChannel) Send(ctx context.Context, msg bus.OutboundMessage) error } if !resp.Success() { + fields := map[string]any{ + "chat_id": msg.ChatID, + "code": resp.Code, + "msg": resp.Msg, + } + if resp.Code == 99991672 { + fields["hint"] = "missing app scopes, enable im:message:send_as_bot (or equivalent) and publish the app" + } + logger.ErrorCF("feishu", "Feishu message send rejected", fields) return fmt.Errorf("feishu api error: code=%d msg=%s", resp.Code, resp.Msg) }