fix: keep agent typing status while queued

This commit is contained in:
jxxghp
2026-05-22 16:46:25 +08:00
parent cb15b711b9
commit 2b5528c0ac
4 changed files with 231 additions and 18 deletions
+35 -15
View File
@@ -422,6 +422,16 @@ class Telegram:
if task and task.is_alive() and task is not threading.current_thread():
task.join(timeout=1)
def _stop_typing_if_needed(
self, chat_id: Union[str, int], stop_typing: bool
) -> None:
"""
按调用方要求停止 typing。
Agent 回复和流式编辑由 worker 统一收口,避免中途发送消息时误关后续排队消息的状态。
"""
if stop_typing:
self._stop_typing_task(chat_id)
def stop_typing(
self,
chat_id: Optional[Union[str, int]] = None,
@@ -453,6 +463,7 @@ class Telegram:
original_message_id: Optional[int] = None,
original_chat_id: Optional[str] = None,
disable_web_page_preview: Optional[bool] = None,
stop_typing: bool = True,
) -> Optional[dict]:
"""
发送Telegram消息
@@ -465,6 +476,7 @@ class Telegram:
:param original_message_id: 原消息ID,如果提供则编辑原消息
:param original_chat_id: 原消息的聊天ID,编辑消息时需要
:param disable_web_page_preview: 是否禁用链接预览
:param stop_typing: 发送完成后是否立即停止 typing
:return: 包含 message_id, chat_id, success 的字典
"""
if not self._telegram_token or not self._telegram_chat_id:
@@ -474,7 +486,7 @@ class Telegram:
chat_id = self._determine_target_chat_id(userid, original_chat_id)
if not title and not text:
logger.warn("标题和内容不能同时为空")
self._stop_typing_task(chat_id)
self._stop_typing_if_needed(chat_id, stop_typing)
return {"success": False}
try:
@@ -510,7 +522,7 @@ class Telegram:
image,
disable_web_page_preview=disable_web_page_preview,
)
self._stop_typing_task(chat_id)
self._stop_typing_if_needed(chat_id, stop_typing)
return {
"success": bool(result),
"message_id": original_message_id,
@@ -525,7 +537,7 @@ class Telegram:
reply_markup=reply_markup,
disable_web_page_preview=disable_web_page_preview,
)
self._stop_typing_task(chat_id)
self._stop_typing_if_needed(chat_id, stop_typing)
if sent and hasattr(sent, "message_id"):
return {
"success": True,
@@ -538,7 +550,7 @@ class Telegram:
except Exception as msg_e:
logger.error(f"发送消息失败:{msg_e}")
self._stop_typing_task(chat_id)
self._stop_typing_if_needed(chat_id, stop_typing)
return {"success": False}
def send_voice(
@@ -547,6 +559,7 @@ class Telegram:
userid: Optional[str] = None,
caption: Optional[str] = None,
original_chat_id: Optional[str] = None,
stop_typing: bool = True,
) -> Optional[dict]:
"""
发送Telegram语音消息。
@@ -558,7 +571,7 @@ class Telegram:
voice_file = Path(voice_path)
if not voice_file.exists():
logger.error(f"语音文件不存在: {voice_file}")
self._stop_typing_task(chat_id)
self._stop_typing_if_needed(chat_id, stop_typing)
return {"success": False}
try:
@@ -569,7 +582,7 @@ class Telegram:
caption=standardize(caption) if caption else None,
parse_mode="MarkdownV2" if caption else None,
)
self._stop_typing_task(chat_id)
self._stop_typing_if_needed(chat_id, stop_typing)
if sent and hasattr(sent, "message_id"):
return {
"success": True,
@@ -579,7 +592,7 @@ class Telegram:
return {"success": bool(sent)}
except Exception as err:
logger.error(f"发送语音消息失败:{err}")
self._stop_typing_task(chat_id)
self._stop_typing_if_needed(chat_id, stop_typing)
return {"success": False}
finally:
try:
@@ -595,6 +608,7 @@ class Telegram:
text: Optional[str] = None,
file_name: Optional[str] = None,
original_chat_id: Optional[str] = None,
stop_typing: bool = True,
) -> Optional[dict]:
"""
发送本地图片或文件给 Telegram 用户。
@@ -606,7 +620,7 @@ class Telegram:
local_file = Path(file_path)
if not local_file.exists() or not local_file.is_file():
logger.error(f"附件文件不存在: {local_file}")
self._stop_typing_task(chat_id)
self._stop_typing_if_needed(chat_id, stop_typing)
return {"success": False}
send_name = file_name or local_file.name
@@ -639,7 +653,7 @@ class Telegram:
caption=standardize(caption) if caption else None,
parse_mode="MarkdownV2" if caption else None,
)
self._stop_typing_task(chat_id)
self._stop_typing_if_needed(chat_id, stop_typing)
if sent and hasattr(sent, "message_id"):
return {
"success": True,
@@ -649,7 +663,7 @@ class Telegram:
return {"success": bool(sent)}
except Exception as err:
logger.error(f"发送本地附件失败: {err}")
self._stop_typing_task(chat_id)
self._stop_typing_if_needed(chat_id, stop_typing)
return {"success": False}
def _determine_target_chat_id(
@@ -685,6 +699,7 @@ class Telegram:
buttons: Optional[List[List[Dict]]] = None,
original_message_id: Optional[int] = None,
original_chat_id: Optional[str] = None,
stop_typing: bool = True,
) -> Optional[bool]:
"""
发送媒体列表消息
@@ -695,11 +710,12 @@ class Telegram:
:param buttons: 按钮列表,格式:[[{"text": "按钮文本", "callback_data": "回调数据"}]]
:param original_message_id: 原消息ID,如果提供则编辑原消息
:param original_chat_id: 原消息的聊天ID,编辑消息时需要
:param stop_typing: 发送完成后是否立即停止 typing
"""
if not self._telegram_token or not self._telegram_chat_id:
return None
# 列表消息也可能是一次交互的最终响应,需要确保 typing 状态在发送后结束
# 列表消息也可能是一次交互的最终响应,默认在发送后结束 typing
chat_id = self._determine_target_chat_id(userid, original_chat_id)
try:
index, image, caption = 1, "", "*%s*" % title
@@ -752,7 +768,7 @@ class Telegram:
logger.error(f"发送消息失败:{msg_e}")
return False
finally:
self._stop_typing_task(chat_id)
self._stop_typing_if_needed(chat_id, stop_typing)
def send_torrents_msg(
self,
@@ -763,6 +779,7 @@ class Telegram:
buttons: Optional[List[List[Dict]]] = None,
original_message_id: Optional[int] = None,
original_chat_id: Optional[str] = None,
stop_typing: bool = True,
) -> Optional[bool]:
"""
发送种子列表消息
@@ -773,11 +790,12 @@ class Telegram:
:param buttons: 按钮列表,格式:[[{"text": "按钮文本", "callback_data": "回调数据"}]]
:param original_message_id: 原消息ID,如果提供则编辑原消息
:param original_chat_id: 原消息的聊天ID,编辑消息时需要
:param stop_typing: 发送完成后是否立即停止 typing
"""
if not self._telegram_token or not self._telegram_chat_id:
return None
# 资源列表是搜索交互的常见出口,也必须统一释放 typing 状态
# 资源列表是搜索交互的常见出口,默认在发送后结束 typing。
chat_id = self._determine_target_chat_id(userid, original_chat_id)
try:
index, caption = 1, "*%s*" % title
@@ -829,7 +847,7 @@ class Telegram:
logger.error(f"发送消息失败:{msg_e}")
return False
finally:
self._stop_typing_task(chat_id)
self._stop_typing_if_needed(chat_id, stop_typing)
@staticmethod
def _create_inline_keyboard(buttons: List[List[Dict]]) -> InlineKeyboardMarkup:
@@ -919,6 +937,7 @@ class Telegram:
text: str,
title: Optional[str] = None,
buttons: Optional[List[List[dict]]] = None,
stop_typing: bool = True,
) -> Optional[bool]:
"""
编辑Telegram消息(公开方法)
@@ -927,6 +946,7 @@ class Telegram:
:param text: 新的消息内容
:param title: 消息标题
:param buttons: 新的按钮列表
:param stop_typing: 编辑完成后是否立即停止 typing
:return: 编辑是否成功
"""
if not self._bot:
@@ -952,7 +972,7 @@ class Telegram:
logger.error(f"编辑Telegram消息异常: {str(e)}")
return False
finally:
self._stop_typing_task(chat_id)
self._stop_typing_if_needed(chat_id, stop_typing)
def __edit_message(
self,