mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-31 06:51:29 +00:00
fix(send_message): add thread-not-found retry for Telegram forum topic sends
The standalone _send_telegram path in send_message_tool lacked the thread-not-found fallback that the gateway adapter has. When a forum topic thread_id was stale or deleted, the send would fail entirely instead of retrying to the General topic. Changes: - Add _is_telegram_thread_not_found() helper matching gateway adapter - Add thread-not-found retry in text send path - Add thread-not-found retry in media send path (with f.seek(0)) - Separate text_kwargs from thread_kwargs to prevent disable_web_page_preview leaking into send_photo/send_video calls Closes #27012
This commit is contained in:
parent
fc42bb918b
commit
df530b4a0c
1 changed files with 86 additions and 25 deletions
|
|
@ -786,6 +786,15 @@ async def _send_to_platform(platform, pconfig, chat_id, message, thread_id=None,
|
|||
return last_result
|
||||
|
||||
|
||||
def _is_telegram_thread_not_found(error: Exception) -> bool:
|
||||
"""Check if a Telegram error is a thread-not-found failure.
|
||||
|
||||
Matches the gateway adapter's ``_is_thread_not_found_error`` for
|
||||
the standalone ``_send_telegram`` path (issue #27012).
|
||||
"""
|
||||
return "thread not found" in str(error).lower()
|
||||
|
||||
|
||||
async def _send_telegram(token, chat_id, message, media_files=None, thread_id=None, disable_link_previews=False, force_document=False):
|
||||
"""Send via Telegram Bot API (one-shot, no polling needed).
|
||||
|
||||
|
|
@ -873,8 +882,12 @@ async def _send_telegram(token, chat_id, message, media_files=None, thread_id=No
|
|||
)
|
||||
if effective_thread_id is not None:
|
||||
thread_kwargs["message_thread_id"] = effective_thread_id
|
||||
# disable_web_page_preview is only valid for send_message, not
|
||||
# send_photo/send_video/etc. Keep it separate so media sends
|
||||
# don't inherit an invalid parameter (issue #27012).
|
||||
text_kwargs = dict(thread_kwargs)
|
||||
if disable_link_previews:
|
||||
thread_kwargs["disable_web_page_preview"] = True
|
||||
text_kwargs["disable_web_page_preview"] = True
|
||||
|
||||
last_msg = None
|
||||
warnings = []
|
||||
|
|
@ -884,11 +897,24 @@ async def _send_telegram(token, chat_id, message, media_files=None, thread_id=No
|
|||
last_msg = await _send_telegram_message_with_retry(
|
||||
bot,
|
||||
chat_id=int_chat_id, text=formatted,
|
||||
parse_mode=send_parse_mode, entities=_entities, **thread_kwargs
|
||||
parse_mode=send_parse_mode, entities=_entities, **text_kwargs
|
||||
)
|
||||
except Exception as md_error:
|
||||
# Parse failed, fall back to plain text
|
||||
if "parse" in str(md_error).lower() or "markdown" in str(md_error).lower() or "html" in str(md_error).lower():
|
||||
# Thread not found — retry without message_thread_id so the
|
||||
# message still delivers (matching the gateway adapter's
|
||||
# fallback behaviour, issue #27012).
|
||||
if _is_telegram_thread_not_found(md_error) and thread_kwargs:
|
||||
logger.warning(
|
||||
"Thread %s not found in _send_telegram, retrying without message_thread_id",
|
||||
thread_kwargs.get("message_thread_id"),
|
||||
)
|
||||
text_kwargs.pop("message_thread_id", None)
|
||||
last_msg = await _send_telegram_message_with_retry(
|
||||
bot,
|
||||
chat_id=int_chat_id, text=formatted,
|
||||
parse_mode=send_parse_mode, **text_kwargs
|
||||
)
|
||||
elif "parse" in str(md_error).lower() or "markdown" in str(md_error).lower() or "html" in str(md_error).lower():
|
||||
logger.warning(
|
||||
"Parse mode %s failed in _send_telegram, falling back to plain text: %s",
|
||||
send_parse_mode,
|
||||
|
|
@ -905,7 +931,7 @@ async def _send_telegram(token, chat_id, message, media_files=None, thread_id=No
|
|||
last_msg = await _send_telegram_message_with_retry(
|
||||
bot,
|
||||
chat_id=int_chat_id, text=plain,
|
||||
parse_mode=None, entities=_entities, **thread_kwargs
|
||||
parse_mode=None, entities=_entities, **text_kwargs
|
||||
)
|
||||
else:
|
||||
raise
|
||||
|
|
@ -920,26 +946,61 @@ async def _send_telegram(token, chat_id, message, media_files=None, thread_id=No
|
|||
ext = os.path.splitext(media_path)[1].lower()
|
||||
try:
|
||||
with open(media_path, "rb") as f:
|
||||
if ext in _IMAGE_EXTS and not force_document:
|
||||
last_msg = await bot.send_photo(
|
||||
chat_id=int_chat_id, photo=f, **thread_kwargs
|
||||
)
|
||||
elif ext in _VIDEO_EXTS:
|
||||
last_msg = await bot.send_video(
|
||||
chat_id=int_chat_id, video=f, **thread_kwargs
|
||||
)
|
||||
elif ext in _VOICE_EXTS and is_voice:
|
||||
last_msg = await bot.send_voice(
|
||||
chat_id=int_chat_id, voice=f, **thread_kwargs
|
||||
)
|
||||
elif ext in _TELEGRAM_SEND_AUDIO_EXTS:
|
||||
last_msg = await bot.send_audio(
|
||||
chat_id=int_chat_id, audio=f, **thread_kwargs
|
||||
)
|
||||
else:
|
||||
last_msg = await bot.send_document(
|
||||
chat_id=int_chat_id, document=f, **thread_kwargs
|
||||
)
|
||||
media_kwargs = dict(thread_kwargs)
|
||||
try:
|
||||
if ext in _IMAGE_EXTS and not force_document:
|
||||
last_msg = await bot.send_photo(
|
||||
chat_id=int_chat_id, photo=f, **media_kwargs
|
||||
)
|
||||
elif ext in _VIDEO_EXTS:
|
||||
last_msg = await bot.send_video(
|
||||
chat_id=int_chat_id, video=f, **media_kwargs
|
||||
)
|
||||
elif ext in _VOICE_EXTS and is_voice:
|
||||
last_msg = await bot.send_voice(
|
||||
chat_id=int_chat_id, voice=f, **media_kwargs
|
||||
)
|
||||
elif ext in _TELEGRAM_SEND_AUDIO_EXTS:
|
||||
last_msg = await bot.send_audio(
|
||||
chat_id=int_chat_id, audio=f, **media_kwargs
|
||||
)
|
||||
else:
|
||||
last_msg = await bot.send_document(
|
||||
chat_id=int_chat_id, document=f, **media_kwargs
|
||||
)
|
||||
except Exception as media_err:
|
||||
if _is_telegram_thread_not_found(media_err) and media_kwargs.get("message_thread_id"):
|
||||
# Thread not found for media — retry without
|
||||
# message_thread_id (issue #27012).
|
||||
logger.warning(
|
||||
"Thread %s not found for media send, retrying without message_thread_id",
|
||||
media_kwargs["message_thread_id"],
|
||||
)
|
||||
# Re-seek the file since the first attempt consumed it
|
||||
f.seek(0)
|
||||
media_kwargs.pop("message_thread_id", None)
|
||||
if ext in _IMAGE_EXTS and not force_document:
|
||||
last_msg = await bot.send_photo(
|
||||
chat_id=int_chat_id, photo=f, **media_kwargs
|
||||
)
|
||||
elif ext in _VIDEO_EXTS:
|
||||
last_msg = await bot.send_video(
|
||||
chat_id=int_chat_id, video=f, **media_kwargs
|
||||
)
|
||||
elif ext in _VOICE_EXTS and is_voice:
|
||||
last_msg = await bot.send_voice(
|
||||
chat_id=int_chat_id, voice=f, **media_kwargs
|
||||
)
|
||||
elif ext in _TELEGRAM_SEND_AUDIO_EXTS:
|
||||
last_msg = await bot.send_audio(
|
||||
chat_id=int_chat_id, audio=f, **media_kwargs
|
||||
)
|
||||
else:
|
||||
last_msg = await bot.send_document(
|
||||
chat_id=int_chat_id, document=f, **media_kwargs
|
||||
)
|
||||
else:
|
||||
raise
|
||||
except Exception as e:
|
||||
warning = _sanitize_error_text(f"Failed to send media {media_path}: {e}")
|
||||
logger.error(warning)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue