From 9c610bc037469eff53dda665535aea8391b377ab Mon Sep 17 00:00:00 2001 From: mingmxren Date: Sun, 1 Mar 2026 17:49:46 +0800 Subject: [PATCH] refactor(channels): remove dead command parser remnants --- pkg/agent/loop_test.go | 2 + pkg/channels/interfaces.go | 7 - pkg/channels/interfaces_command_test.go | 10 -- pkg/channels/telegram/telegram.go | 19 +-- pkg/channels/telegram/telegram_commands.go | 154 ------------------ pkg/channels/telegram/telegram_dispatch.go | 22 --- .../telegram/telegram_dispatch_test.go | 26 --- pkg/channels/whatsapp/whatsapp.go | 37 +---- .../whatsapp/whatsapp_command_test.go | 29 +--- .../whatsapp_native/whatsapp_command_test.go | 29 +--- .../whatsapp_native/whatsapp_native.go | 22 --- 11 files changed, 17 insertions(+), 340 deletions(-) delete mode 100644 pkg/channels/telegram/telegram_commands.go delete mode 100644 pkg/channels/telegram/telegram_dispatch.go diff --git a/pkg/agent/loop_test.go b/pkg/agent/loop_test.go index 876adb15e..2c8fe90e5 100644 --- a/pkg/agent/loop_test.go +++ b/pkg/agent/loop_test.go @@ -5,6 +5,8 @@ import ( "fmt" "os" "path/filepath" + "slices" + "strings" "testing" "time" diff --git a/pkg/channels/interfaces.go b/pkg/channels/interfaces.go index 2bb4da3e0..42f18405c 100644 --- a/pkg/channels/interfaces.go +++ b/pkg/channels/interfaces.go @@ -48,10 +48,3 @@ type PlaceholderRecorder interface { type CommandRegistrarCapable interface { RegisterCommands(ctx context.Context, defs []commands.Definition) error } - -// CommandParserCapable is implemented by channels that expose a command -// dispatch entrypoint backed by shared command definitions/dispatcher. -// It is optional and intended for cross-channel command handling features. -type CommandParserCapable interface { - DispatchCommand(ctx context.Context, req commands.Request) commands.Result -} diff --git a/pkg/channels/interfaces_command_test.go b/pkg/channels/interfaces_command_test.go index 947f6b5c1..de5502644 100644 --- a/pkg/channels/interfaces_command_test.go +++ b/pkg/channels/interfaces_command_test.go @@ -11,16 +11,6 @@ type mockRegistrar struct{} func (mockRegistrar) RegisterCommands(context.Context, []commands.Definition) error { return nil } -type mockParser struct{} - -func (mockParser) DispatchCommand(context.Context, commands.Request) commands.Result { - return commands.Result{Matched: false} -} - func TestCommandRegistrarCapable_Compiles(t *testing.T) { var _ CommandRegistrarCapable = mockRegistrar{} } - -func TestCommandParserCapable_Compiles(t *testing.T) { - var _ CommandParserCapable = mockParser{} -} diff --git a/pkg/channels/telegram/telegram.go b/pkg/channels/telegram/telegram.go index cfcde789e..e1fabf913 100644 --- a/pkg/channels/telegram/telegram.go +++ b/pkg/channels/telegram/telegram.go @@ -41,14 +41,12 @@ var ( type TelegramChannel struct { *channels.BaseChannel - bot *telego.Bot - bh *telegohandler.BotHandler - commands TelegramCommander - dispatcher commands.Dispatching - config *config.Config - chatIDs map[string]int64 - ctx context.Context - cancel context.CancelFunc + bot *telego.Bot + bh *telegohandler.BotHandler + config *config.Config + chatIDs map[string]int64 + ctx context.Context + cancel context.CancelFunc registerFunc func(context.Context, []commands.Definition) error commandRegCancel context.CancelFunc @@ -94,8 +92,6 @@ func NewTelegramChannel(cfg *config.Config, bus *bus.MessageBus) (*TelegramChann return &TelegramChannel{ BaseChannel: base, - commands: NewTelegramCommands(bot, cfg), - dispatcher: commands.NewDispatcher(commands.NewRegistry(commands.BuiltinDefinitions(cfg))), bot: bot, config: cfg, chatIDs: make(map[string]int64), @@ -123,9 +119,6 @@ func (c *TelegramChannel) Start(ctx context.Context) error { c.bh = bh bh.HandleMessage(func(ctx *th.Context, message telego.Message) error { - if c.dispatchCommand(ctx, message) { - return nil - } return c.handleMessage(ctx, &message) }, th.AnyMessage()) diff --git a/pkg/channels/telegram/telegram_commands.go b/pkg/channels/telegram/telegram_commands.go deleted file mode 100644 index cd24fa9e9..000000000 --- a/pkg/channels/telegram/telegram_commands.go +++ /dev/null @@ -1,154 +0,0 @@ -package telegram - -import ( - "context" - "fmt" - "strings" - - "github.com/mymmrac/telego" - - "github.com/sipeed/picoclaw/pkg/commands" - "github.com/sipeed/picoclaw/pkg/config" -) - -type TelegramCommander interface { - Help(ctx context.Context, message telego.Message) error - Start(ctx context.Context, message telego.Message) error - Show(ctx context.Context, message telego.Message) error - List(ctx context.Context, message telego.Message) error -} - -type cmd struct { - bot *telego.Bot - config *config.Config -} - -func NewTelegramCommands(bot *telego.Bot, cfg *config.Config) TelegramCommander { - return &cmd{ - bot: bot, - config: cfg, - } -} - -func commandArgs(text string) string { - parts := strings.SplitN(text, " ", 2) - if len(parts) < 2 { - return "" - } - return strings.TrimSpace(parts[1]) -} - -func (c *cmd) Help(ctx context.Context, message telego.Message) error { - defs := commands.NewRegistry(commands.BuiltinDefinitions(c.config)).ForChannel("telegram") - msg := commands.FormatHelpMessage(defs) - _, err := c.bot.SendMessage(ctx, &telego.SendMessageParams{ - ChatID: telego.ChatID{ID: message.Chat.ID}, - Text: msg, - ReplyParameters: &telego.ReplyParameters{ - MessageID: message.MessageID, - }, - }) - return err -} - -func (c *cmd) Start(ctx context.Context, message telego.Message) error { - _, err := c.bot.SendMessage(ctx, &telego.SendMessageParams{ - ChatID: telego.ChatID{ID: message.Chat.ID}, - Text: "Hello! I am PicoClaw 🦞", - ReplyParameters: &telego.ReplyParameters{ - MessageID: message.MessageID, - }, - }) - return err -} - -func (c *cmd) Show(ctx context.Context, message telego.Message) error { - args := commandArgs(message.Text) - if args == "" { - _, err := c.bot.SendMessage(ctx, &telego.SendMessageParams{ - ChatID: telego.ChatID{ID: message.Chat.ID}, - Text: "Usage: /show [model|channel]", - ReplyParameters: &telego.ReplyParameters{ - MessageID: message.MessageID, - }, - }) - return err - } - - var response string - switch args { - case "model": - response = fmt.Sprintf("Current Model: %s (Provider: %s)", - c.config.Agents.Defaults.GetModelName(), - c.config.Agents.Defaults.Provider) - case "channel": - response = "Current Channel: telegram" - default: - response = fmt.Sprintf("Unknown parameter: %s. Try 'model' or 'channel'.", args) - } - - _, err := c.bot.SendMessage(ctx, &telego.SendMessageParams{ - ChatID: telego.ChatID{ID: message.Chat.ID}, - Text: response, - ReplyParameters: &telego.ReplyParameters{ - MessageID: message.MessageID, - }, - }) - return err -} - -func (c *cmd) List(ctx context.Context, message telego.Message) error { - args := commandArgs(message.Text) - if args == "" { - _, err := c.bot.SendMessage(ctx, &telego.SendMessageParams{ - ChatID: telego.ChatID{ID: message.Chat.ID}, - Text: "Usage: /list [models|channels]", - ReplyParameters: &telego.ReplyParameters{ - MessageID: message.MessageID, - }, - }) - return err - } - - var response string - switch args { - case "models": - provider := c.config.Agents.Defaults.Provider - if provider == "" { - provider = "configured default" - } - response = fmt.Sprintf("Configured Model: %s\nProvider: %s\n\nTo change models, update config.json", - c.config.Agents.Defaults.GetModelName(), provider) - - case "channels": - var enabled []string - if c.config.Channels.Telegram.Enabled { - enabled = append(enabled, "telegram") - } - if c.config.Channels.WhatsApp.Enabled { - enabled = append(enabled, "whatsapp") - } - if c.config.Channels.Feishu.Enabled { - enabled = append(enabled, "feishu") - } - if c.config.Channels.Discord.Enabled { - enabled = append(enabled, "discord") - } - if c.config.Channels.Slack.Enabled { - enabled = append(enabled, "slack") - } - response = fmt.Sprintf("Enabled Channels:\n- %s", strings.Join(enabled, "\n- ")) - - default: - response = fmt.Sprintf("Unknown parameter: %s. Try 'models' or 'channels'.", args) - } - - _, err := c.bot.SendMessage(ctx, &telego.SendMessageParams{ - ChatID: telego.ChatID{ID: message.Chat.ID}, - Text: response, - ReplyParameters: &telego.ReplyParameters{ - MessageID: message.MessageID, - }, - }) - return err -} diff --git a/pkg/channels/telegram/telegram_dispatch.go b/pkg/channels/telegram/telegram_dispatch.go deleted file mode 100644 index 1bb79c467..000000000 --- a/pkg/channels/telegram/telegram_dispatch.go +++ /dev/null @@ -1,22 +0,0 @@ -package telegram - -import ( - "context" - - "github.com/mymmrac/telego" - - "github.com/sipeed/picoclaw/pkg/commands" -) - -func (c *TelegramChannel) DispatchCommand(ctx context.Context, req commands.Request) commands.Result { - if c.dispatcher == nil { - return commands.Result{Matched: false} - } - return c.dispatcher.Dispatch(ctx, req) -} - -func (c *TelegramChannel) dispatchCommand(ctx context.Context, message telego.Message) bool { - // Generic slash commands are now executed in the agent-centric command path. - // Channel adapters must not consume them locally. - return false -} diff --git a/pkg/channels/telegram/telegram_dispatch_test.go b/pkg/channels/telegram/telegram_dispatch_test.go index d63782eb3..1ea4a4824 100644 --- a/pkg/channels/telegram/telegram_dispatch_test.go +++ b/pkg/channels/telegram/telegram_dispatch_test.go @@ -9,34 +9,8 @@ import ( "github.com/sipeed/picoclaw/pkg/bus" "github.com/sipeed/picoclaw/pkg/channels" - "github.com/sipeed/picoclaw/pkg/commands" ) -func TestDispatchCommand_DoesNotConsumeGenericCommandsLocally(t *testing.T) { - ch := &TelegramChannel{} - called := false - ch.dispatcher = commands.DispatchFunc(func(context.Context, commands.Request) commands.Result { - called = true - return commands.Result{Matched: true, Command: "noop"} - }) - - msg := telego.Message{ - Text: "/help", - MessageID: 7, - Chat: telego.Chat{ - ID: 123, - }, - } - - handled := ch.dispatchCommand(context.Background(), msg) - if handled { - t.Fatalf("handled=%v", handled) - } - if called { - t.Fatalf("handled=%v called=%v", handled, called) - } -} - func TestHandleMessage_DoesNotConsumeGenericCommandsLocally(t *testing.T) { messageBus := bus.NewMessageBus() ch := &TelegramChannel{ diff --git a/pkg/channels/whatsapp/whatsapp.go b/pkg/channels/whatsapp/whatsapp.go index 48adde335..70b3e02bf 100644 --- a/pkg/channels/whatsapp/whatsapp.go +++ b/pkg/channels/whatsapp/whatsapp.go @@ -11,7 +11,6 @@ import ( "github.com/sipeed/picoclaw/pkg/bus" "github.com/sipeed/picoclaw/pkg/channels" - "github.com/sipeed/picoclaw/pkg/commands" "github.com/sipeed/picoclaw/pkg/config" "github.com/sipeed/picoclaw/pkg/identity" "github.com/sipeed/picoclaw/pkg/logger" @@ -20,14 +19,13 @@ import ( type WhatsAppChannel struct { *channels.BaseChannel - conn *websocket.Conn - config config.WhatsAppConfig - url string - dispatcher commands.Dispatching - ctx context.Context - cancel context.CancelFunc - mu sync.Mutex - connected bool + conn *websocket.Conn + config config.WhatsAppConfig + url string + ctx context.Context + cancel context.CancelFunc + mu sync.Mutex + connected bool } func NewWhatsAppChannel(cfg config.WhatsAppConfig, bus *bus.MessageBus) (*WhatsAppChannel, error) { @@ -44,7 +42,6 @@ func NewWhatsAppChannel(cfg config.WhatsAppConfig, bus *bus.MessageBus) (*WhatsA BaseChannel: base, config: cfg, url: cfg.BridgeURL, - dispatcher: commands.NewDispatcher(commands.NewRegistry(commands.BuiltinDefinitions(nil))), connected: false, }, nil } @@ -251,25 +248,5 @@ func (c *WhatsAppChannel) handleIncomingMessage(msg map[string]any) { return } - if c.tryHandleCommand(c.ctx, content, chatID, senderID, messageID) { - return - } - c.HandleMessage(c.ctx, peer, messageID, senderID, chatID, content, mediaPaths, metadata, sender) } - -func (c *WhatsAppChannel) tryHandleCommand( - ctx context.Context, - text, chatID, senderID, messageID string, -) bool { - // Generic slash commands are now executed in the agent-centric command path. - // Channel adapters must not consume them locally. - return false -} - -func (c *WhatsAppChannel) DispatchCommand(ctx context.Context, req commands.Request) commands.Result { - if c.dispatcher == nil { - return commands.Result{Matched: false} - } - return c.dispatcher.Dispatch(ctx, req) -} diff --git a/pkg/channels/whatsapp/whatsapp_command_test.go b/pkg/channels/whatsapp/whatsapp_command_test.go index 69dbd4595..ee8aa4a52 100644 --- a/pkg/channels/whatsapp/whatsapp_command_test.go +++ b/pkg/channels/whatsapp/whatsapp_command_test.go @@ -7,37 +7,14 @@ import ( "github.com/sipeed/picoclaw/pkg/bus" "github.com/sipeed/picoclaw/pkg/channels" - "github.com/sipeed/picoclaw/pkg/commands" "github.com/sipeed/picoclaw/pkg/config" ) -func TestTryHandleCommand_DoesNotConsumeGenericCommandsLocally(t *testing.T) { - ch := &WhatsAppChannel{} - called := false - ch.dispatcher = commands.DispatchFunc(func(context.Context, commands.Request) commands.Result { - called = true - return commands.Result{Matched: true, Handled: true} - }) - - handled := ch.tryHandleCommand(context.Background(), "/help", "chat1", "user1", "mid1") - if handled { - t.Fatalf("handled=%v", handled) - } - if called { - t.Fatalf("handled=%v called=%v", handled, called) - } -} - func TestHandleIncomingMessage_DoesNotConsumeGenericCommandsLocally(t *testing.T) { messageBus := bus.NewMessageBus() - called := false ch := &WhatsAppChannel{ BaseChannel: channels.NewBaseChannel("whatsapp", config.WhatsAppConfig{}, messageBus, nil), - dispatcher: commands.DispatchFunc(func(context.Context, commands.Request) commands.Result { - called = true - return commands.Result{Matched: true, Handled: true} - }), - ctx: context.Background(), + ctx: context.Background(), } ch.handleIncomingMessage(map[string]any{ @@ -48,10 +25,6 @@ func TestHandleIncomingMessage_DoesNotConsumeGenericCommandsLocally(t *testing.T "content": "/help", }) - if called { - t.Fatal("expected generic command dispatch to be bypassed") - } - ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() diff --git a/pkg/channels/whatsapp_native/whatsapp_command_test.go b/pkg/channels/whatsapp_native/whatsapp_command_test.go index 5ff5ec4ca..cc2dcb619 100644 --- a/pkg/channels/whatsapp_native/whatsapp_command_test.go +++ b/pkg/channels/whatsapp_native/whatsapp_command_test.go @@ -14,37 +14,14 @@ import ( "github.com/sipeed/picoclaw/pkg/bus" "github.com/sipeed/picoclaw/pkg/channels" - "github.com/sipeed/picoclaw/pkg/commands" "github.com/sipeed/picoclaw/pkg/config" ) -func TestTryHandleCommand_DoesNotConsumeGenericCommandsLocally(t *testing.T) { - ch := &WhatsAppNativeChannel{} - called := false - ch.dispatcher = commands.DispatchFunc(func(context.Context, commands.Request) commands.Result { - called = true - return commands.Result{Matched: true, Handled: true} - }) - - handled := ch.tryHandleCommand(context.Background(), "/help", "chat1", "user1", "mid1") - if handled { - t.Fatalf("handled=%v", handled) - } - if called { - t.Fatalf("handled=%v called=%v", handled, called) - } -} - func TestHandleIncoming_DoesNotConsumeGenericCommandsLocally(t *testing.T) { messageBus := bus.NewMessageBus() - called := false ch := &WhatsAppNativeChannel{ BaseChannel: channels.NewBaseChannel("whatsapp_native", config.WhatsAppConfig{}, messageBus, nil), - dispatcher: commands.DispatchFunc(func(context.Context, commands.Request) commands.Result { - called = true - return commands.Result{Matched: true, Handled: true} - }), - runCtx: context.Background(), + runCtx: context.Background(), } evt := &events.Message{ @@ -63,10 +40,6 @@ func TestHandleIncoming_DoesNotConsumeGenericCommandsLocally(t *testing.T) { ch.handleIncoming(evt) - if called { - t.Fatal("expected generic command dispatch to be bypassed") - } - ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() diff --git a/pkg/channels/whatsapp_native/whatsapp_native.go b/pkg/channels/whatsapp_native/whatsapp_native.go index f801f7e07..188a7c8fa 100644 --- a/pkg/channels/whatsapp_native/whatsapp_native.go +++ b/pkg/channels/whatsapp_native/whatsapp_native.go @@ -30,7 +30,6 @@ import ( "github.com/sipeed/picoclaw/pkg/bus" "github.com/sipeed/picoclaw/pkg/channels" - "github.com/sipeed/picoclaw/pkg/commands" "github.com/sipeed/picoclaw/pkg/config" "github.com/sipeed/picoclaw/pkg/identity" "github.com/sipeed/picoclaw/pkg/logger" @@ -56,7 +55,6 @@ type WhatsAppNativeChannel struct { mu sync.Mutex runCtx context.Context runCancel context.CancelFunc - dispatcher commands.Dispatching reconnectMu sync.Mutex reconnecting bool stopping atomic.Bool // set once Stop begins; prevents new wg.Add calls @@ -78,7 +76,6 @@ func NewWhatsAppNativeChannel( BaseChannel: base, config: cfg, storePath: storePath, - dispatcher: commands.NewDispatcher(commands.NewRegistry(commands.BuiltinDefinitions(nil))), } return c, nil } @@ -390,9 +387,6 @@ func (c *WhatsAppNativeChannel) handleIncoming(evt *events.Message) { if !c.IsAllowedSender(sender) { return } - if c.tryHandleCommand(c.runCtx, content, chatID, senderID, messageID) { - return - } logger.DebugCF( "whatsapp", @@ -402,22 +396,6 @@ func (c *WhatsAppNativeChannel) handleIncoming(evt *events.Message) { c.HandleMessage(c.runCtx, peer, messageID, senderID, chatID, content, mediaPaths, metadata, sender) } -func (c *WhatsAppNativeChannel) tryHandleCommand( - ctx context.Context, - text, chatID, senderID, messageID string, -) bool { - // Generic slash commands are now executed in the agent-centric command path. - // Channel adapters must not consume them locally. - return false -} - -func (c *WhatsAppNativeChannel) DispatchCommand(ctx context.Context, req commands.Request) commands.Result { - if c.dispatcher == nil { - return commands.Result{Matched: false} - } - return c.dispatcher.Dispatch(ctx, req) -} - func (c *WhatsAppNativeChannel) Send(ctx context.Context, msg bus.OutboundMessage) error { if !c.IsRunning() { return channels.ErrNotRunning