mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-02 15:47:16 +00:00
feat(telegram): enhance message handling with group stream deletion and form placeholder detection
This commit is contained in:
@@ -429,28 +429,34 @@ class TelegramAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
|||||||
args['parse_mode'] = 'MarkdownV2'
|
args['parse_mode'] = 'MarkdownV2'
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
async def _delete_group_stream_message(self, chat_mode: str, chat_id: int, stream_id: int | None):
|
||||||
|
if chat_mode != 'group' or stream_id is None:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
await self.bot.delete_message(chat_id=chat_id, message_id=stream_id)
|
||||||
|
except telegram.error.TelegramError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _is_form_placeholder_chunk(text: str) -> bool:
|
||||||
|
"""Return True for invisible placeholder chunks used to carry forms."""
|
||||||
|
|
||||||
|
if not text:
|
||||||
|
return True
|
||||||
|
|
||||||
|
cleaned = text.replace('\u200b', '').replace('\u200c', '').replace('\u200d', '').replace('\ufeff', '').strip()
|
||||||
|
return cleaned == ''
|
||||||
|
|
||||||
async def create_message_card(self, message_id, event):
|
async def create_message_card(self, message_id, event):
|
||||||
assert isinstance(event.source_platform_object, Update)
|
assert isinstance(event.source_platform_object, Update)
|
||||||
update = event.source_platform_object
|
update = event.source_platform_object
|
||||||
chat_id = update.effective_chat.id
|
chat_id = update.effective_chat.id
|
||||||
chat_type = update.effective_chat.type
|
|
||||||
effective_message = update.effective_message
|
effective_message = update.effective_message
|
||||||
message_thread_id = getattr(effective_message, 'message_thread_id', None) if effective_message else None
|
message_thread_id = getattr(effective_message, 'message_thread_id', None) if effective_message else None
|
||||||
|
|
||||||
if chat_type == 'private':
|
args = self._build_message_args(chat_id, 'Thinking...', message_thread_id)
|
||||||
import time as _time
|
send_msg = await self.bot.send_message(**args)
|
||||||
|
self.msg_stream_id[message_id] = ('message', send_msg.message_id, False)
|
||||||
draft_id = int(_time.time() * 1000)
|
|
||||||
self.msg_stream_id[message_id] = ('private', draft_id)
|
|
||||||
args = self._build_message_args(chat_id, 'Thinking...', message_thread_id, draft_id=draft_id)
|
|
||||||
try:
|
|
||||||
await self.bot.send_message_draft(**args)
|
|
||||||
except (telegram.error.RetryAfter, telegram.error.BadRequest):
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
args = self._build_message_args(chat_id, 'Thinking...', message_thread_id)
|
|
||||||
send_msg = await self.bot.send_message(**args)
|
|
||||||
self.msg_stream_id[message_id] = ('group', send_msg.message_id)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -473,7 +479,9 @@ class TelegramAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
|||||||
if message_id not in self.msg_stream_id:
|
if message_id not in self.msg_stream_id:
|
||||||
return
|
return
|
||||||
|
|
||||||
chat_mode, stream_id = self.msg_stream_id[message_id]
|
stream_state = self.msg_stream_id[message_id]
|
||||||
|
chat_mode, stream_id = stream_state[:2]
|
||||||
|
has_visible_content = len(stream_state) > 2 and stream_state[2]
|
||||||
components = await TelegramMessageConverter.yiri2target(message, self.bot)
|
components = await TelegramMessageConverter.yiri2target(message, self.bot)
|
||||||
|
|
||||||
if not components or components[0]['type'] != 'text':
|
if not components or components[0]['type'] != 'text':
|
||||||
@@ -485,8 +493,17 @@ class TelegramAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
|||||||
form_data = getattr(bot_message, '_form_data', None)
|
form_data = getattr(bot_message, '_form_data', None)
|
||||||
|
|
||||||
if form_data and is_final:
|
if form_data and is_final:
|
||||||
|
if not has_visible_content:
|
||||||
|
await self._send_form_action_buttons(message_source, form_data, edit_message_id=stream_id)
|
||||||
|
else:
|
||||||
|
await self._send_form_action_buttons(message_source, form_data)
|
||||||
self.msg_stream_id.pop(message_id, None)
|
self.msg_stream_id.pop(message_id, None)
|
||||||
await self._send_form_action_buttons(message_source, form_data)
|
return
|
||||||
|
|
||||||
|
if self._is_form_placeholder_chunk(content):
|
||||||
|
if is_final and bot_message.tool_calls is None and not has_visible_content:
|
||||||
|
await self._delete_group_stream_message(chat_mode, chat_id, stream_id)
|
||||||
|
self.msg_stream_id.pop(message_id, None)
|
||||||
return
|
return
|
||||||
|
|
||||||
if chat_mode == 'private':
|
if chat_mode == 'private':
|
||||||
@@ -504,6 +521,7 @@ class TelegramAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
pass # Ignore other draft errors (cosmetic)
|
pass # Ignore other draft errors (cosmetic)
|
||||||
|
self.msg_stream_id[message_id] = (chat_mode, stream_id, True)
|
||||||
if is_final and bot_message.tool_calls is None:
|
if is_final and bot_message.tool_calls is None:
|
||||||
# Finalise: send the real message, discard the draft
|
# Finalise: send the real message, discard the draft
|
||||||
args = self._build_message_args(chat_id, content, message_thread_id)
|
args = self._build_message_args(chat_id, content, message_thread_id)
|
||||||
@@ -518,7 +536,22 @@ class TelegramAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
|||||||
self.msg_stream_id.pop(message_id)
|
self.msg_stream_id.pop(message_id)
|
||||||
else:
|
else:
|
||||||
# Streaming via edit_message_text (persistent message)
|
# Streaming via edit_message_text (persistent message)
|
||||||
if (msg_seq - 1) % 8 == 0 or is_final:
|
if stream_id is None:
|
||||||
|
args = self._build_message_args(chat_id, content, message_thread_id)
|
||||||
|
try:
|
||||||
|
send_msg = await self.bot.send_message(**args)
|
||||||
|
except telegram.error.BadRequest as exc:
|
||||||
|
if 'Message_too_long' in str(exc):
|
||||||
|
args['text'] = self._process_markdown(content[:4000] + '\n\n鈥?(truncated)')
|
||||||
|
send_msg = await self.bot.send_message(**args)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
self.msg_stream_id[message_id] = (chat_mode, send_msg.message_id, True)
|
||||||
|
if is_final and bot_message.tool_calls is None:
|
||||||
|
self.msg_stream_id.pop(message_id, None)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not has_visible_content or (msg_seq - 1) % 8 == 0 or is_final:
|
||||||
args = {
|
args = {
|
||||||
'message_id': stream_id,
|
'message_id': stream_id,
|
||||||
'chat_id': chat_id,
|
'chat_id': chat_id,
|
||||||
@@ -534,6 +567,7 @@ class TelegramAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
|||||||
await self.bot.edit_message_text(**args)
|
await self.bot.edit_message_text(**args)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
self.msg_stream_id[message_id] = (chat_mode, stream_id, True)
|
||||||
|
|
||||||
if is_final and bot_message.tool_calls is None:
|
if is_final and bot_message.tool_calls is None:
|
||||||
self.msg_stream_id.pop(message_id)
|
self.msg_stream_id.pop(message_id)
|
||||||
@@ -542,6 +576,7 @@ class TelegramAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
|||||||
self,
|
self,
|
||||||
message_source: platform_events.MessageEvent,
|
message_source: platform_events.MessageEvent,
|
||||||
form_data: dict,
|
form_data: dict,
|
||||||
|
edit_message_id: int | None = None,
|
||||||
):
|
):
|
||||||
"""Send inline keyboard buttons for Dify human_input_required form actions."""
|
"""Send inline keyboard buttons for Dify human_input_required form actions."""
|
||||||
actions = form_data.get('actions', [])
|
actions = form_data.get('actions', [])
|
||||||
@@ -604,6 +639,20 @@ class TelegramAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
|
|||||||
if message_thread_id:
|
if message_thread_id:
|
||||||
args['message_thread_id'] = message_thread_id
|
args['message_thread_id'] = message_thread_id
|
||||||
|
|
||||||
|
if edit_message_id is not None:
|
||||||
|
edit_args = {
|
||||||
|
'chat_id': chat_id,
|
||||||
|
'message_id': edit_message_id,
|
||||||
|
'text': args['text'],
|
||||||
|
}
|
||||||
|
if 'reply_markup' in args:
|
||||||
|
edit_args['reply_markup'] = args['reply_markup']
|
||||||
|
try:
|
||||||
|
await self.bot.edit_message_text(**edit_args)
|
||||||
|
return
|
||||||
|
except telegram.error.TelegramError:
|
||||||
|
await self._delete_group_stream_message('group', chat_id, edit_message_id)
|
||||||
|
|
||||||
await self.bot.send_message(**args)
|
await self.bot.send_message(**args)
|
||||||
|
|
||||||
def get_launcher_id(self, event: platform_events.MessageEvent) -> str | None:
|
def get_launcher_id(self, event: platform_events.MessageEvent) -> str | None:
|
||||||
|
|||||||
Reference in New Issue
Block a user