Wechat Converter

by davidlam-oss
5
4
3
2
1
Score: 36/100

Description

This plugin has not been manually reviewed by Obsidian staff. Convert Markdown into polished WeChat articles with live preview, copy, and draft sync.

Reviews

No reviews yet.

Stats

stars
8,323
downloads
0
forks
25
days
NaN
days
NaN
days
0
total PRs
0
open PRs
0
closed PRs
0
merged PRs
0
total issues
0
open issues
0
closed issues
0
commits

Latest Version

Invalid date

Changelog

README file from

Github

简体中文 | English

📝 微信公众号排版转换器 (WeChat Converter)

English summary

WeChat Converter is an Obsidian plugin for writers who publish Markdown notes to WeChat Official Accounts and other Chinese content platforms. It provides live preview, WeChat-ready HTML conversion, rich HTML copy, WeChat draft sync, local image processing, math and diagram export, AI-assisted layout, and optional multi-platform draft delivery through the Obsidian Publisher companion browser extension.

让技术写作回归优雅与纯粹。

一款专为 Obsidian 打造的公众号与多平台发布增强插件。它不仅仅是一个转换工具,更是您内容创作流中的"数字化妆师"。我们解决了 Obsidian 到微信公众号排版的"最后一公里"问题,也把同一篇文章继续送往知乎、掘金、CSDN 等内容平台,让您专注于内容创作,无需为繁琐的格式调整和重复发布而分心。

只需一键,即可将您的 Markdown 笔记转换为符合微信生态美学、阅读体验极佳的 HTML;也可以在发布窗口选择其他平台,通过 Obsidian 发布助手浏览器插件保存为各平台草稿。无论是代码块、引用、列表、本地图片还是公式图表,都尽量保持从 Obsidian 到发布端的完整呈现。

Version Obsidian License Chrome Companion

本项目基于开源项目 ai-writing-plugins 进行深度重构与迭代开发。我们致力于打造 Obsidian 生态中体验最好的公众号排版工具。

如果这个插件帮你节省了公众号排版、复制或同步草稿箱的时间,欢迎支持项目继续维护

🔐 隐私与权限说明

插件默认在你的 Obsidian 本地运行,不包含客户端遥测,也不会自动上传你的笔记内容。以下能力只会在你主动使用对应功能时触发:

  • 网络请求:同步微信公众号草稿时会访问微信官方 API;配置 API 代理时会访问你填写的代理地址;使用 AI 编排时会访问你配置的 AI Provider;使用多平台发布时会连接本机浏览器插件服务。
  • 本地文件读取:处理当前笔记引用的本地图片、封面和 Mermaid / LaTeX 导出资源时,会读取必要的 vault 内文件。
  • 剪贴板:点击复制按钮时,插件会把当前预览内容写入系统剪贴板,便于粘贴到微信公众号后台。
  • 第三方账号:微信公众号同步需要你自行配置 AppID / AppSecret;其他平台发布由「Obsidian 发布助手」浏览器插件使用你浏览器中已有的登录态保存草稿。
  • Pro / 浏览器插件:多平台发布和 Pro 授权能力由配套浏览器插件处理;Obsidian 插件侧负责写作、排版、平台选择和任务投递。

🚀 v2.8.3 新功能:草稿更新与素材库封面

v2.8.3 让微信草稿同步更接近真实发文流程:同一篇 Obsidian 笔记再次同步到同一个公众号账号时,可以更新已关联的微信草稿,减少重复草稿;发布窗口也支持直接从微信素材库选择封面,并在多平台分发时保持封面一致。

  • 保留草稿关联:同步成功后会记录当前笔记与微信草稿的关联;后续修改文章再次同步时,会优先更新这篇草稿,而不是每次新建一篇。
  • 可控取消关联:发布窗口会显示已关联草稿状态;需要重新创建草稿时,可以取消关联,操作前会有确认,避免误点。
  • 微信素材库封面:封面图可以从本地上传,也可以从微信素材库选择。素材库选择器支持列表缓存、刷新和更稳定的加载状态。
  • 多平台封面一致:当你选择微信素材库图片作为封面时,发送到浏览器插件的多平台任务会携带同一张封面,避免其他平台收到空封面或不同封面。
  • 任务列表修复:修复 Markdown 任务列表 [ ] / [x] 在微信输出中的显示问题,包含 AI 布局结果缓存中的任务列表标记。

English overview

WeChat Converter is an Obsidian plugin for writers who publish Markdown notes to WeChat Official Accounts and other Chinese content platforms. It provides live preview, WeChat-ready HTML conversion, rich HTML copy, WeChat draft sync, local image processing, math and diagram export, AI-assisted layout, and optional multi-platform draft delivery through the Obsidian Publisher companion browser extension.

Full English documentation is available in README.en.md.

Privacy and permissions

The plugin does not include client-side telemetry and does not automatically upload your notes. The following capabilities are used only when you explicitly run the related feature:

  • Network access and fetch() calls: WeChat draft sync calls the official WeChat API, custom API proxy settings call the proxy URL you configure, AI layout calls the AI Provider you configure, and multi-platform delivery connects to the local companion browser extension service.
  • Local filesystem access: The plugin reads local vault files only when processing images, covers, Mermaid exports, LaTeX exports, and other assets referenced by the current note.
  • Clipboard access: Copy actions write the current rendered article to the system clipboard so you can paste it into the WeChat editor or another publishing surface.
  • Third-party accounts: WeChat sync requires your own AppID and AppSecret. Other platforms are handled by the companion browser extension using the login state already present in your browser.
  • Companion browser extension: Multi-platform publishing and Pro licensing are coordinated with Obsidian Publisher. The Obsidian plugin keeps the writing, rendering, platform selection, and task handoff inside your vault.

🚀 v2.8.0 新功能:多平台一键分发

现在,插件不再只服务于微信公众号。你可以在同一个"发布与分发"窗口里,把同一篇文章继续投递到知乎、掘金、CSDN、小红书、头条号等 20+ 内容平台的草稿箱,由配套的「Obsidian 发布助手」浏览器插件驱动。

  • 微信仍走官方 API:公众号草稿箱继续使用插件自己的 AppID / AppSecret 同步链路,保留封面、摘要、多账号和发布默认值。
  • 其他平台走浏览器插件:知乎、掘金、CSDN 等平台通过「Obsidian 发布助手」使用浏览器登录态保存为草稿,不需要在 Obsidian 里重复登录每个平台。
  • 发布前可选平台:在发布弹窗中切换到“其他平台”,勾选目标平台后发送到浏览器插件。
  • 轻量状态提示:设置页和发布窗口会显示桥接连接状态、已选平台和上次检测到的登录状态,最终结果以浏览器插件任务窗口为准。
  • 适合多渠道创作者:同一篇 Obsidian 笔记可以先发公众号,再同步到多个内容平台草稿箱,最后分别检查排版并手动发布。

💡 核心升级点 (Key Highlights)

相较于原版,我们重写了核心渲染逻辑并新增了大量实用功能,旨在实现真正的**"所见即所得"**:

  1. 🌐 多平台发布 (Multi-platform Publishing) ⭐ v2.8.0 新增

    • 一处写作,多处分发:在 Obsidian 中完成写作和排版后,可继续发送到知乎、掘金、CSDN、语雀、小红书等平台草稿箱。
    • 「Obsidian 发布助手」浏览器插件接管平台登录态:其他平台通过浏览器已有登录态保存草稿,Obsidian 不内嵌平台登录,也不接管 Cookie。
    • 微信与其他平台分工清晰:微信公众号继续使用插件自己的官方 API 链路,其他平台走「Obsidian 发布助手」浏览器插件链路。
    • 发布窗口直接选择平台:在“发布与分发”窗口切换到“其他平台”,勾选平台后发送到浏览器插件。
    • 任务结果回到插件查看:草稿链接、失败原因和单平台重试由 「Obsidian 发布助手」任务窗口处理,减少 Obsidian 里的等待和阻塞。
  2. ➗ 完美支持数学公式 (Math Support) ⭐ v2.1 新增

    • LaTeX 全面支持:直接书写 $E=mc^2$$$...$$,所见即所得。
    • 纯矢量 SVG 渲染:采用独家无缓存技术,将公式转为独立的 SVG 矢量图,无论放大多少倍都清晰锐利。
    • 抗清洗:完美抵抗微信公众号的样式清洗,不再出现乱码或被吞掉的情况。
  3. 📊 Mermaid 图表支持

    • 沿用 Obsidian 原生渲染:在 Obsidian 里能正常显示的 Mermaid 图表,会在插件预览中继续显示。
    • 导出自动转 PNG:复制到公众号或同步到草稿箱时,会自动将 Mermaid 图表栅格化为 PNG,避免 SVG 过长导致微信拦截。
    • 保留图表原色:导出时不会套用数学公式的改色逻辑,尽量保持 Mermaid 主题与连线配色。
  4. 🚀 一键同步到微信草稿箱 (v2.2 增强)

    • 极速并发上传:图片上传速度提升 300%,支持多线程并发处理,大图文章秒传。
    • 智能重试机制:自动处理网络抖动与 Token 过期,全程零人工干预,稳如泰山。
    • 实时进度反馈:新增精确的上传进度条 (e.g., "3/12"),让等待不再焦虑。
    • 安全防重:严格的幂等性设计,杜绝因网络超时产生的重复草稿。
    • 告别复制粘贴:直接将文章同步到微信公众号后台草稿箱,图片自动上传。
    • 智能封面处理:如果你的文档头部信息(frontmatter)里写了 cover,会优先用它做封面;没写就自动用正文第一张图,也可以手动换图。
    • 草稿更新:同一篇笔记再次同步到同一个公众号账号时,优先更新已关联草稿,避免后台堆出重复草稿。
    • 素材库封面:发布窗口支持从微信素材库选择封面;选择素材库封面后,多平台分发也会尽量携带同一张封面。
    • 智能摘要辅助:如果 frontmatter 里写了 excerpt,会优先用它;没写就自动截取前 45 个字,也可以手动改。
    • 同步后自动清理(可选):发送成功后,可以自动删除你在设置里指定的目录(默认关闭)。
    • 目录支持变量:清理目录支持 {{note}},会自动替换成当前文档名,例如 published/{{note}}_img
    • 可回收:支持优先移动到系统回收站,误删可恢复。
    • 多账号管理:支持最多 5 个公众号账号配置,快速切换同步。
    • 账号级发布默认值:可为每个公众号账号单独设置默认原文链接、留言开关和“仅粉丝可留言”。
    • Cloudflare 代理支持:解决 IP 白名单频繁变化问题,查看部署指南
  5. 🎛 全新可视化设置面板 (Settings Panel)

    • 告别繁琐的代码修改!我们内置了直观的设置面板,让您可以实时调整字体、字号、主题色等参数,一切尽在掌握。
  6. 🎨 三大专家级主题 (Premade Themes)

    • 内置 简约 (Simple)经典 (Classic)优雅 (Elegant) 三款精心设计的主题,覆盖从技术博客到人文随笔的各种场景。
    • 引用块支持更克制的中性灰样式,减少大面积主题色对正文阅读的干扰。
    • Callout 支持按语义类型高亮,如 notetipwarningdanger 等;未知类型会自动回退为信息类样式。
  7. 🖼️ 强大的本地图片支持 (Local Image Support)

    • 打破图床限制:完美支持 Obsidian 的本地图片引用(包括 ![[Wiki Link]]![]())。
    • 头像上传:支持直接上传本地图片作为作者头像,插件会自动转码为 Base64。
    • 强大的本地图片支持:无论是相对路径、绝对路径还是 WikiLink,都能自动识别并压缩。
    • GIF 动图支持:针对 GIF 格式特别优化,自动绕过压缩流程,完美保留完整动画帧。
    • 图片左右滑动查看:多张图片可以组织成横向滑动图片块,适合步骤截图、对比图、长图拆分和敏感图片提示。
    • 任务列表兼容:Markdown 任务列表会转换为微信更稳定的勾选标记,避免 [ ] / [x] 泄漏到最终文章。
    • 温馨提示:建议图片(尤其是 GIF)保持在 10MB 以内,以获得最佳的处理速度和公众号兼容性。超过 10MB 时插件会弹出提醒。
  8. ⚡️ 实时渲染预览 (Live Preview)

    • 右侧预览区实现了毫秒级响应的实时渲染。您在左侧 Markdown 编辑的每一个字符,都会即时反馈在右侧的公众号预览视图中。
    • 📱 双模预览 (Dual Mode Preview) ⭐ v2.2 新增
      • 手机仿真模式 (默认):提供逼真的 iPhone X 边框与刘海屏效果,支持深色模式适配,还原最真实的读者视角。
      • 经典全宽模式:在设置中一键切换回无边框全屏预览,利用屏幕每一寸空间。
    • ↕️ 双向同步滚动 (Bidirectional Sync):无论是在左侧编辑还是右侧浏览,另一侧都会如影随形,精准对齐。
  9. 💻 Mac 风格代码块与样式还原

    • 重新设计了代码块样式,支持 macOS 窗口风格及行号显示。
    • 1:1 完美还原:我们在 Obsidian 预览区看到的样式(包括间距、颜色、边框、阴影),复制到微信后台后将分毫不差
    • 宽表格横向滑动:列数较多或内容较长的表格会自动保持可左右滑动,避免在手机端被强行压缩或截断。

📖 使用方法

  1. 唤起插件

    • 点击 Obsidian 左侧边栏的 🪄 图标 (WeChat Converter)。
    • 或使用命令面板 (Cmd/Ctrl + P) 搜索并执行 "Open Wechat Converter"。
  2. 预览与调整

    • 插件会自动加载当前激活的笔记内容。
    • 在右侧面板中,您可以实时预览排版效果。
  3. 一键复制

    • 确认预览效果满意后,点击底部的 [📋 复制到公众号] 按钮。
    • 提示"已复制"后,直接在微信公众号后台编辑器中 Ctrl/Cmd + V 粘贴即可。
  4. 一键同步到微信草稿箱 ⭐ 新功能

    • 先在插件设置里填好公众号账号(AppID / AppSecret)。
    • 点击 [🚀 一键同步],选择账号后即可发送。
    • 每个公众号账号都可以单独配置发布默认值:
      • 默认原文链接
      • 默认开启留言
      • 默认仅粉丝可留言
    • 默认值读取规则:
      • 摘要优先读 excerpt
      • 封面优先读 cover
      • cover_dir 用于同步成功后的“自动清理目录”判断,不作为封面图来源
      • 如果没有,就自动回退到插件默认逻辑(摘要自动截取、封面取正文首图)
    • 你在弹窗里手动上传封面、手动改摘要,始终优先于自动值。
    • 如果当前笔记已经关联过微信草稿,再次同步会优先更新原草稿;需要新建时可在发布窗口取消关联。
    • 如果封面已经在微信素材库里,可以直接点击“从素材库选择”,不用重复上传本地文件。
    • 发送成功后,文章会出现在公众号后台草稿箱。
  5. 发送到其他内容平台 ⭐ v2.8.0 新增

    • 先安装 Obsidian 发布助手浏览器插件,并确保目标平台已经在浏览器里登录。
    • 在插件设置 → 其他平台 → 开启「启用浏览器插件发布」,填入浏览器插件中显示的连接令牌。
    • 点击“测试连接”,确认 Obsidian 能连接到浏览器插件。
    • 在设置页勾选想同步的平台;需要时可点击“读取已选平台状态”。
    • 回到转换器,点击 发布与分发,切换到 其他平台
    • 勾选知乎、掘金、CSDN 等目标平台后,点击 发送到浏览器插件
    • 如果微信草稿箱页选择的是素材库封面,插件会把这张封面一并发送给浏览器插件,尽量保证各平台草稿封面一致。
    • Obsidian 会把当前文章投递给 Obsidian 发布助手;后续草稿链接、失败原因和单平台重试请在浏览器插件任务窗口中查看。

横向滑动内容

宽表格左右滑动

当 Markdown 表格列数较多、单元格内容较长,或在手机预览中超过正文宽度时,插件会自动给表格加上横向滚动容器。你不需要额外写语法,正常写 Markdown 表格即可:

| 指标 | 第一季度 | 第二季度 | 第三季度 | 第四季度 | 备注 |
| --- | --- | --- | --- | --- | --- |
| 转化率 | 12.4% | 15.8% | 18.1% | 21.6% | 适合保留完整列宽 |

预览、复制到公众号、同步到草稿箱时,超宽表格都会尽量保持“左右滑动查看”,避免被压成很窄的列。

图片左右滑动

如果你想让多张图片横向排列,并在预览或微信公众号里左右滑动查看,可以使用 Obsidian Callout 写法:

> [!image-swipe] 左右滑动查看图片
> ![[步骤一.png]]
> ![[步骤二.png]]
> ![步骤三](https://raw.githubusercontent.com/davidlam-oss/obsidian-wechat-converter/HEAD/attachments/step-3.png)

如果第一屏需要先展示敏感图片提示,可以使用:

> [!image-sensitive] 此类图片可能引发不适,向左滑动查看
> ![[图片一.png]]
> ![[图片二.png]]

也可以先选中多行图片语法,再打开命令面板 (Cmd/Ctrl + P) 运行 插入图片块插入敏感图片块,插件会自动帮你包裹成对应的 Callout。

AI 编排(实验功能)

  • 入口有两处:
    • 插件设置里的 AI 编排:用于配置 Provider、默认布局、默认颜色和缓存策略
    • 转换器顶部工具栏里的 AI 编排:用于对当前文章生成和应用版式
  • 当前支持 3 类 Provider 接口:
    • OpenAI 兼容接口
    • Gemini 兼容格式
    • Anthropic 兼容格式
  • 内置 3 套布局家族:
    • 原文增强型:最接近普通预览,正文连续性更强
    • 教程卡片型:更适合步骤拆解、清单、案例说明
    • 轻杂志型:更强调留白、图文节奏和编辑感
  • 可以选择 自动配色,也可以在生成前手动指定颜色方案。
  • 生成后可以继续手动切换颜色方案,复用当前布局结构,不必每次都重新生成。
  • 每个布局家族会保留当前文章最新的一份缓存;重新打开侧边栏后,可以直接应用匹配当前文章的缓存结果。
  • 面板里可以直接看:
    • schema 校验提醒
    • 布局 JSON
    • 错误详情
    • 复制给 AI 的排查 Prompt
    • 当前 JSON 或错误详情
  • 如果重新生成失败,上一版成功结果仍然可以保留,方便你继续预览和比较。

同步后自动清理(可选)

  • 入口:插件设置 → 高级设置
  • 默认是关闭的。
  • 只有“发送到微信草稿箱成功”后,才会触发清理。
  • 清理的是你在设置里填的目录(删目录,不是删单个文件)。
  • 目录支持 {{note}},会自动替换成当前文档名。
  • 清理目录请填写 vault 内相对路径(不要填 /Users/...C:\\... 这类绝对路径)。
  • 清理成功后,如果文档里的 cover / cover_dir 指向这个已删除目录,插件会自动清空这些失效字段。
  • 建议开启“使用系统回收站”,这样删错也能找回。
  • 示例:published/{{note}}_img
    如果当前文档名是 post,实际删除目录是 published/post_img

Mermaid 图表导出说明

  • 在 Obsidian 预览里能正常显示的 Mermaid 图表,会尽量在插件预览里继续保持可见。
  • 当你执行“复制到公众号”或“一键同步到草稿箱”时,插件会自动把 Mermaid 图表栅格化为 PNG。
  • 这样做的目的不是改变样式,而是为了减少微信公众号对长 SVG、复杂 SVG 的清洗和拦截问题。
  • 导出链路会尽量保留 Mermaid 原本的颜色,不会套用数学公式那一套 SVG 清理逻辑。

✍️ 中文标点标准化规则

插件支持在右侧排版 setting panel 中开启或关闭“正文标点标准化”:

  • 入口:右侧排版 setting panel正文标点标准化
  • 作用范围:仅作用于预览 / 复制 / 同步结果
  • 不会修改原始 Markdown 文件

已支持的替换规则

中文语境下,插件会将常见英文半角标点替换为中文全角标点:

  • ,
  • .
  • :
  • ?
  • !
  • ;
  • "..."“...”
  • (...)(...)

已保护的场景

以下内容会尽量保持原样,避免误伤技术文档:

  • 行内代码与代码块,例如 `content````ts ... ```
  • URL 与邮箱,例如 https://example.com/a?b=1[email protected]
  • 常见技术性括号表达式、数学记号、函数/调用语法,例如 公式(x+y)矩阵(A,B)foo(bar, baz)f(x, y)
  • 英文原句与数字小数点,例如 Version 2.1 is stable.
  • 文件名、相对路径、Windows 路径,例如 README.mdtests/foo.test.jsC:\Users\name\demo.md
  • 命令行参数与脚本名,例如 --runtest:coverage
  • 环境变量赋值,例如 NODE_ENV=production
  • 日期时间,例如 2026-03-09 09:15:112026/02/13 23:00
  • Markdown 结构本身,例如表格分隔符、代码围栏、链接语法等

当前边界

  • 这是规则驱动的标准化能力,不依赖模型。
  • 它适合做“发布前质检”和“统一格式”,不适合做语义判断。
  • ... / ...... 这类省略号写法会保留原样,不会强制改写。
  • 像“并列词语之间把逗号改成顿号 ”这类规则目前没有启用,因为容易误改,后续如果要做,更适合放到 AI 精修能力里。

🎨 引用与 Callout 样式

  • 入口:右侧排版 setting panel → 引用 / Callout 样式
  • 现在支持两种整体模式:
    • 跟随主题
    • 中性灰(推荐)
  • 中性灰 模式更适合长文阅读,会明显减弱大面积主题色的存在感。
  • Callout 会按语义类型自动着色,例如 notetipwarningdanger
  • 对于插件还不认识的 Callout 类型,会统一回退为信息类样式,而不是随机继承当前主题色。

🔧 代理设置(解决 IP 白名单问题)

微信公众号 API 需要 IP 白名单验证。如果你使用 VPN 或动态 IP,可以通过 Cloudflare Worker 代理解决。

部署步骤

  1. 创建 Cloudflare Worker

    • 登录 Cloudflare Dashboard
    • 左侧菜单选择 Workers & PagesCreate ApplicationCreate Worker
    • 命名(如 wechat-proxy)并点击 Deploy
  2. 编辑 Worker 代码

    点击 Edit code,替换为以下代码:

    export default {
      async fetch(request, env) {
        const MAX_UPLOAD_BYTES = 10 * 1024 * 1024;
        const ALLOWED_METHODS = new Set(['GET', 'POST', 'UPLOAD']);
        const corsHeaders = {
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Methods': 'POST, OPTIONS',
          'Access-Control-Allow-Headers': 'Content-Type',
        };
    
        const jsonResponse = (payload, status = 200) =>
          new Response(JSON.stringify(payload), {
            status,
            headers: {
              ...corsHeaders,
              'Content-Type': 'application/json; charset=utf-8',
            },
          });
    
        const isAllowedWechatUrl = (rawUrl) => {
          try {
            const parsed = new URL(rawUrl);
            return parsed.protocol === 'https:' && parsed.hostname === 'api.weixin.qq.com';
          } catch {
            return false;
          }
        };
    
        const toUint8Array = (base64) => {
          const binary = atob(base64);
          const bytes = new Uint8Array(binary.length);
          for (let i = 0; i < binary.length; i++) {
            bytes[i] = binary.charCodeAt(i);
          }
          return bytes;
        };
    
        // 处理 CORS 预检请求
        if (request.method === 'OPTIONS') {
          return new Response(null, { headers: corsHeaders });
        }
    
        if (request.method !== 'POST') {
          return jsonResponse({ error: 'Method Not Allowed' }, 405);
        }
    
        try {
          const body = await request.json();
          const { url, method = 'GET', data, fileData, fileName, mimeType } = body;
          const normalizedMethod = String(method || 'GET').toUpperCase();
          
          // 安全校验:只允许访问微信 API
          if (typeof url !== 'string' || !isAllowedWechatUrl(url)) {
            return jsonResponse({ error: 'Invalid URL. Only https://api.weixin.qq.com/ is allowed.' }, 400);
          }
    
          if (!ALLOWED_METHODS.has(normalizedMethod)) {
            return jsonResponse({ error: 'Invalid method. Only GET, POST, and UPLOAD are allowed.' }, 400);
          }
    
          let response;
          if (normalizedMethod === 'UPLOAD') {
            if (typeof fileData !== 'string' || fileData.length === 0) {
              return jsonResponse({ error: 'Missing fileData for upload.' }, 400);
            }
    
            const approxBytes = Math.floor(fileData.length * 3 / 4);
            if (approxBytes > MAX_UPLOAD_BYTES) {
              return jsonResponse({ error: 'Upload too large. Maximum size is 10 MB.' }, 413);
            }
    
            const safeMimeType =
              typeof mimeType === 'string' && mimeType.startsWith('image/')
                ? mimeType
                : 'application/octet-stream';
            const safeFileName =
              typeof fileName === 'string' && /^[\w.\-]+$/.test(fileName)
                ? fileName
                : 'image';
    
            // 文件上传处理:Base64 -> Binary -> FormData
            const bytes = toUint8Array(fileData);
            const formData = new FormData();
            formData.append('media', new Blob([bytes], { type: safeMimeType }), safeFileName);
            response = await fetch(url, { method: 'POST', body: formData });
          } else {
            // 普通 JSON 请求
            const opts = { method: normalizedMethod };
            if (normalizedMethod === 'POST') {
              opts.headers = { 'Content-Type': 'application/json' };
              if (data !== undefined) opts.body = JSON.stringify(data);
            }
            response = await fetch(url, opts);
          }
    
          const responseText = await response.text();
          let result;
          try {
            result = responseText ? JSON.parse(responseText) : {};
          } catch {
            return jsonResponse({ error: 'Upstream returned a non-JSON response.' }, 502);
          }
    
          return jsonResponse(result, response.status);
        } catch (error) {
          return jsonResponse({ error: 'Proxy request failed.' }, 500);
        }
      }
    };
    

    这个版本比旧示例更适合直接公开在文档里:

    • 不记录请求 URL,避免把 appidsecretaccess_token 写进日志。
    • 不回显原始请求体和异常堆栈,减少敏感信息暴露。
    • 只允许 GETPOSTUPLOAD,并且只允许访问 https://api.weixin.qq.com/
    • 上传大小限制为 10 MB,避免异常大文件拖垮 Worker。
    • 上传字段固定为 media,与插件当前行为保持一致。
  3. 配置微信 IP 白名单

    将 Cloudflare 出口 IP 添加到微信公众号白名单(官方 IP 列表):

    173.245.48.0/20, 103.21.244.0/22, 103.22.200.0/22, 103.31.4.0/22
    141.101.64.0/18, 108.162.192.0/18, 190.93.240.0/20, 188.114.96.0/20
    197.234.240.0/22, 198.41.128.0/17, 162.158.0.0/15, 104.16.0.0/13
    104.24.0.0/14, 172.64.0.0/13, 131.0.72.0/22
    
  4. 插件配置

    在插件设置 → 高级设置API 代理地址 中填入你的 Worker URL:

    https://wechat-proxy.your-account.workers.dev
    

    该代理仅建议自用,请不要公开分享 Worker 地址,也不要将其部署为公共服务。

🚀 安装

  1. GitHub Releases 下载最新的 obsidian-wechat-converter.zip 插件包。
  2. 解压并将其中的文件夹放入 Obsidian vault 的 .obsidian/plugins/ 目录中。

    最终路径应为:.../.obsidian/plugins/obsidian-wechat-converter/

  3. 确保文件夹内至少包含以下文件(三件套运行时):
    • main.js
    • manifest.json
    • styles.css
  4. 重启 Obsidian 或在设置中刷新插件列表,并启用插件。

BRAT 安装/更新

如果你使用 BRAT 管理插件更新:

  1. 安装并启用 BRAT 插件。
  2. 在 BRAT 中添加仓库:DavidLam-oss/obsidian-wechat-converter
  3. 安装后执行一次冒烟检查:
    • 打开转换面板
    • 预览渲染
    • 复制到公众号
    • (可选)一键同步到草稿箱

说明:当前版本已支持标准三件套运行时,BRAT 更新路径与 Obsidian 插件标准发布方式一致。

🤝 贡献 (Contributing)

欢迎提交 Issue 或 Pull Request!

  1. Fork 本仓库。
  2. 创建您的特性分支 (git checkout -b feature/AmazingFeature)。
  3. 提交您的更改 (git commit -m 'Add some AmazingFeature')。
  4. 推送到分支 (git push origin feature/AmazingFeature)。
  5. 开启一个 Pull Request。

📄 许可证

本项目采用 MIT License 开源。

👨‍💻 作者

林小卫很行 (DavidLam)

一名热衷于提升生产力的开发者与内容创作者。 如果您在使用过程中有任何问题、建议或发现了 Bug,欢迎随时在 GitHub Issue 区留言反馈。相信工具的力量,让创作更自由。