memos/plugin/telegram/bot.go
2023-10-26 09:02:50 +08:00

115 lines
2.7 KiB
Go

package telegram
import (
"context"
"errors"
"fmt"
"strings"
"time"
"go.uber.org/zap"
"github.com/usememos/memos/internal/log"
)
type Handler interface {
BotToken(ctx context.Context) string
MessageHandle(ctx context.Context, bot *Bot, message Message, attachments []Attachment) error
CallbackQueryHandle(ctx context.Context, bot *Bot, callbackQuery CallbackQuery) error
}
type Bot struct {
handler Handler
}
// NewBotWithHandler create a telegram bot with specified handler.
func NewBotWithHandler(h Handler) *Bot {
return &Bot{handler: h}
}
const noTokenWait = 30 * time.Second
const errRetryWait = 10 * time.Second
// Start start a long polling using getUpdates to get Update, call r.MessageHandle while get new message updates.
func (b *Bot) Start(ctx context.Context) {
var offset int64
for {
updates, err := b.GetUpdates(ctx, offset)
if err == ErrInvalidToken {
time.Sleep(noTokenWait)
continue
}
if err != nil {
log.Warn("fail to telegram.GetUpdates", zap.Error(err))
time.Sleep(errRetryWait)
continue
}
singleMessages := make([]Message, 0, len(updates))
groupMessages := make([]Message, 0, len(updates))
for _, update := range updates {
offset = update.UpdateID + 1
// handle CallbackQuery update
if update.CallbackQuery != nil {
err := b.handler.CallbackQueryHandle(ctx, b, *update.CallbackQuery)
if err != nil {
log.Error("fail to handle CallbackQuery", zap.Error(err))
}
continue
}
// handle Message update
if update.Message != nil {
message := *update.Message
// skip unsupported message
if !message.IsSupported() {
_, err := b.SendReplyMessage(ctx, message.Chat.ID, message.MessageID, "Supported messages: animation, audio, text, document, photo, video, video note, voice, other messages with caption")
if err != nil {
log.Error(fmt.Sprintf("fail to telegram.SendReplyMessage for messageID=%d", message.MessageID), zap.Error(err))
}
continue
}
// Group message need do more
if message.MediaGroupID != nil {
groupMessages = append(groupMessages, message)
continue
}
singleMessages = append(singleMessages, message)
continue
}
}
err = b.handleSingleMessages(ctx, singleMessages)
if err != nil {
log.Error("fail to handle singleMessage", zap.Error(err))
}
err = b.handleGroupMessages(ctx, groupMessages)
if err != nil {
log.Error("fail to handle plain text message", zap.Error(err))
}
}
}
var ErrInvalidToken = errors.New("token is invalid")
func (b *Bot) apiURL(ctx context.Context) (string, error) {
token := b.handler.BotToken(ctx)
if token == "" {
return "", ErrInvalidToken
}
if strings.HasPrefix(token, "http") {
return token, nil
}
return "https://api.telegram.org/bot" + token, nil
}