# 图片输入验收 目标:所有可接收图片的渠道统一使用 DSH Chat 的图片准入规则;已有微信收图行为不退化。不包含 AI 生图或机器人图片出站。 ## 自动化与真实联调的边界 `npm test` 覆盖本地构建、渠道协议夹具、下载失败与网关注入等可重复验证。模拟 SDK / HTTP 响应不是平台真实联调,也不能证明某个实际提供商的视觉推理成功。发布前应使用自己的测试机器人完成以下检查;不要把 token、下载签名 URL、解密密钥或私人图片写进测试报告。 ## 渠道清单 | 渠道 | 图片入口 | 联调要点 | | --- | --- | --- | | 微信 | iLink 图片项 | 扫码身份私聊;保留图片与文字;已有文件/语音不退化 | | 企业微信 | 图片消息、混合消息图片项 | 私聊与群 @;回复仍关联原回调帧 | | 钉钉 | 图片消息、富文本图片项 | 下载凭证换取媒体;原回复机制不退化 | | 飞书 | 图片消息、富文本图片项 | 消息资源下载权限;只 @ 当前机器人时群聊才触发 | | Lark | 与飞书相同 | 独立验证国际版 API 域名与资源权限 | | QQ | 消息图片附件 | 私聊与群 @;非图片文件不冒充图片 | | Telegram | photo、图片 document | caption 保留;相册逐张进入同一会话;群相册仅在同群、同发送者、同相册内共享 @ 准入(30 秒窗口),不放开其他消息 | ## 渠道侧接收预算 为在进入宿主前限制下载开销,渠道可以比 Chat 附件存储更早拒绝过大的消息: - 企业微信、钉钉:单条最多 4 张,单张最多 10 MiB。 - 飞书、Lark、QQ:单条最多 4 张,累计最多 20 MiB,整批下载有超时。 - Telegram:单张最多 10 MiB;群相册最多保留 20 个成员的短期准入状态,最多缓存 64 个相册。 - 微信保留原 iLink 下载路径及单媒体 50 MiB 限制;宿主仍可能按自身限制拒绝。 这些是插件下载预算,不代表平台官方上限。宿主最终还会进行图片格式、像素、数量和大小校验。 ## 管理员配置额外图片主机 QQ、企业微信和钉钉支持账号级高级配置 `config.additionalImageHosts`。它是精确主机名的字符串,用逗号、空格或换行分隔,最多 16 个;重复项去重并转为小写。默认不需要填写,只有确认机器人实际返回了新的受信资源域名时才添加。 在停止 DSH 后,备份插件状态目录中的 `channels.json`,只修改 `channels.<账号 ID>.config` 下的这个字段,保留其他设置与凭据引用,然后自行重启。默认状态目录为 `$DSH_HOME/dsh-im-connect`;若配置了 `stateDir`,以该配置为准。以下只是字段示例,必须把示例域名换成实际确认的主机: ```json "additionalImageHosts": "images.example.com, assets.example.com" ``` - 不接受 URL、路径、端口、账号密码、IP、localhost 或通配符;禁止填写 `*.aliyuncs.com`、`*.myqcloud.com`。 - 仅影响当前账号:允许这些精确名字在 Fake-IP `198.18.0.0/15` 环境下载;QQ 同时将它们纳入图片来源名单。钉钉可将其 HTTP 资源地址升级 HTTPS。 - 不关闭 TLS、私网/回环 IP、重定向、大小或超时检查。配置主机仍不能直接解析到 `10.x`、`127.x` 等地址。 - 配置不会从收到的图片链接自动扩展。管理员应先核对资源所属平台/租户,不能将不可信域名当作故障兜底。 - 普通重连保留配置;显式设置空字符串可清空。当前为高级配置字段,普通凭据表单不提供编辑入口。 ## 请求复用与失败重试 - 钉钉 token 缓存属于账号适配器实例,不按 `clientId` 跨账号全局共享;并发请求合并,提前到期刷新,停止/重新连接使旧缓存失效。 - 微信图片只对可恢复的网络/服务端下载失败进行有限重试,最多两次下载尝试;取消、权限拒绝、缺失图片、大小/解密错误不盲目重试。失败不把剩余附言独立作为命令或新任务提交。 - 微信保留协议中的其他 HTTPS `full_url`,不因重试优化而强制单个 CDN;禁止明文、URL 凭据、IP 直连、localhost 和重定向。它仍使用原生 `fetch`,本轮没有将其改造为连接时 DNS 固定解析下载器,不能套用 QQ 下载器的全部安全保证。 ## 模型矩阵 每个渠道均使用已授权的测试用户执行: 1. 当前会话选中支持 `image` 的模型:发送一张图片,再发送带说明的图片。确认只有一次用户消息入站、模型收到图像内容、DSH 历史消息可以显示图片。 2. 发送平台支持的多图消息,确认没有只取第一张或只留下说明文字。 3. 当前会话切换到明确只支持文本的模型:发图应收到明确错误,不得把丢失图片的请求假装成功。纯文字仍可发送。 4. 全局默认模型与会话模型故意设为不同能力,确认使用会话模型的判断。 5. 能力元数据缺失:与 Chat 一样不单凭元数据缺失拒绝;实际模型仍可能拒绝图像请求。 6. 图片提交与模型切换同时发生:不得绕过宿主的图片准入与模型选择串行规则。 7. 重启 DSH 后打开历史频道会话,确认图片不是依赖临时文件路径的失效预览。 ## 失败与安全矩阵 下载错误提示会区分地址校验、HTTP 状态、DNS、超时、大小和解密等原因;本机日志只记录原因分类与必要的地址元数据(主机名、协议、端口、是否含 userinfo),不记录签名 URL、路径、密码或解密密钥。小图片报错不一定是大小超限。 QQ 使用统一的连接时 DNS 校验下载器,支持内置精确媒体域名和管理员配置的额外主机;受信主机的省略协议/HTTP 形式统一使用 HTTPS。代理/TUN 将域名解析为 `198.18.0.0/15` Fake-IP 时,仅对内置或管理员配置的精确主机兼容;其他主机的 Fake-IP、真实私网/回环地址和重定向仍被拒绝,TLS 校验不关闭。 钉钉 API 返回的内置 OSS 主机或账号额外受信主机,其 HTTP 图片链接会在无 userinfo、无非默认端口时升级为 HTTPS,保留原签名查询串;其他 HTTP 域名不会因此被放行。 - 下载失败、过期 URL、无资源权限、损坏图片、大小超限:明确提示;不静默丢弃纯图,不让异常终止整个渠道。 - 图文混合消息中图片失败:明确提示重发完整消息,不把失去图片的附言降级为纯文字命令,也不能让模型误以为已看过图。 - 未授权私聊、群聊未 @ 当前机器人:不能通过图片绕过访问控制。 - 图片附言是 `/new`、`批准` 或 `yes`:不能把附言误当作会话命令或工具批准而吃掉图片。 - 连续发送两张图以及随后一条文字:回复关联原消息,不能因下载耗时不同而错配。 - 旧宿主没有标准图片提交能力:明确说明需要升级,不能伪造原生图片提交成功;已有纯文字会话仍可用。 ## 发布验证记录 ### 当前真实联调状态 以下为开发阶段反馈,不是全渠道验收通过记录。本轮优化未安装到运行实例,也未代替用户启动服务。 | 渠道 | 当前证据 | 本轮优化后的真实验收 | | --- | --- | --- | | QQ | 用户确认前一测试包已恢复 | 安全下载器改动后待复测 | | 飞书 | 用户确认可以收到图片 | 未执行完整模型识图/历史预览验收 | | Lark | 共享源码与 SDK 域名测试通过;用户无测试环境 | 未联调 | | 企业微信 | 已按失败日志修补 COS 域名兼容 | 缺少最终取图确认,待复测 | | 钉钉 | 已修补 OSS Fake-IP 与 HTTP→HTTPS 兼容 | 缺少最终取图确认,token 缓存待复测 | | 微信 | 原有收图链路保留 | 本轮失败分类/重试未联调 | | Telegram | 图片与群相册协议夹具通过 | 未联调 | ### 可选真实宿主契约测试 未设置宿主包路径时,本地 `npm test` 会跳过需要额外宿主包的集成测试;CI 的测试工作流会安装隔离宿主并运行它们。可将以下包安装到独立临时目录(不要为测试改动插件运行时依赖): ```sh npm install --prefix "$CONTRACT_DIR" --no-audit --no-fund \ @deepseek-ai/dsh-api-session-controller@0.1.2-rc.1 \ @deepseek-ai/dsh-attachment-local@0.1.2-rc.1 DSH_CHAT_CONTRACT_ROOT="$CONTRACT_DIR/node_modules/@deepseek-ai/dsh-api-session-controller" \ DSH_ATTACHMENT_CONTRACT_ROOT="$CONTRACT_DIR/node_modules/@deepseek-ai/dsh-attachment-local" \ npm test ``` `CONTRACT_DIR` 应为可写临时目录的绝对路径。第一项运行真实 Chat 准入代码,检查当前会话模型、未知能力元数据和切换模型串行规则;第二项通过真实附件后端解码、存储、重新打开并读取图片,同时验证损坏数据被拒绝。两者仍不调用线上模型或 IM 平台。 记录所用插件版本、DSH 版本、渠道、会话模型、各项通过/失败,以及脱敏后的错误原因。图片与提供商真实联调未执行时必须标记“未联调”,不能用自动化夹具通过替代。