From 29dfae1518c3021f009e6658fad912369351ba00 Mon Sep 17 00:00:00 2001 From: Typer_Body Date: Thu, 12 Feb 2026 02:44:47 +0800 Subject: [PATCH] Add files via upload --- src/langbot/pkg/platform/sources/satori.png | Bin 0 -> 10663 bytes src/langbot/pkg/platform/sources/satori.py | 1015 ++++++++++++++++++ src/langbot/pkg/platform/sources/satori.yaml | 65 ++ 3 files changed, 1080 insertions(+) create mode 100644 src/langbot/pkg/platform/sources/satori.png create mode 100644 src/langbot/pkg/platform/sources/satori.py create mode 100644 src/langbot/pkg/platform/sources/satori.yaml diff --git a/src/langbot/pkg/platform/sources/satori.png b/src/langbot/pkg/platform/sources/satori.png new file mode 100644 index 0000000000000000000000000000000000000000..8a12cc6da189c1fa7e0ce8bdacf6127cd2cdaabe GIT binary patch literal 10663 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 str: + """Convert LangBot MessageChain to Satori message format""" + content_parts = [] + + for component in message_chain: + if isinstance(component, platform_message.Plain): + text = component.text.replace("&", "&").replace("<", "<").replace(">", ">") + content_parts.append(text) + elif isinstance(component, platform_message.Image): + # Prefer URL over base64 to avoid buffer overflow issues with large images + if component.url: + content_parts.append(f'') + elif hasattr(component, "base64") and component.base64: + # Process base64 data + base64_data = component.base64 + # Remove whitespace that might corrupt the data + base64_data = base64_data.replace('\n', '').replace('\r', '').replace(' ', '') + + # Check size - if too large, try to upload + MAX_INLINE_SIZE = 32 * 1024 # 32KB limit for inline base64 + + # Extract raw base64 and mime type + raw_b64 = base64_data + mime_type = "image/png" + if base64_data.startswith("data:"): + try: + header, raw_b64 = base64_data.split(',', 1) + if ';' in header: + mime_type = header.split(':')[1].split(';')[0] + except (ValueError, IndexError): + pass + + if len(raw_b64) > MAX_INLINE_SIZE: + # Try to upload large image + try: + # Fix base64 padding if needed + padding = 4 - len(raw_b64) % 4 + if padding != 4: + raw_b64 += '=' * padding + image_bytes = base64.b64decode(raw_b64) + uploaded_url = await adapter.upload_image(image_bytes, mime_type) + if uploaded_url: + await adapter.logger.info(f"Satori 图片上传成功: {len(image_bytes)} 字节") + content_parts.append(f'') + else: + # Upload failed, use inline (may fail) + await adapter.logger.warning("Satori 图片上传失败,使用内联模式") + content_parts.append(f'') + except Exception as e: + await adapter.logger.error(f"Satori 图片处理失败: {e}") + content_parts.append(f'') + else: + # Small image, use inline + content_parts.append(f'') + elif isinstance(component, platform_message.At): + if component.target: + content_parts.append(f'') + elif isinstance(component, platform_message.AtAll): + content_parts.append('') + elif isinstance(component, platform_message.Reply): + content_parts.append(f'') + elif isinstance(component, platform_message.Quote): + content_parts.append(f'') + elif isinstance(component, platform_message.Face): + # Satori中的表情可以使用emoticon元素 + face_id = getattr(component, 'face_id', 'unknown') + content_parts.append(f'') + elif isinstance(component, platform_message.Voice): + if hasattr(component, 'url') and component.url: + content_parts.append(f'