d0u926 downloadsCreate a WeChat Official Account draft from the current Markdown note.
Turn the Markdown note you are editing into a WeChat Official Account draft, formatted with the inline HTML the WeChat editor accepts. Typography comes from dgs-wechat-publisher.
The plugin only ever creates a draft. It never publishes an article and never sends a mass message.
Desktop only, and Chinese-facing by nature: the runtime messages are in Chinese, because the people writing these articles are.
Put your Official Account credentials in a .env file outside your vault:
WECHAT_APP_ID=wx…
WECHAT_APP_SECRET=…
WECHAT_AUTHOR=Your name # optional
In Settings → Community plugins → DGS WeChat Publisher, set Credentials file to that
file's full path — written out in full, since ~ is not expanded — then press Check. This is
the only way to give the plugin credentials: nothing secret is written into your vault.
In the WeChat admin console, whitelist this machine's public IP address and confirm the account has permission to use the material and draft APIs.
Give the note some frontmatter:
---
title: An article
description: The summary readers see in the subscription list
cover: ./images/cover.jpg # optional — you are asked when it is missing
author: A name # optional
lang: zh # zh (default) or en; picks the typography
---
Run 预检并预览当前笔记 (preview) to check the rendering, then 发布当前笔记到公众号草稿箱 (publish) to create the draft.
Images. Standard Markdown () and Obsidian wiki embeds
(![[photo.jpg]]) both work, in the body and for the cover. Relative references (./, ../)
resolve against the note's folder, a leading / against the vault root, and remote http(s) images
are downloaded and re-uploaded. JPEG and PNG under WeChat's 1 MB limit are uploaded untouched, so
quality and transparency survive; anything larger is scaled and re-encoded. Covers are cropped to
the 900×500 thumbnail WeChat wants. The body endpoint takes no GIF, so an animated one is uploaded
as its first frame.
Missing pieces. A note without a title, digest, author or cover is not rejected. The cover picker offers candidates best guess first — the bundle's own images, then the note's pictures, then its folder, then the vault — each with a thumbnail. The confirmation dialog asks for whatever text is still missing, within WeChat's own length limits. With Remember what you fill in enabled (the default), your answers are written back to the note's frontmatter, so the next publish asks nothing.
Bundles. An article can be a folder rather than a single note, matching the layout the base library publishes from the command line:
my-article/
├── index.md shared, language-independent frontmatter — the cover in particular
├── zh.md title and description, which are per-language by nature
├── en.md
└── images/
└── cover.jpg
Running the command on a translation picks up the index.md beside it, and anything the
translation names itself wins. Running it on index.md asks which translation to publish. The
filename decides the typography, ahead of any lang in the frontmatter. A cover at
images/cover.* is used without asking. Write-back always goes to the translation you published.
Failures. If the draft cannot be created, the cover's permanent-material slot is given back rather than left orphaned in your account. If any image reference was not replaced with the URL WeChat returned, publishing aborts instead of shipping a draft whose pictures only load in your vault.
Storing the AppSecret in the plugin's data.json would put it wherever your vault syncs — iCloud,
Obsidian Sync, a git remote — and Obsidian gives plugins no sandbox from one another, so any other
installed plugin could read it. The AppSecret is the account's master credential: it can send mass
messages, rewrite menus and enumerate followers, far beyond what this plugin needs. Keeping it in a
file you control, and whitelisting your IP in the WeChat console, are the two things that actually
contain the damage if it leaks.
Reading that file is the only thing the plugin does outside your vault, and it does it through
Obsidian's own FileSystemAdapter.readLocalFile — the plugin imports no Node filesystem module,
and the build fails if one ever reappears in the bundle. Everything else lives in
src/local-file.ts: recognising an absolute path and reading that one file.
Nothing is written outside the vault, ever, and the plugin never reads your environment — which is
why the path has to be written out in full rather than starting with ~.
Covers are offered from the folders the article already points at — the bundle's images/, the
note's own pictures, its folder — rather than from a listing of every file in your vault.
npm install
npm test # unit tests for the pure logic
npm run lint # the same rules the plugin directory's review runs
npm run build
npm run deploy copies the build into the vaults listed in scripts/deploy.mjs.
Bump the version in manifest.json, package.json and versions.json, then push a tag that
matches it exactly:
git tag -a 0.1.2 -m 0.1.2 && git push origin main 0.1.2
GitHub Actions checks the tag against the manifest, runs the tests, builds, and opens a draft
release with main.js, manifest.json and styles.css attached. Publishing it stays a manual
step.
MIT — see LICENSE.