bot.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package telegram
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "strings"
  7. "time"
  8. "github.com/usememos/memos/common/log"
  9. "go.uber.org/zap"
  10. )
  11. type Handler interface {
  12. BotToken(ctx context.Context) string
  13. MessageHandle(ctx context.Context, bot *Bot, message Message, blobs map[string][]byte) error
  14. CallbackQueryHandle(ctx context.Context, bot *Bot, callbackQuery CallbackQuery) error
  15. }
  16. type Bot struct {
  17. handler Handler
  18. }
  19. // NewBotWithHandler create a telegram bot with specified handler.
  20. func NewBotWithHandler(h Handler) *Bot {
  21. return &Bot{handler: h}
  22. }
  23. const noTokenWait = 30 * time.Second
  24. const errRetryWait = 10 * time.Second
  25. // Start start an infinity call of getUpdates from Telegram, call r.MessageHandle while get new message updates.
  26. func (b *Bot) Start(ctx context.Context) {
  27. var offset int
  28. for {
  29. updates, err := b.GetUpdates(ctx, offset)
  30. if err == ErrInvalidToken {
  31. time.Sleep(noTokenWait)
  32. continue
  33. }
  34. if err != nil {
  35. log.Warn("fail to telegram.GetUpdates", zap.Error(err))
  36. time.Sleep(errRetryWait)
  37. continue
  38. }
  39. singleMessages := make([]Message, 0, len(updates))
  40. groupMessages := make([]Message, 0, len(updates))
  41. for _, update := range updates {
  42. offset = update.UpdateID + 1
  43. // handle CallbackQuery update
  44. if update.CallbackQuery != nil {
  45. err := b.handler.CallbackQueryHandle(ctx, b, *update.CallbackQuery)
  46. if err != nil {
  47. log.Error("fail to handle CallbackQuery", zap.Error(err))
  48. }
  49. continue
  50. }
  51. // handle Message update
  52. if update.Message != nil {
  53. message := *update.Message
  54. // skip message other than text or photo
  55. if message.Text == nil && message.Photo == nil {
  56. _, err := b.SendReplyMessage(ctx, message.Chat.ID, message.MessageID, "Only text or photo message be supported")
  57. if err != nil {
  58. log.Error(fmt.Sprintf("fail to telegram.SendReplyMessage for messageID=%d", message.MessageID), zap.Error(err))
  59. }
  60. continue
  61. }
  62. // Group message need do more
  63. if message.MediaGroupID != nil {
  64. groupMessages = append(groupMessages, message)
  65. continue
  66. }
  67. singleMessages = append(singleMessages, message)
  68. continue
  69. }
  70. }
  71. err = b.handleSingleMessages(ctx, singleMessages)
  72. if err != nil {
  73. log.Error("fail to handle singleMessage", zap.Error(err))
  74. }
  75. err = b.handleGroupMessages(ctx, groupMessages)
  76. if err != nil {
  77. log.Error("fail to handle plain text message", zap.Error(err))
  78. }
  79. }
  80. }
  81. var ErrInvalidToken = errors.New("token is invalid")
  82. func (b *Bot) apiURL(ctx context.Context) (string, error) {
  83. token := b.handler.BotToken(ctx)
  84. if token == "" {
  85. return "", ErrInvalidToken
  86. }
  87. if strings.HasPrefix(token, "http") {
  88. return token, nil
  89. }
  90. return "https://api.telegram.org/bot" + token, nil
  91. }