77 lines
1.9 KiB
Go
77 lines
1.9 KiB
Go
package bot
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"time"
|
|
|
|
"botpontos/internal/adapters/telegram"
|
|
)
|
|
|
|
// Runner faz o long polling do Telegram e responde cada mensagem.
|
|
type Runner struct {
|
|
client *telegram.Client
|
|
router *Router
|
|
log *slog.Logger
|
|
pollTimeout int
|
|
}
|
|
|
|
func NewRunner(client *telegram.Client, router *Router, log *slog.Logger, pollTimeout int) *Runner {
|
|
return &Runner{client: client, router: router, log: log, pollTimeout: pollTimeout}
|
|
}
|
|
|
|
// Run roda até o contexto ser cancelado. Erros de rede não derrubam o
|
|
// worker: o loop espera alguns segundos e tenta de novo.
|
|
func (r *Runner) Run(ctx context.Context) {
|
|
var offset int64
|
|
backoff := time.Second
|
|
r.log.Info("bot do Telegram iniciado")
|
|
for ctx.Err() == nil {
|
|
updates, err := r.client.GetUpdates(ctx, offset, r.pollTimeout)
|
|
if err != nil {
|
|
if ctx.Err() != nil {
|
|
return
|
|
}
|
|
r.log.Warn("falha no getUpdates do Telegram", "error", err, "retry_in", backoff.String())
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-time.After(backoff):
|
|
}
|
|
if backoff < 30*time.Second {
|
|
backoff *= 2
|
|
}
|
|
continue
|
|
}
|
|
backoff = time.Second
|
|
|
|
for _, update := range updates {
|
|
offset = update.UpdateID + 1
|
|
msg := update.Message
|
|
// Só conversas privadas: o bot trata dados pessoais de jornada.
|
|
if msg == nil || msg.Text == "" || (msg.Chat.Type != "" && msg.Chat.Type != "private") {
|
|
continue
|
|
}
|
|
r.handle(ctx, msg)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *Runner) handle(ctx context.Context, msg *telegram.Message) {
|
|
defer func() {
|
|
if v := recover(); v != nil {
|
|
r.log.Error("panic ao tratar mensagem do Telegram", "panic", v)
|
|
}
|
|
}()
|
|
handleCtx, cancel := context.WithTimeout(ctx, 45*time.Second)
|
|
defer cancel()
|
|
|
|
reply := r.router.Handle(handleCtx, msg.Chat.ID, msg.From.Name(), msg.Text)
|
|
if reply == "" {
|
|
return
|
|
}
|
|
if _, err := r.client.Send(ctx, msg.Chat.ID, reply); err != nil {
|
|
r.log.Warn("falha ao responder no Telegram", "error", err)
|
|
}
|
|
}
|