初始提交: Qihu-AIFlow v2.0.1 完整项目
- 前端 React+TypeScript (源码+构建产物) - 后端 Python FastAPI - 数据库迁移 001-015 - Docker部署配置 - 微信ClawBot API频道绑定 - 项目文档 README.md
This commit is contained in:
commit
78ff261b5a
199 files changed
+69126
No files matched your search
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
企业微信平台适配器
|
||||
"""
|
||||
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}},
|
||||
}
|
||||
Reference in new issue
Block a user