177 lines
3.5 KiB
Go
177 lines
3.5 KiB
Go
package tg
|
|
|
|
import (
|
|
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
|
)
|
|
|
|
|
|
type CommandType uint8
|
|
const (
|
|
PrivateCommandType CommandType = iota
|
|
GroupCommandType
|
|
ChannelCommandType
|
|
)
|
|
|
|
type CommandName string
|
|
type Command struct {
|
|
Name CommandName
|
|
Type CommandType
|
|
Description string
|
|
Action Action
|
|
Widget Widget
|
|
WidgetArg any
|
|
}
|
|
|
|
type CommandMap map[CommandName]Command
|
|
|
|
func NewCommand(name CommandName, desc string) Command {
|
|
if name == "" || desc == "" {
|
|
panic("name and description cannot be an empty string")
|
|
}
|
|
return Command{
|
|
Name: name,
|
|
Description: desc,
|
|
}
|
|
}
|
|
|
|
func (c Command) WithAction(a Action) Command {
|
|
c.Action = a
|
|
return c
|
|
}
|
|
|
|
func (c Command) WithWidget(w Widget) Command {
|
|
c.Widget = w
|
|
return c
|
|
}
|
|
|
|
// Convert command into the tgbotapi.BotCommand
|
|
func (c Command) ToAPI() tgbotapi.BotCommand {
|
|
ret := tgbotapi.BotCommand{}
|
|
ret.Command = string(c.Name)
|
|
ret.Description = c.Description
|
|
return ret
|
|
}
|
|
|
|
// Simple command to go to another screen.
|
|
func (c Command) Go(pth Widget) Command {
|
|
return c.WithAction(WidgetGo{
|
|
Path: pth,
|
|
})
|
|
}
|
|
|
|
func (c Command) GoWithArg(pth Widget, arg any) Command {
|
|
return c.WithAction(WidgetGo{
|
|
Path: pth,
|
|
Arg: arg,
|
|
})
|
|
}
|
|
|
|
// The type is used to recognize commands and execute
|
|
// its actions and widgets .
|
|
type CommandCompo struct {
|
|
PreStart Action
|
|
Commands CommandMap
|
|
Usage Action
|
|
}
|
|
|
|
// Returns new empty CommandCompo.
|
|
func NewCommandCompo(cmds ...Command) *CommandCompo {
|
|
ret := (&CommandCompo{}).SetCommands(cmds...)
|
|
return ret
|
|
}
|
|
|
|
// Set the commands to handle.
|
|
func (w *CommandCompo) SetCommands(cmds ...Command) *CommandCompo {
|
|
if w.Commands == nil {
|
|
w.Commands = make(CommandMap)
|
|
}
|
|
for _, cmd := range cmds {
|
|
if cmd.Name == "" {
|
|
panic("empty command name")
|
|
}
|
|
_, ok := w.Commands[cmd.Name]
|
|
if ok {
|
|
panic("duplicate command definition")
|
|
}
|
|
w.Commands[cmd.Name] = cmd
|
|
}
|
|
return w
|
|
}
|
|
|
|
// Set the prestart action.
|
|
func (w *CommandCompo) SetPreStart(a Action) *CommandCompo {
|
|
w.PreStart = a
|
|
return w
|
|
}
|
|
|
|
// Set the usage action.
|
|
func (w *CommandCompo) SetUsage(a Action) *CommandCompo {
|
|
w.Usage = a
|
|
return w
|
|
}
|
|
|
|
// Filtering all the non commands.
|
|
func (widget *CommandCompo) Filter(
|
|
u Update,
|
|
) bool {
|
|
if u.Message == nil || !u.Message.IsCommand() {
|
|
return false
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Implementing server.
|
|
func (compo *CommandCompo) Serve(c Context) {
|
|
// First should bring the new command into the action.
|
|
c.Bot().DeleteCommands()
|
|
err := c.Bot().SetCommands(
|
|
tgbotapi.NewBotCommandScopeChat(c.SessionID().ToAPI()),
|
|
compo.Commands,
|
|
)
|
|
|
|
if err != nil {
|
|
c.Sendf("error: %q", err)
|
|
}
|
|
|
|
var cmdUpdates *UpdateChan
|
|
for u := range c.Input() {
|
|
if c.Path() == nil && u.Message != nil {
|
|
// Skipping and executing the preinit action
|
|
// while we have the empty screen.
|
|
// E. g. the session did not start.
|
|
if !u.Message.IsCommand() ||
|
|
u.Message.Command() != "start" {
|
|
c.WithUpdate(u).Run(compo.PreStart)
|
|
continue
|
|
}
|
|
}
|
|
|
|
if u.Message != nil && u.Message.IsCommand() {
|
|
// Command handling.
|
|
cmdName := CommandName(u.Message.Command())
|
|
cmd, ok := compo.Commands[cmdName]
|
|
if !ok {
|
|
c.WithUpdate(u).Run(compo.Usage)
|
|
continue
|
|
}
|
|
|
|
c.WithUpdate(u).Run(cmd.Action)
|
|
if cmd.Widget != nil {
|
|
// Closing current widget
|
|
cmdUpdates.Close()
|
|
// And running the other one.
|
|
cmdUpdates, _ = c.runWidget(cmd.Widget, cmd.WidgetArg)
|
|
}
|
|
continue
|
|
}
|
|
|
|
if !cmdUpdates.Closed() {
|
|
// Send to the commands channel if we are
|
|
// executing one.
|
|
cmdUpdates.Send(u)
|
|
} else {
|
|
c.SkipUpdate(u)
|
|
}
|
|
}
|
|
}
|