feat(commands): add dispatcher and request/result contracts

This commit is contained in:
mingmxren 2026-03-01 02:26:11 +08:00
parent 885dc310e3
commit 6975ab1945
3 changed files with 88 additions and 0 deletions

View file

@ -6,4 +6,5 @@ type Definition struct {
Usage string
Aliases []string
Channels []string
Handler Handler
}

View file

@ -0,0 +1,59 @@
package commands
import (
"context"
"strings"
)
type Handler func(ctx context.Context, req Request) error
type Request struct {
Channel string
ChatID string
SenderID string
Text string
MessageID string
}
type Result struct {
Matched bool
Command string
Err error
}
type Dispatcher struct {
reg *Registry
}
func NewDispatcher(reg *Registry) *Dispatcher {
return &Dispatcher{reg: reg}
}
func (d *Dispatcher) Dispatch(ctx context.Context, req Request) Result {
token := firstToken(req.Text)
if token == "" {
return Result{Matched: false}
}
cmdName := strings.TrimPrefix(token, "/")
for _, def := range d.reg.ForChannel(req.Channel) {
if def.Name != cmdName {
continue
}
if def.Handler == nil {
return Result{Matched: true, Command: def.Name}
}
err := def.Handler(ctx, req)
return Result{Matched: true, Command: def.Name, Err: err}
}
return Result{Matched: false}
}
func firstToken(input string) string {
parts := strings.Fields(strings.TrimSpace(input))
if len(parts) == 0 {
return ""
}
return parts[0]
}

View file

@ -0,0 +1,28 @@
package commands
import (
"context"
"testing"
)
func TestDispatcher_MatchSlashCommand(t *testing.T) {
called := false
defs := []Definition{
{
Name: "help",
Handler: func(context.Context, Request) error {
called = true
return nil
},
},
}
d := NewDispatcher(NewRegistry(defs))
res := d.Dispatch(context.Background(), Request{
Channel: "telegram",
Text: "/help",
})
if !res.Matched || !called || res.Err != nil {
t.Fatalf("dispatch result = %+v, called=%v", res, called)
}
}