From 2ffe2967d699616753644989aa59572a08be7640 Mon Sep 17 00:00:00 2001 From: Ethan Date: Tue, 25 Feb 2025 11:32:35 +0800 Subject: [PATCH] feat: add download image port configuration and improve image retrieval process --- pkg/core/migrations/m025_gewechat_config.py | 1 + pkg/platform/sources/gewechat.py | 15 +-- pkg/platform/sources/gewechat.yaml | 14 +-- pkg/utils/image.py | 102 ++++++++++++-------- templates/platform.json | 1 + templates/schema/platform.json | 5 + 6 files changed, 83 insertions(+), 55 deletions(-) diff --git a/pkg/core/migrations/m025_gewechat_config.py b/pkg/core/migrations/m025_gewechat_config.py index 3ed108c0..b5b01a43 100644 --- a/pkg/core/migrations/m025_gewechat_config.py +++ b/pkg/core/migrations/m025_gewechat_config.py @@ -23,6 +23,7 @@ class GewechatConfigMigration(migration.Migration): "adapter": "gewechat", "enable": False, "gewechat_url": "http://your-gewechat-server:2531", + "gewechat_downloadImage_port": 2532, "port": 2286, "callback_url": "http://your-callback-url:2286/gewechat/callback", "app_id": "", diff --git a/pkg/platform/sources/gewechat.py b/pkg/platform/sources/gewechat.py index 7bac28c8..23c6d41b 100644 --- a/pkg/platform/sources/gewechat.py +++ b/pkg/platform/sources/gewechat.py @@ -56,7 +56,7 @@ class GewechatMessageConverter(adapter.MessageConverter): message: dict, bot_account_id: str ) -> platform_message.MessageChain: - + if message["Data"]["MsgType"] == 1: # 检查消息开头,如果有 wxid_sbitaz0mt65n22:\n 则删掉 regex = re.compile(r"^wxid_.*:") @@ -89,8 +89,8 @@ class GewechatMessageConverter(adapter.MessageConverter): app_id=self.config["app_id"], xml_content=image_xml, token=self.config["token"], - data_dir=self.config.get("data_dir", "/root/docker/gewechat/data/download"), - image_type=2 + image_type=2, + download_image_port=self.config["gewechat_downloadImage_port"] ) return platform_message.MessageChain([ @@ -107,20 +107,15 @@ class GewechatMessageConverter(adapter.MessageConverter): elif message["Data"]["MsgType"] == 49: # 支持微信聊天记录的消息类型,将 XML 内容转换为 MessageChain 传递 try: - # 首先确保内容是字符串 content = message["Data"]["Content"]["string"] - # 如果内容已经是base64编码,尝试解码 try: - # 先将字符串编码为UTF-8字节 content_bytes = content.encode('utf-8') - # 然后进行base64解码 decoded_content = base64.b64decode(content_bytes) return platform_message.MessageChain( [platform_message.Unknown(content=decoded_content)] ) except Exception as e: - # 如果解码失败,直接返回原始内容 return platform_message.MessageChain( [platform_message.Plain(text=content)] ) @@ -175,7 +170,7 @@ class GewechatEventConverter(adapter.EventConverter): time=event["Data"]["CreateTime"], source_platform_object=event, ) - elif 'wxid_' in event["Data"]["FromUserName"]["string"]: + else: return platform_events.FriendMessage( sender=platform_entities.Friend( id=event["Data"]["FromUserName"]["string"], @@ -221,7 +216,7 @@ class GeWeChatAdapter(adapter.MessagePlatformAdapter): @self.quart_app.route('/gewechat/callback', methods=['POST']) async def gewechat_callback(): data = await quart.request.json - # print(json.dumps(data, indent=4, ensure_ascii=False)) + print(json.dumps(data, indent=4, ensure_ascii=False)) if 'testMsg' in data: return 'ok' diff --git a/pkg/platform/sources/gewechat.yaml b/pkg/platform/sources/gewechat.yaml index 42659411..05b721a3 100644 --- a/pkg/platform/sources/gewechat.yaml +++ b/pkg/platform/sources/gewechat.yaml @@ -17,6 +17,13 @@ spec: type: string required: true default: "" + - name: gewechat_downloadImage_port + label: + en_US: GeWeChat download image port + zh_CN: GeWeChat 下载图片的端口 + type: int + required: true + default: 2532 - name: port label: en_US: Port @@ -45,13 +52,6 @@ spec: type: string required: true default: "" - - name: data_dir - label: - en_US: Data Directory - zh_CN: 数据目录 - type: string - required: true - default: "/root/docker/gewechat/data/download" execution: python: path: ./gewechat.py diff --git a/pkg/utils/image.py b/pkg/utils/image.py index f5618d10..5f185615 100644 --- a/pkg/utils/image.py +++ b/pkg/utils/image.py @@ -11,6 +11,7 @@ import httpx import os import aiofiles import pathlib +import asyncio from urllib.parse import urlparse @@ -19,69 +20,94 @@ async def get_gewechat_image_base64( app_id: str, xml_content: str, token: str, - data_dir: str = "/root/docker/gewechat/data/download", # gewechat数据目录 - image_type: int = 2 + image_type: int = 2, + download_image_port: int = 2532, ) -> typing.Tuple[str, str]: - """从gewechat本地文件系统获取图片并转换为base64格式 + """从gewechat服务器获取图片并转换为base64格式 Args: gewechat_url (str): gewechat服务器地址(用于获取图片URL) app_id (str): gewechat应用ID xml_content (str): 图片的XML内容 token (str): Gewechat API Token - data_dir (str): gewechat数据目录 image_type (int, optional): 图片类型. Defaults to 2. + download_image_port (int, optional): 图片下载服务端口. Defaults to 2532. Returns: typing.Tuple[str, str]: (base64编码, 图片格式) + + Raises: + aiohttp.ClientTimeout: 请求超时(15秒)或连接超时(2秒) + Exception: 其他错误 """ headers = { 'X-GEWE-TOKEN': token, 'Content-Type': 'application/json' } - async with aiohttp.ClientSession() as session: - # 获取图片下载链接 - async with session.post( - f"{gewechat_url}/v2/api/message/downloadImage", - headers=headers, - json={ - "appId": app_id, - "type": image_type, - "xml": xml_content - } - ) as response: - if response.status != 200: - raise Exception(f"获取gewechat图片下载失败: {await response.text()}") + # 设置超时 + timeout = aiohttp.ClientTimeout( + total=15.0, # 总超时时间15秒 + connect=2.0, # 连接超时2秒 + sock_connect=2.0, # socket连接超时2秒 + sock_read=15.0 # socket读取超时15秒 + ) - resp_data = await response.json() - if resp_data.get("ret") != 200: - raise Exception(f"获取gewechat图片下载链接失败: {resp_data}") + try: + async with aiohttp.ClientSession(timeout=timeout) as session: + # 获取图片下载链接 + try: + async with session.post( + f"{gewechat_url}/v2/api/message/downloadImage", + headers=headers, + json={ + "appId": app_id, + "type": image_type, + "xml": xml_content + } + ) as response: + if response.status != 200: + raise Exception(f"获取gewechat图片下载失败: {await response.text()}") - # 从URL中解析文件路径 - file_url = resp_data['data']['fileUrl'] - parsed_url = urlparse(file_url) - path_parts = parsed_url.path.strip('/').split('/') + resp_data = await response.json() + if resp_data.get("ret") != 200: + raise Exception(f"获取gewechat图片下载链接失败: {resp_data}") - # 构建本地文件路径 - # URL格式: /20250224/wx_U48cqQsdtqedi4Cak7MnN/35f6240a-c778-4579-93ee-d8002f91a8ed.png - local_path = os.path.join(data_dir, *path_parts) + file_url = resp_data['data']['fileUrl'] + except asyncio.TimeoutError: + raise Exception("获取图片下载链接超时") + except aiohttp.ClientError as e: + raise Exception(f"获取图片下载链接网络错误: {str(e)}") - # 确保文件存在 - if not os.path.exists(local_path): - raise Exception(f"图片文件不存在: {local_path}") + # 解析原始URL并替换端口 + parsed_url = urlparse(gewechat_url) + base_url = f"http://{parsed_url.hostname}:{download_image_port}" + download_url = f"{base_url}/download/{file_url}" - # 读取本地文件 - async with aiofiles.open(local_path, 'rb') as f: - image_data = await f.read() + # 下载图片 + try: + async with session.get(download_url) as img_response: + if img_response.status != 200: + raise Exception(f"下载图片失败: {await img_response.text()}, URL: {download_url}") - # 获取图片格式 - image_format = pathlib.Path(local_path).suffix.lstrip('.') + image_data = await img_response.read() - # 转换为base64 - base64_str = base64.b64encode(image_data).decode('utf-8') + content_type = img_response.headers.get('Content-Type', '') + if content_type: + image_format = content_type.split('/')[-1] + else: + image_format = file_url.split('.')[-1] + + base64_str = base64.b64encode(image_data).decode('utf-8') + + return base64_str, image_format + except asyncio.TimeoutError: + raise Exception(f"下载图片超时, URL: {download_url}") + except aiohttp.ClientError as e: + raise Exception(f"下载图片网络错误: {str(e)}, URL: {download_url}") + except Exception as e: + raise Exception(f"获取图片失败: {str(e)}") from e - return base64_str, image_format async def get_wecom_image_base64(pic_url: str) -> tuple[str, str]: """ diff --git a/templates/platform.json b/templates/platform.json index 8e8e73ef..2cccf058 100644 --- a/templates/platform.json +++ b/templates/platform.json @@ -64,6 +64,7 @@ "adapter": "gewechat", "enable": false, "gewechat_url": "http://your-gewechat-server:2531", + "gewechat_downloadImage_port": 2532, "port": 2286, "callback_url": "http://your-callback-url:2286/gewechat/callback", "app_id": "", diff --git a/templates/schema/platform.json b/templates/schema/platform.json index c33907c1..2f1114bd 100644 --- a/templates/schema/platform.json +++ b/templates/schema/platform.json @@ -325,6 +325,11 @@ "default": "", "description": "gewechat 的 url" }, + "gewechat_downloadImage_port": { + "type": "integer", + "default": 2532, + "description": "gewechat 下载图片的端口" + }, "port": { "type": "integer", "default": 2286,