From ea07d8ad0072891936eab6bade22ec65b51d942f Mon Sep 17 00:00:00 2001 From: Bijin <38134380+sliverp@users.noreply.github.com> Date: Sat, 21 Mar 2026 22:06:54 +0800 Subject: [PATCH] fix(telegram): add document message support (docx/pdf/etc) (#2069) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Telegram adapter only handles TEXT, COMMAND, PHOTO, and VOICE messages. Document files (docx, pdf, etc.) sent by users are silently dropped because: 1. MessageHandler filters lack filters.Document.ALL 2. target2yiri() has no message.document branch 3. yiri2target() has no platform_message.File branch 4. send_message() has no 'document' component handler Changes: - Add filters.Document.ALL to the MessageHandler filter set - Add message.document parsing in target2yiri() → platform_message.File - Add platform_message.File handling in yiri2target() → document component - Add 'document' type handling in send_message() via bot.send_document() This allows Telegram document messages to flow through the existing PreProcessor and Dify file upload pipeline, consistent with how other adapters (Lark, KOOK, Discord, WeCom) already handle files. Closes #2065 --- src/langbot/pkg/platform/sources/telegram.py | 49 +++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/langbot/pkg/platform/sources/telegram.py b/src/langbot/pkg/platform/sources/telegram.py index d43b9333..51797a66 100644 --- a/src/langbot/pkg/platform/sources/telegram.py +++ b/src/langbot/pkg/platform/sources/telegram.py @@ -42,6 +42,25 @@ class TelegramMessageConverter(abstract_platform_adapter.AbstractMessageConverte photo_bytes = f.read() components.append({'type': 'photo', 'photo': photo_bytes}) + elif isinstance(component, platform_message.File): + file_bytes = None + + if component.base64: + # Strip data URI prefix if present (e.g. "data:application/pdf;base64,...") + b64_data = component.base64 + if ';base64,' in b64_data: + b64_data = b64_data.split(';base64,', 1)[1] + file_bytes = base64.b64decode(b64_data) + elif component.url: + session = httpclient.get_session() + async with session.get(component.url) as response: + file_bytes = await response.read() + elif component.path: + with open(component.path, 'rb') as f: + file_bytes = f.read() + + file_name = getattr(component, 'name', None) or 'file' + components.append({'type': 'document', 'document': file_bytes, 'filename': file_name}) elif isinstance(component, platform_message.Forward): for node in component.node_list: components.extend(await TelegramMessageConverter.yiri2target(node.message_chain, bot)) @@ -104,6 +123,27 @@ class TelegramMessageConverter(abstract_platform_adapter.AbstractMessageConverte ) ) + if message.document: + if message.caption: + message_components.extend(parse_message_text(message.caption)) + + file = await message.document.get_file() + file_name = message.document.file_name or 'document' + file_size = message.document.file_size or 0 + file_format = message.document.mime_type or 'application/octet-stream' + + file_bytes = None + async with httpclient.get_session(trust_env=True).get(file.file_path) as response: + file_bytes = await response.read() + + message_components.append( + platform_message.File( + name=file_name, + size=file_size, + base64=f'data:{file_format};base64,{base64.b64encode(file_bytes).decode("utf-8")}', + ) + ) + return platform_message.MessageChain(message_components) @@ -179,7 +219,7 @@ class TelegramAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): application = ApplicationBuilder().token(config['token']).build() bot = application.bot application.add_handler( - MessageHandler(filters.TEXT | (filters.COMMAND) | filters.PHOTO | filters.VOICE, telegram_callback) + MessageHandler(filters.TEXT | (filters.COMMAND) | filters.PHOTO | filters.VOICE | filters.Document.ALL, telegram_callback) ) super().__init__( config=config, @@ -218,6 +258,13 @@ class TelegramAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): continue args['photo'] = telegram.InputFile(photo) await self.bot.send_photo(**args) + elif component_type == 'document': + doc = component.get('document') + if doc is None: + continue + filename = component.get('filename', 'file') + args['document'] = telegram.InputFile(doc, filename=filename) + await self.bot.send_document(**args) async def reply_message( self,