package qq_obv11 // 本文件承载 OneBot v11 的**入站附件字节内联**(切片 IM-1):文件类消息段(数组 // 形态的 `{type:"file",data:{...}}` 与 CQ 码形态的 `[CQ:file,...]`)里,实现方通常 // 会给出一个 `url`(离线文件/群文件的下载地址),字节就照它取回。 // // 三条边界: // - **只认平台系资产主机**:白名单 = 腾讯系 CDN(`*.qq.com`、`*.qq.com.cn`、 // `*.qpic.cn`,覆盖 `multimedia.nt.qq.com.cn` 与 `gchat.qpic.cn`)+ 部署自己 // 声明的 OneBot API 端点(本地实现常自己下发文件 URL)。消息段里的 `url` 是 // 对端上报的内容,白名单是硬门:不在名单里一个字节都不发出去; // - **没有真 url 就不下载**:段里只有文件 id/文件名时(`url` 缺失,`file` 被当作 // 引用写进了 URL 字段),它连绝对 http(s) 地址都不是 ⇒ 白名单直接否决,绝不 // 拼凑地址去猜; // - 尺寸/超时/预筛/日志口径全在 `internal/channel/mediafetch`(四个适配器共用)。 import ( "context" "git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter" "git.yeij.top/AskaEth/Cyrene_v2/internal/channel/mediafetch" ) // 腾讯系资产主机白名单:OneBot 实现上报的文件地址落在这些子树里(`.qq.com` 家族 // 覆盖 `multimedia.nt.qq.com.cn` 之外的多媒体域名,`.qpic.cn` 覆盖图片/文件 CDN)。 // 通配条目**不含**裸的 `qq.com` 本身,因此不会顺带放行 `qq.com` 主页这类无关主机。 const ( qqAssetOriginQQ = "https://*.qq.com" qqAssetOriginQQCN = "https://*.qq.com.cn" qqAssetOriginQpic = "https://*.qpic.cn" ) // inlineInboundMedia 把受支持格式的文件附件取回并内联到消息上。失败不抛出:该附件 // 保持无字节,由 channelbridge 的入站摄取照既有口径给模型一行诚实提示。 func (a *Adapter) inlineInboundMedia(ctx context.Context, msg *adapter.UnifiedMessage) { mediafetch.Inline(ctx, a.log, msg, qqFetcher{a: a}) } // qqFetcher 实现 mediafetch.Fetcher:OneBot 上报的 `url` 就是资产地址。 type qqFetcher struct{ a *Adapter } // Fetch 按来源白名单取回一份文件资产。下载**不复用**出站 HTTP API 的客户端:那个 // 客户端带着 OneBot access_token 头,绝不能把它发到 CDN 主机上,因此这里交给 // mediafetch 自建一个干净客户端。 func (f qqFetcher) Fetch(ctx context.Context, m adapter.Media) ([]byte, error) { return mediafetch.Fetch(ctx, mediafetch.Request{ URL: m.URL, Allow: f.a.assetAllowlist(), Timeout: mediafetch.DefaultTimeout, }) } // assetAllowlist 给出 OneBot 文件资产的来源白名单:腾讯系 CDN + 已配置的 OneBot // API 端点(`api_base_url`,本地部署常是 `http://127.0.0.1:5700` 这类地址 —— 它由 // 部署显式声明,可信度与其余 API 调用一致)。`api_base_url` 为空时只用腾讯系条目。 func (a *Adapter) assetAllowlist() mediafetch.Allowlist { origins := []string{qqAssetOriginQQ, qqAssetOriginQQCN, qqAssetOriginQpic} if origin, err := mediafetch.OriginOf(a.cfg.APIBaseURL); err == nil { origins = append(origins, origin) } return mediafetch.NewAllowlist(origins...) }