Skip to content

fix(wechat-official): 修复临时素材上传时 img 媒体类型错误及解构崩溃 - #448

Open
VincentZyu233 wants to merge 4 commits into
satorijs:mainfrom
VincentZyu233:fix/wechat-official-media-type-img
Open

VincentZyu233 wants to merge 4 commits into
satorijs:mainfrom
VincentZyu233:fix/wechat-official-media-type-img

Conversation

@VincentZyu233

@VincentZyu233 VincentZyu233 commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

问题描述

在 @satorijs/adapter-wechat-official 中,当插件通过 h.image(...) 发送图片时(生成 <img ...> 元素,element.type === 'img'),微信公众号被动回复无法发出图片并报错崩溃。

根因分析

  1. 媒体类型未做映射:
    在 uploadMedia(element) 中,代码写为:
    const uploadType = element.type === 'audio' ? 'voice' : element.type
    仅对 audio -> voice 进行了转换,而未将 element.type === 'img' 转换为微信要求的 image。
  2. 微信官方 API 拒绝上传:
    微信公众平台临时素材上传接口(/cgi-bin/media/upload)仅支持 image、voice、video、thumb,不支持 img。当传入 type=img 时,微信直接返回错误:
    { "errcode": 40004, "errmsg": "invalid media type hint: ..." }
    并且不返回 media_id,导致 uploadMedia 返回 undefined。
  3. 未捕获异常导致会话挂起超时:
    在 flushMedia(element) 中:
    const [media] = await this.uploadMedia(element)
    直接对 undefined 进行数组解构,瞬间抛出 TypeError: (intermediate value) is not iterable 未捕获异常;导致本次 Webhook 请求未在 5 秒内返回 XML 响应,触发超时无应答。

修复方案

  1. 类型归一化:在 uploadMedia 中增加对 img -> image 的类型转换;
  2. 防御性检查:在 flushMedia 中对 uploadMedia 返回值进行存在性校验,避免解构 undefined 引发进程异常;
  3. 完善错误日志与重试机制:在素材上传失败时记录告警,兼容 string 格式响应,并在 40001 Token 失效时自动刷新凭据重试;
  4. 超链接与未知元素支持:在 visit 中支持 type === 'a' 并生成合规 HTML 标签,对未知元素 fallback 递归渲染兜底。

Co-authored-by: gemini-code-assist <200291788+gemini-code-assist@users.noreply.github.com>
@VincentZyu233 VincentZyu233 changed the title fix(wechat-official): normalize img type to image and handle upload failure fix(wechat-official): 修复临时素材上传时 img 媒体类型错误及解构崩溃 Nov 6, 2026
VincentZyu233 and others added 3 commits November 6, 2026 17:39
Co-authored-by: gemini-code-assist <200291788+gemini-code-assist@users.noreply.github.com>
Co-authored-by: gemini-code-assist <200291788+gemini-code-assist@users.noreply.github.com>
Co-authored-by: gemini-code-assist <200291788+gemini-code-assist@users.noreply.github.com>
@VincentZyu233
VincentZyu233 marked this pull request as ready for review November 7, 2026 05:56
@VincentZyu233

VincentZyu233 commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor Author

实机验证通过 (Verified in Production)

经过真实微信公众号环境实机测试,本次 PR 的各项功能均已验证生效:

  1. 语音与图片临时素材收发(Media Upload & Reply):

    • 语音指令(如 fa 来)顺利生成临时素材并下发(1" 语音);
    • 图片指令(如 fa.list)将 uploadType === 'img' 规整为 image,微信素材接口返回正常并被动下发,不再报 40004 invalid media type hint;
    • 防御性解构避免了异常时的进程崩溃。
  2. 超链接与小程序卡片跳转(Rich Text & Miniprogram Link):

    • 支持解析 type === 'a' 属性并合规渲染,微信客户端内可直接识别并唤起小程序;
    • 未知复合元素降级递归渲染,防止消息内容被截断丢失。

实机测试截图

1. 文本 / 图片 / 语音 实机测试效果

文本、图片与语音实机测试

2. 超链接与小程序跳转 实机测试效果

超链接与小程序跳转实机测试

@VincentZyu233

VincentZyu233 commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor Author

💡 核心改动依据与业界成熟实践参考

补充说明本 PR 的核心改动依据。除已通过真实微信公众号实机测试验证外,本 PR 的设计亦严格遵循微信官方规范并对齐业界成熟微信 SDK 的最佳实践:

1. 媒体类型映射 (img -> image)

  • 微信官方规范:微信公众平台临时素材上传接口(/cgi-bin/media/upload)仅支持 image、voice、video、thumb 四种类型。由于 Satori 渲染出的图片元素类型是 img,直接透传会触发微信报错 {"errcode": 40004, "errmsg": "invalid media type hint"}。
  • 成熟开源 SDK 实现:
    在权威微信开源 SDK wechatpy 中,媒体上传类型同样严格限定为 image,详见代码实现:
    👉 wechatpy@a695b89: wechatpy/client/api/media.py#L13-L24

2. Access Token 40001 异常自愈与自动重试机制

  • 业务场景:在多进程/多实例运行、密钥手动重置或本地 2 小时缓存偶发与微信服务端失效状态不同步时,上传接口可能遇到 errcode === 40001(invalid credential / access_token expired)。若无自愈机制,会导致当次图片上传与发送直接失败中断。
  • 成熟开源 SDK 实现:
    业界标杆 SDK 普遍在请求层/素材层内置了 40001 错误拦截、主动刷新 Token 并无缝重新发起请求的设计,例如 wechatpy 的核心重试逻辑:
    👉 wechatpy@a695b89: wechatpy/client/base.py#L118-L129

本 PR 在保证架构简洁克制的前提下,完成了上述关键兼容与容错,保证了适配器在生产环境下的长期稳定运行。

@VincentZyu233
VincentZyu233 force-pushed the fix/wechat-official-media-type-img branch 2 times, most recently from 7381ba1 to 8a189fd Compare November 7, 2026 06:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant