feat(channels): support WeChat and WeCom image and file attachments - #95
feat(channels): support WeChat and WeCom image and file attachments#95orhuoxu wants to merge 5 commits into
Conversation
|
有一个需要合并前修复的问题: [P1] 微信/企微通过 微信: def download_media(self, context_token: str, media_id: str) -> bytes:
...
return response.content企微: response = client.get(...)
...
return response.content
if not data or len(data) > MAX_CHANNEL_MEDIA_BYTES:但这时整个响应已经通过
建议让这两个 |
|
感谢你的细致审查和问题指正!确实是之前考虑得不够周全,我已经按照你的建议完成修改并补充了相关测试,再次感谢! |
e4a9db6 to
30a204a
Compare
| if len(key) not in {16, 24, 32} or len(data) % 16: | ||
| raise ValueError | ||
| decryptor = Cipher(algorithms.AES(key), modes.ECB()).decryptor() | ||
| padded = decryptor.update(data) + decryptor.finalize() |
| padder = padding.PKCS7(algorithms.AES.block_size).padder() | ||
| padded = padder.update(plaintext) + padder.finalize() | ||
| encryptor = Cipher(algorithms.AES(key), modes.ECB()).encryptor() | ||
| encrypted = encryptor.update(padded) + encryptor.finalize() |
|
2 条 AES-ECB 5 条路径告警 企微 access token 缓存会在同企业的多个机器人之间串用 |
感谢你的细致审查和问题指正!看看还有什么问题吗?感谢🙏 2 条 AES-ECB:代码拿微信下发的 full_url 下载内容,下载的就是微信服务端使用ECB+PKCS#7 生成的媒体密文,为兼容该上游格式进行 AES-ECB/PKCS#7 解密。CodeQL警告我建议按“第三方固定协议兼容性例外”处置 修复内容 路径遍历告警: 企微 access token 缓存串用: 微信附件诊断日志: |
概述
StaffDeck 支持通过微信和企业微信接收入站图片、PDF 以及受支持的文本文件,并将附件接入既有对话链路。
用户可以直接在微信或企业微信会话中发送图片或文件。渠道适配器负责解析消息中的附件元数据、下载和解密媒体;统一附件桥接层负责将媒体暂存、解析为
ChatAttachment,并将其传递给既有的 AgentLoop、会话消息存储和模型输入流程。控制台的渠道会话详情支持图片预览和非图片附件下载。中文文件名通过 UTF-8 文件名参数传递,避免浏览器因响应头编码问题无法下载。
本实现复用 Web 端已有的附件暂存、解析、模型输入和消息元数据能力,不改变 AgentLoop、会话模型和渠道出站投递的核心语义。
支持范围
微信
image_item和file_item。kind=image。kind=file,保留文件名和文件大小。请识别并描述这张图片的内容。请读取并概述这个文件。full_url下载媒体。full_url时,使用官方downloadmedia接口。aiohttp,有限重试和流式读取;httpx回退;curl回退,使用独立 TLS 栈和 HTTP/1.1。curl回退兼容微信 CDN 的连接重置、传输超时以及异常Content-Length。微信 CDN 回退依赖部署环境提供系统
curl。如果系统没有curl,仅能使用前面的 Python HTTP 客户端。企业微信
image消息。file消息。mixed消息中的文本、图片。aeskey解密。media/get下载路径使用 access token 缓存、提前刷新和 token 失效重试。ChannelInboundAttachmentdataclass,保持持久化重放后的附件语义一致。统一入站附件协议
ChannelInbound增加attachments字段,携带当前入站消息的临时附件描述。每个
ChannelInboundAttachment包含:media_id:渠道侧媒体标识或媒体 URL。kind:image或file。filename:渠道提供的文件名或生成的回退文件名。content_type:渠道提供的 MIME 类型或适配器推断值。size:可选的媒体大小信息。download_params:渠道下载所需的临时参数,例如context_token、full_url、aes_key和预期大小。这些下载参数只用于处理当前入站消息,不作为长期对话附件元数据直接暴露给模型或前端。
微信媒体下载
微信图片和文件的
full_url指向微信 CDN,响应是经过加密的媒体内容。下载流程如下:微信媒体 URL 必须满足以下条件:
aiohttp路径使用流式读取和分块大小限制。httpx和系统curl回退路径会在接收响应后执行大小限制,因此部署环境仍应使用可信网络边界,并避免将不受信任的 URL 直接传入适配器。微信 CDN 可能在加密响应中包含尾随字节,或返回与实际传输内容不完全一致的
Content-Length。当前实现允许特定的 curl 传输结果继续进入 AES 解密,最终由解密、预期大小和图片 magic bytes 校验判断内容是否有效。企业微信媒体下载
企业微信 URL 下载流程如下:
企微智能机器人 URL 使用 WebSocket 所属事件循环下载;传统
media/get路径使用缓存的 access token。access token 失效时会强制刷新一次并重试,其他错误直接失败。附件桥接与解析
渠道附件由
inbound_attachments_to_chat统一桥接到 Web 附件管道:FFD9之后的渠道尾随字节。parse_chat_attachment解析附件。stage_chat_attachment将原始数据安全暂存到用户附件目录。sha256和 sandbox 路径的ChatAttachmentRead传递给对话请求。统一附件解析支持:
单个有效渠道附件上限为 25 MB。加密下载会预留 AES 分块所需的密文余量。
会话和模型链路
渠道附件进入既有
ChatTurnRequest.attachments字段,不新增独立的 AgentLoop 分支:附件元数据会随用户消息写入消息
metadata_json。Harness 会将附件物化到任务工作区,并在模型支持视觉输入时将有效图片作为视觉输入传递。如果渠道附件下载或解析失败,当前实现会记录异常并跳过该附件;入站事件本身不保证因此自动重试。此时 Agent 可能只收到默认的附件处理提示,而没有实际附件内容。
控制台
控制台行为:
filename和 UTF-8filename*,支持中文文件名。测试和验证
已覆盖的核心测试包括: