Merge pull request #2092 from badgerbees/fix/telegram-edit-timeout
fix(telegram): avoid duplicate messages on streaming edit timeouts
This commit is contained in:
commit
415abc8cd4
1 changed files with 57 additions and 2 deletions
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
@ -377,8 +378,38 @@ func (c *TelegramChannel) EditMessage(ctx context.Context, chatID string, messag
|
||||||
}
|
}
|
||||||
_, err = c.bot.EditMessageText(ctx, editMsg)
|
_, err = c.bot.EditMessageText(ctx, editMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logParseFailed(err, useMarkdownV2)
|
// If it failed because it was already modified (likely from a previous
|
||||||
_, err = c.bot.EditMessageText(ctx, tu.EditMessageText(tu.ID(cid), mid, content))
|
// attempt that timed out on our end but landed on Telegram), we treat
|
||||||
|
// it as success to prevent the Manager from sending a duplicate message.
|
||||||
|
if strings.Contains(err.Error(), "message is not modified") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only fallback to plain text if the error looks like a parsing failure (Bad Request).
|
||||||
|
// Network errors or timeouts should NOT trigger a retry with different content.
|
||||||
|
if strings.Contains(err.Error(), "Bad Request") {
|
||||||
|
logParseFailed(err, useMarkdownV2)
|
||||||
|
_, err = c.bot.EditMessageText(ctx, tu.EditMessageText(tu.ID(cid), mid, content))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "message is not modified") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if isPostConnectError(err) {
|
||||||
|
logger.WarnCF(
|
||||||
|
"telegram",
|
||||||
|
"EditMessage likely landed but result is unknown; swallowing error to prevent duplicate",
|
||||||
|
map[string]any{
|
||||||
|
"chat_id": chatID,
|
||||||
|
"mid": mid,
|
||||||
|
"error": err.Error(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return nil // Swallow to prevent Manager fallback to a new SendMessage
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|
@ -1134,6 +1165,30 @@ func cryptoRandInt() int {
|
||||||
return int(binary.BigEndian.Uint32(b[:])) | 1 // ensure non-zero
|
return int(binary.BigEndian.Uint32(b[:])) | 1 // ensure non-zero
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isPostConnectError identifies network errors that likely occurred after
|
||||||
|
// the request was transmitted to Telegram (e.g. dropped connection while
|
||||||
|
// waiting for response). Swallowing these for edits prevents duplicate
|
||||||
|
// fallbacks, at the small risk of leaving a stale placeholder if the
|
||||||
|
// edit never actually reached the server.
|
||||||
|
func isPostConnectError(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context errors (timeout/canceled) are too broad; they can be triggered
|
||||||
|
// locally before any data is sent. Never swallow them.
|
||||||
|
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := strings.ToLower(err.Error())
|
||||||
|
// Narrowly target connection dropouts where the request likely landed.
|
||||||
|
return strings.Contains(msg, "connection reset by peer") ||
|
||||||
|
strings.Contains(msg, "unexpected eof") ||
|
||||||
|
strings.Contains(msg, "connection closed by foreign host") ||
|
||||||
|
strings.Contains(msg, "broken pipe")
|
||||||
|
}
|
||||||
|
|
||||||
// VoiceCapabilities returns the voice capabilities of the channel.
|
// VoiceCapabilities returns the voice capabilities of the channel.
|
||||||
func (c *TelegramChannel) VoiceCapabilities() channels.VoiceCapabilities {
|
func (c *TelegramChannel) VoiceCapabilities() channels.VoiceCapabilities {
|
||||||
return channels.VoiceCapabilities{ASR: true, TTS: true}
|
return channels.VoiceCapabilities{ASR: true, TTS: true}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue