feat(whatsapp): reuse shared dispatcher for text commands
This commit is contained in:
parent
9350e9d4c4
commit
414877da9d
4 changed files with 107 additions and 0 deletions
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/sipeed/picoclaw/pkg/bus"
|
"github.com/sipeed/picoclaw/pkg/bus"
|
||||||
"github.com/sipeed/picoclaw/pkg/channels"
|
"github.com/sipeed/picoclaw/pkg/channels"
|
||||||
|
"github.com/sipeed/picoclaw/pkg/commands"
|
||||||
"github.com/sipeed/picoclaw/pkg/config"
|
"github.com/sipeed/picoclaw/pkg/config"
|
||||||
"github.com/sipeed/picoclaw/pkg/identity"
|
"github.com/sipeed/picoclaw/pkg/identity"
|
||||||
"github.com/sipeed/picoclaw/pkg/logger"
|
"github.com/sipeed/picoclaw/pkg/logger"
|
||||||
|
|
@ -22,6 +23,7 @@ type WhatsAppChannel struct {
|
||||||
conn *websocket.Conn
|
conn *websocket.Conn
|
||||||
config config.WhatsAppConfig
|
config config.WhatsAppConfig
|
||||||
url string
|
url string
|
||||||
|
dispatcher commands.Dispatching
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
|
@ -42,6 +44,7 @@ func NewWhatsAppChannel(cfg config.WhatsAppConfig, bus *bus.MessageBus) (*WhatsA
|
||||||
BaseChannel: base,
|
BaseChannel: base,
|
||||||
config: cfg,
|
config: cfg,
|
||||||
url: cfg.BridgeURL,
|
url: cfg.BridgeURL,
|
||||||
|
dispatcher: commands.NewDispatcher(commands.NewRegistry(commands.BuiltinDefinitions(nil))),
|
||||||
connected: false,
|
connected: false,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -248,5 +251,34 @@ func (c *WhatsAppChannel) handleIncomingMessage(msg map[string]any) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.tryHandleCommand(c.ctx, content, chatID, senderID, messageID) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.HandleMessage(c.ctx, peer, messageID, senderID, chatID, content, mediaPaths, metadata, sender)
|
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 {
|
||||||
|
if c.dispatcher == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
res := c.dispatcher.Dispatch(ctx, commands.Request{
|
||||||
|
Channel: "whatsapp",
|
||||||
|
ChatID: chatID,
|
||||||
|
SenderID: senderID,
|
||||||
|
Text: text,
|
||||||
|
MessageID: messageID,
|
||||||
|
})
|
||||||
|
if res.Err != nil {
|
||||||
|
logger.WarnCF("whatsapp", "Command execution failed", map[string]any{
|
||||||
|
"command": res.Command,
|
||||||
|
"error": res.Err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.Matched
|
||||||
|
}
|
||||||
|
|
|
||||||
22
pkg/channels/whatsapp/whatsapp_command_test.go
Normal file
22
pkg/channels/whatsapp/whatsapp_command_test.go
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
package whatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/sipeed/picoclaw/pkg/commands"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTryHandleCommand_UsesDispatcher(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 := ch.tryHandleCommand(context.Background(), "/help", "chat1", "user1", "mid1")
|
||||||
|
if !handled || !called {
|
||||||
|
t.Fatalf("handled=%v called=%v", handled, called)
|
||||||
|
}
|
||||||
|
}
|
||||||
24
pkg/channels/whatsapp_native/whatsapp_command_test.go
Normal file
24
pkg/channels/whatsapp_native/whatsapp_command_test.go
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
//go:build whatsapp_native
|
||||||
|
|
||||||
|
package whatsapp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/sipeed/picoclaw/pkg/commands"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTryHandleCommand_UsesDispatcher(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 := ch.tryHandleCommand(context.Background(), "/help", "chat1", "user1", "mid1")
|
||||||
|
if !handled || !called {
|
||||||
|
t.Fatalf("handled=%v called=%v", handled, called)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -30,6 +30,7 @@ import (
|
||||||
|
|
||||||
"github.com/sipeed/picoclaw/pkg/bus"
|
"github.com/sipeed/picoclaw/pkg/bus"
|
||||||
"github.com/sipeed/picoclaw/pkg/channels"
|
"github.com/sipeed/picoclaw/pkg/channels"
|
||||||
|
"github.com/sipeed/picoclaw/pkg/commands"
|
||||||
"github.com/sipeed/picoclaw/pkg/config"
|
"github.com/sipeed/picoclaw/pkg/config"
|
||||||
"github.com/sipeed/picoclaw/pkg/identity"
|
"github.com/sipeed/picoclaw/pkg/identity"
|
||||||
"github.com/sipeed/picoclaw/pkg/logger"
|
"github.com/sipeed/picoclaw/pkg/logger"
|
||||||
|
|
@ -55,6 +56,7 @@ type WhatsAppNativeChannel struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
runCtx context.Context
|
runCtx context.Context
|
||||||
runCancel context.CancelFunc
|
runCancel context.CancelFunc
|
||||||
|
dispatcher commands.Dispatching
|
||||||
reconnectMu sync.Mutex
|
reconnectMu sync.Mutex
|
||||||
reconnecting bool
|
reconnecting bool
|
||||||
stopping atomic.Bool // set once Stop begins; prevents new wg.Add calls
|
stopping atomic.Bool // set once Stop begins; prevents new wg.Add calls
|
||||||
|
|
@ -76,6 +78,7 @@ func NewWhatsAppNativeChannel(
|
||||||
BaseChannel: base,
|
BaseChannel: base,
|
||||||
config: cfg,
|
config: cfg,
|
||||||
storePath: storePath,
|
storePath: storePath,
|
||||||
|
dispatcher: commands.NewDispatcher(commands.NewRegistry(commands.BuiltinDefinitions(nil))),
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
@ -387,6 +390,9 @@ func (c *WhatsAppNativeChannel) handleIncoming(evt *events.Message) {
|
||||||
if !c.IsAllowedSender(sender) {
|
if !c.IsAllowedSender(sender) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if c.tryHandleCommand(c.runCtx, content, chatID, senderID, messageID) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
logger.DebugCF(
|
logger.DebugCF(
|
||||||
"whatsapp",
|
"whatsapp",
|
||||||
|
|
@ -396,6 +402,29 @@ func (c *WhatsAppNativeChannel) handleIncoming(evt *events.Message) {
|
||||||
c.HandleMessage(c.runCtx, peer, messageID, senderID, chatID, content, mediaPaths, metadata, sender)
|
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 {
|
||||||
|
if c.dispatcher == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
res := c.dispatcher.Dispatch(ctx, commands.Request{
|
||||||
|
Channel: "whatsapp_native",
|
||||||
|
ChatID: chatID,
|
||||||
|
SenderID: senderID,
|
||||||
|
Text: text,
|
||||||
|
MessageID: messageID,
|
||||||
|
})
|
||||||
|
if res.Err != nil {
|
||||||
|
logger.WarnCF("whatsapp", "Command execution failed", map[string]any{
|
||||||
|
"command": res.Command,
|
||||||
|
"error": res.Err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return res.Matched
|
||||||
|
}
|
||||||
|
|
||||||
func (c *WhatsAppNativeChannel) Send(ctx context.Context, msg bus.OutboundMessage) error {
|
func (c *WhatsAppNativeChannel) Send(ctx context.Context, msg bus.OutboundMessage) error {
|
||||||
if !c.IsRunning() {
|
if !c.IsRunning() {
|
||||||
return channels.ErrNotRunning
|
return channels.ErrNotRunning
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue