mirror of
https://github.com/HugeFrog24/go-telegram-bot.git
synced 2026-08-28 22:11:38 +00:00
66 lines
1.8 KiB
Go
66 lines
1.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/go-telegram/bot"
|
|
"github.com/go-telegram/bot/models"
|
|
)
|
|
|
|
// typingRefreshInterval re-arms the chat action before Telegram expires it.
|
|
// The Bot API sets the status "for 5 seconds or less", so anything at or above
|
|
// 5s leaves visible gaps. Telegram also clears the status as soon as the bot
|
|
// sends a message, so streamed segments naturally interrupt it until the next
|
|
// tick; there is no API call to clear it early.
|
|
const typingRefreshInterval = 4 * time.Second
|
|
|
|
// startChatAction shows a chat action (typing, uploading a photo, recording a
|
|
// voice note) and keeps it alive until the returned stop function runs.
|
|
//
|
|
// The returned function is idempotent and MUST be deferred by the caller. A
|
|
// keepalive loop that can outlive its turn is the failure mode behind
|
|
// openclaw/openclaw#27177, where the indicator stuck on until the process was
|
|
// restarted, so the loop here owns a derived context and exits on the first of:
|
|
// stop being called, or the parent context ending.
|
|
func (b *Bot) startChatAction(
|
|
ctx context.Context,
|
|
chatID int64,
|
|
businessConnectionID string,
|
|
action models.ChatAction,
|
|
) (stop func()) {
|
|
actionCtx, cancel := context.WithCancel(ctx)
|
|
|
|
send := func() {
|
|
params := &bot.SendChatActionParams{
|
|
ChatID: chatID,
|
|
Action: action,
|
|
}
|
|
if businessConnectionID != "" {
|
|
params.BusinessConnectionID = businessConnectionID
|
|
}
|
|
if _, err := b.tgBot.SendChatAction(actionCtx, params); err != nil {
|
|
// Cosmetic only: a failed indicator must never abort the turn.
|
|
InfoLogger.Printf("[%s] chat action %q failed for chat %d: %v",
|
|
b.config.ID, action, chatID, err)
|
|
}
|
|
}
|
|
|
|
send()
|
|
|
|
go func() {
|
|
ticker := time.NewTicker(typingRefreshInterval)
|
|
defer ticker.Stop()
|
|
for {
|
|
select {
|
|
case <-actionCtx.Done():
|
|
return
|
|
case <-ticker.C:
|
|
send()
|
|
}
|
|
}
|
|
}()
|
|
|
|
return cancel
|
|
}
|