Golang framework for developing Telegram Bots
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

types.go 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. package finch
  2. import (
  3. "bytes"
  4. "sync"
  5. "github.com/go-telegram-bot-api/telegram-bot-api"
  6. )
  7. // Help contains information about a command,
  8. // used for showing info in the help command.
  9. type Help struct {
  10. Name string
  11. Description string
  12. Example string
  13. Botfather [][]string
  14. }
  15. // String converts a Help struct into a pretty string.
  16. //
  17. // full makes each command item multiline with extra newlines.
  18. func (h Help) String(full bool) string {
  19. b := &bytes.Buffer{}
  20. b.WriteString(h.Name)
  21. if full {
  22. b.WriteString("\n")
  23. } else {
  24. b.WriteString(" - ")
  25. }
  26. b.WriteString(h.Description)
  27. b.WriteString("\n")
  28. if full {
  29. b.WriteString("Example: ")
  30. b.WriteString(h.Example)
  31. b.WriteString("\n")
  32. }
  33. b.WriteString("\n")
  34. return b.String()
  35. }
  36. // BotfatherString formats a Help struct into something for Botfather.
  37. func (h Help) BotfatherString() string {
  38. if len(h.Botfather) == 0 {
  39. return ""
  40. }
  41. b := bytes.Buffer{}
  42. for k, v := range h.Botfather {
  43. b.WriteString(v[0])
  44. b.WriteString(" - ")
  45. b.WriteString(v[1])
  46. if k+1 != len(h.Botfather) {
  47. b.WriteString("\n")
  48. }
  49. }
  50. return b.String()
  51. }
  52. // Command contains the methods a must have.
  53. type Command interface {
  54. Help() Help
  55. Init(*CommandState, *Finch) error
  56. ShouldExecute(tgbotapi.Message) bool
  57. Execute(tgbotapi.Message) error
  58. ExecuteWaiting(tgbotapi.Message) error
  59. ShouldExecuteCallback(tgbotapi.CallbackQuery) bool
  60. ExecuteCallback(tgbotapi.CallbackQuery) error
  61. IsHighPriority(tgbotapi.Message) bool
  62. }
  63. // CommandBase is a default Command that handles various tasks for you,
  64. // and allows for you to not have to write empty methods.
  65. type CommandBase struct {
  66. *CommandState
  67. *Finch
  68. }
  69. // Help returns an empty Help struct.
  70. func (CommandBase) Help() Help { return Help{} }
  71. // Init sets CommandState equal to the current CommandState.
  72. //
  73. // If you overwrite this method, you must still set CommandState and Finch
  74. // to the correct values!
  75. func (cmd *CommandBase) Init(c *CommandState, f *Finch) error {
  76. cmd.CommandState = c
  77. cmd.Finch = f
  78. return nil
  79. }
  80. // ShouldExecute returns false, you should overwrite this method.
  81. func (CommandBase) ShouldExecute(tgbotapi.Message) bool { return false }
  82. // Execute returns nil to show no error, you should overwrite this method.
  83. func (CommandBase) Execute(tgbotapi.Message) error { return nil }
  84. // ExecuteWaiting returns nil to show no error, you may overwrite this
  85. // when you are expecting to get a reply that is not a command.
  86. func (CommandBase) ExecuteWaiting(tgbotapi.Message) error { return nil }
  87. // ShouldExecuteCallback allows you to set if you want your command to respond
  88. // to a callback query, even if SetWaiting was not set.
  89. func (CommandBase) ShouldExecuteCallback(tgbotapi.CallbackQuery) bool { return false }
  90. // ExecuteCallback returns nil to show no error, you may overwrite this
  91. // when you are expecting to get a callback query.
  92. func (CommandBase) ExecuteCallback(tgbotapi.CallbackQuery) error { return nil }
  93. // IsHighPriority return false, you should overwrite this function to
  94. // return true if your command needs to execute before checking for
  95. // commands that are waiting for a reply or keyboard input.
  96. func (CommandBase) IsHighPriority(tgbotapi.Message) bool { return false }
  97. // Get fetches an item from the Config struct.
  98. func (cmd CommandBase) Get(key string) interface{} {
  99. return cmd.Finch.Config[key]
  100. }
  101. // Set sets an item in the Config struct, then saves it.
  102. func (cmd CommandBase) Set(key string, value interface{}) {
  103. cmd.Finch.Config[key] = value
  104. cmd.Finch.Config.Save()
  105. }
  106. type userWaitMap struct {
  107. mutex *sync.Mutex
  108. userWait map[int]bool
  109. }
  110. // CommandState is the current state of a command.
  111. // It contains the command and if the command is waiting for a reply.
  112. type CommandState struct {
  113. Command Command
  114. waitingForReplyUser userWaitMap
  115. }
  116. // NewCommandState creates a new CommandState with an initialized map.
  117. func NewCommandState(cmd Command) *CommandState {
  118. return &CommandState{
  119. Command: cmd,
  120. waitingForReplyUser: userWaitMap{
  121. mutex: &sync.Mutex{},
  122. userWait: map[int]bool{},
  123. },
  124. }
  125. }
  126. // IsWaiting checks if the current CommandState is waiting for input from
  127. // this user.
  128. func (state *CommandState) IsWaiting(user int) bool {
  129. state.waitingForReplyUser.mutex.Lock()
  130. defer state.waitingForReplyUser.mutex.Unlock()
  131. if v, ok := state.waitingForReplyUser.userWait[user]; ok {
  132. return v
  133. }
  134. return false
  135. }
  136. // SetWaiting sets that the bot should expect user input from this user.
  137. func (state *CommandState) SetWaiting(user int) {
  138. state.waitingForReplyUser.mutex.Lock()
  139. defer state.waitingForReplyUser.mutex.Unlock()
  140. state.waitingForReplyUser.userWait[user] = true
  141. }
  142. // ReleaseWaiting sets that the bot should not expect any input from
  143. // this user.
  144. func (state *CommandState) ReleaseWaiting(user int) {
  145. state.waitingForReplyUser.mutex.Lock()
  146. defer state.waitingForReplyUser.mutex.Unlock()
  147. state.waitingForReplyUser.userWait[user] = false
  148. }
  149. // InlineCommand is a single command executed for an Inline Query.
  150. type InlineCommand interface {
  151. Execute(*Finch, tgbotapi.InlineQuery) error
  152. }