""" 企业微信平台适配器 """ import hashlib import json import logging from typing import Any from ..platform_adapter import register_platform_adapter, BasePlatformAdapter, PlatformMetadata logger = logging.getLogger(__name__) @register_platform_adapter( name="wecom", display_name="企业微信", description="企业微信消息平台适配器,支持文本、图片、文件、语音消息", support_streaming=False, support_image=True, support_file=True, support_voice=True, ) class WecomPlatformAdapter(BasePlatformAdapter): """企业微信平台适配器""" def get_metadata(self) -> PlatformMetadata: from ..platform_adapter import platform_registry return platform_registry["wecom"] def verify_signature(self, payload: dict, signature: str, timestamp: str) -> bool: """企业微信签名验证: SHA1(token + timestamp + nonce)""" if not signature or not timestamp: return True # 无签名要求默认通过 token = self.config.get("token", "") encoding_aes_key = self.config.get("encoding_aes_key", "") nonce = (encoding_aes_key or token or "defaultnonce")[:16] tmp = "".join(sorted([token, timestamp, nonce])) expected = hashlib.sha1(tmp.encode()).hexdigest() return signature == expected def parse_webhook(self, payload: dict) -> dict: """解析企业微信webhook payload为统一消息格式""" msg_type = payload.get("MsgType", "text") content = "" content_type = "text" if msg_type == "text": content = payload.get("Text", {}).get("Content", "") content_type = "text" elif msg_type == "image": content = payload.get("Image", {}).get("MediaId", "") content_type = "image" elif msg_type == "voice": content = payload.get("Voice", {}).get("MediaId", "") content_type = "voice" elif msg_type == "video": content = payload.get("Video", {}).get("MediaId", "") content_type = "video" elif msg_type == "file": content = payload.get("FileName", "") content_type = "file" elif msg_type == "location": loc = payload.get("Location", {}) content = json.dumps(loc, ensure_ascii=False) content_type = "location" elif msg_type == "event": content = json.dumps({"event": payload.get("Event", ""), "event_key": payload.get("EventKey", "")}) content_type = "event" else: content = json.dumps(payload, ensure_ascii=False) content_type = msg_type return { "content_type": content_type, "content": content, "sender_id": payload.get("FromUserName", ""), "sender_name": payload.get("FromUserName", ""), "message_id": str(payload.get("MsgId", "")), "timestamp": int(payload.get("CreateTime", 0)), "group_id": None, "group_name": None, "raw": payload, } def build_reply(self, channel_config: dict, content: str, reply_to: str = None) -> dict: """构建企业微信回复消息""" webhook_key = channel_config.get("webhook_key", "") return { "api_url": f"https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key={webhook_key}", "headers": {"Content-Type": "application/json"}, "body": {"msgtype": "text", "text": {"content": content}}, }