command.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package tg
  2. import (
  3. tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
  4. )
  5. type CommandType uint8
  6. const (
  7. PrivateCommandType CommandType = iota
  8. GroupCommandType
  9. ChannelCommandType
  10. )
  11. type CommandName string
  12. type Command struct {
  13. Name CommandName
  14. Type CommandType
  15. Description string
  16. Action Action
  17. Widget Widget
  18. }
  19. type CommandMap map[CommandName]*Command
  20. func NewCommand(name CommandName, desc string) Command {
  21. if name == "" || desc == "" {
  22. panic("name and description cannot be an empty string")
  23. }
  24. return &Command{
  25. Name: name,
  26. Description: desc,
  27. }
  28. }
  29. func (c *Command) WithAction(a Action) *Command {
  30. c.Action = a
  31. return c
  32. }
  33. func (c Command) WithWidget(w Widget) Command {
  34. c.Widget = w
  35. return c
  36. }
  37. func (c Command) WidgetFunc(fn Func) Command {
  38. return c.WithWidget(fn)
  39. }
  40. func (c Command) ToApi() tgbotapi.BotCommand {
  41. ret := tgbotapi.BotCommand{}
  42. ret.Command = string(c.Name)
  43. ret.Description = c.Description
  44. return ret
  45. }
  46. func (c Command) Go(pth Path, args ...any) Command {
  47. return c.WithAction(ScreenGo{
  48. Path: pth,
  49. Args: args,
  50. })
  51. }
  52. // The type is used to recognize commands and execute
  53. // its actions and widgets .
  54. type CommandCompo struct {
  55. PreStart Action
  56. Commands CommandMap
  57. Usage Action
  58. }
  59. // Returns new empty CommandCompo.
  60. func NewCommandCompo(cmds ...*Command) *CommandCompo {
  61. ret := CommandCompo{}.WithCommands(cmds...)
  62. return ret
  63. }
  64. // Set the commands to handle.
  65. func (w CommandCompo) WithCommands(cmds ...*Command) *CommandCompo {
  66. if w.Commands == nil {
  67. w.Commands = make(CommandMap)
  68. }
  69. for _, cmd := range cmds {
  70. if cmd.Name == "" {
  71. panic("empty command name")
  72. }
  73. _, ok := w.Commands[cmd.Name]
  74. if ok {
  75. panic("duplicate command definition")
  76. }
  77. w.Commands[cmd.Name] = cmd
  78. }
  79. return w
  80. }
  81. // Set the prestart action.
  82. func (w *CommandCompo) WithPreStart(a Action) *CommandCompo {
  83. w.PreStart = a
  84. return w
  85. }
  86. // Set the usage action.
  87. func (w CommandCompo) WithUsage(a Action) *CommandCompo {
  88. w.Usage = a
  89. return w
  90. }
  91. // Set the usage action with function.
  92. func (w CommandCompo) WithUsageFunc(fn ActionFunc) *CommandCompo {
  93. return w.WithUsage(fn)
  94. }
  95. func (widget CommandCompo) Filter(
  96. u *Update,
  97. ) bool {
  98. if u.Message == nil || !u.Message.IsCommand() {
  99. return false
  100. }
  101. return false
  102. }
  103. // Implementing server.
  104. func (compo CommandCompo) Serve(c Context) {
  105. /*commanders := make(map[CommandName] BotCommander)
  106. for k, v := range compo.Commands {
  107. commanders[k] = v
  108. }*/
  109. c.bot.DeleteCommands()
  110. err := c.bot.SetCommands(
  111. tgbotapi.NewBotCommandScopeChat(c.Session.Id.ToApi()),
  112. compo.Commands,
  113. )
  114. if err != nil {
  115. c.Sendf("error: %q", err)
  116. }
  117. var cmdUpdates *UpdateChan
  118. for u := range c.Input() {
  119. if c.Path() == "" && u.Message != nil {
  120. // Skipping and executing the preinit action
  121. // while we have the empty screen.
  122. // E. g. the session did not start.
  123. if !(u.Message.IsCommand() && u.Message.Command() == "start") {
  124. c.WithUpdate(u).Run(compo.PreStart)
  125. continue
  126. }
  127. }
  128. if u.Message != nil && u.Message.IsCommand() {
  129. // Command handling.
  130. cmdName := CommandName(u.Message.Command())
  131. cmd, ok := compo.Commands[cmdName]
  132. if !ok {
  133. c.WithUpdate(u).Run(compo.Usage)
  134. continue
  135. }
  136. c.WithUpdate(u).Run(cmd.Action)
  137. if cmd.Widget != nil {
  138. cmdUpdates.Close()
  139. cmdUpdates, _ = c.WithUpdate(u).RunWidget(cmd.Widget)
  140. }
  141. continue
  142. }
  143. if !cmdUpdates.Closed() {
  144. // Send to the commands channel if we are
  145. // executing one.
  146. cmdUpdates.Send(u)
  147. } else {
  148. c.Skip(u)
  149. }
  150. }
  151. }