astro-to-wechat: A Small Tool For Syncing Astro Posts To WeChat Drafts
· 25 min read · Views --

astro-to-wechat: A Small Tool For Syncing Astro Posts To WeChat Drafts

Author: Alex Xiang


astro-to-wechat: A Small Tool For Syncing Astro Posts To WeChat Drafts

If you write a blog with Astro or another static site generator and also run a WeChat Official Account, you will likely hit the same pain point: the same article has one HTML/CSS shape on the website, but the WeChat editor has another set of rules. Cover images, inline images, external links, and styles all need adaptation. After copying and pasting a few times, the natural question is: can a published page automatically become a WeChat draft?

astro-to-wechat is a Python command-line tool built for that scenario. Given a site URL or post slug, it calls WeChat Official Account APIs to create or update a draft, and writes the returned media_id back into the Markdown frontmatter. It started as a private script for www.zicode.com, then was extracted into a standalone MIT-licensed repository so others can use it with their own Astro projects.

This article explains why it exists, what it does, how to install and use it, how it differs from the main-site script, and where I want to take it next.

Why It Exists: Blog And WeChat Are Two Pipelines

Many technical writers follow a two-channel habit: publish first on the blog, where version control, code highlighting, and long-form structure are easier; then redistribute on WeChat, where readers inside WeChat can discover and share it more easily.

The source content is often the same, but the shape differs:

  • The website is your own HTML, with your own layout, dark mode, and components.
  • WeChat articles must pass through material uploads, allowed tags, external-link restrictions, and style limits.

My early workflow was simple but painful: write Markdown in Astro, deploy it, open the WeChat platform, paste the body section by section, and upload the cover again. This created a lot of repeated work and made it easy for the WeChat draft to drift from the website version.

The need was clear: use the online page, or local build output, as the source of truth and automatically produce a WeChat-compatible draft. Once that path works, both individual posts and serial columns save a lot of mechanical work.

Why Extract It From The Monorepo

In the www.zicode.com repository, the sync script used to be tightly coupled to the whole site. Content directories, build output paths, and style conventions were hard-coded under astro-site/. That was convenient for maintaining this site, but unfriendly to readers who only wanted the sync ability.

So I extracted it into ax2/astro-to-wechat:

  • Kept: Python script, config/wechat.example.json, and a minimal package.json for using sharp to convert SVG covers to PNG.
  • Excluded: site theme, articles, deployment configuration, and secrets.
  • Configuration: paths such as content_root, public_root, dist_root, and node_modules_root point to your Astro project instead of being hard-coded.

This means you can clone the repository anywhere, point it at ../my-blog, and keep it independent from your site development.

What It Does

The core flow is:

fetch page -> extract article body -> process images and styles -> call WeChat draft API

  1. Page source

    • By default, the tool builds the post URL from site_url plus --slug and fetches it over HTTP.
    • If dist_root is configured and dist/blog/<slug>/index.html exists, it reads local build output first. This avoids mismatch when the online site has not been deployed yet, or when you want the draft to match the current build exactly.
  2. Body extraction

    • The HTML parser finds the article body wrapped in .prose, matching this site’s Astro layout. If your theme uses a different class, change the parser logic or standardize the class name.
    • It removes page title areas and keeps reading-related block content.
  3. WeChat-side HTML and images

    • Images in the body are uploaded to WeChat material APIs, and their src values are replaced with WeChat-returned URLs.
    • Allowed tags receive inline styles so technical posts remain readable in WeChat. Tables, code, and block quotes are handled conservatively under WeChat’s allowlist.
    • If the body contains a post-site-intro block, used by this site for its introduction block, it is converted into inline styles close to the blog’s light theme.
  4. Cover and draft

    • Cover priority is roughly: --thumb -> frontmatter heroImage -> configured default image -> page og:image -> first image in body.
    • SVG-to-PNG conversion is supported through sharp after running npm install in this repository.
    • The script calls create-draft or update-draft APIs. It can optionally publish or send. On success, it can write wechatDraftMediaId and wechatPublishId back into Markdown.

The workflow on this site also supports batch sync by column through --column, based on the frontmatter column field. That is useful when a whole series needs to enter the draft box.

Pitfalls From Implementation

The hard part was not simply calling the API. It was aligning details:

  • Images: Relative paths, public paths, and online absolute URLs need unified resolution. Failure logs must be readable, and the script must decide whether fallback to original URLs is acceptable.
  • Styles: WeChat supports only a limited set of tags and styles. Complex layouts must be simplified into conservative paragraphs, lists, tables, and code blocks.
  • External links: In some cases, external links are collected into a reference section at the end, instead of leaving uncontrolled jumps throughout the body.
  • Original declaration: WeChat’s draft API does not expose a field for declaring an article as original. The script cannot check that box for you. So the configuration has declare_original, and frontmatter has original. After sync, the script prints a reminder so the editor can confirm it in the backend.

These lessons are reflected in the current script structure and the README’s Behavior Notes.

Install And Use

Environment: Python 3.10+. If you need SVG cover conversion, install Node 18+ and run npm install in this repository.

git clone https://github.com/ax2/astro-to-wechat.git
cd astro-to-wechat
npm install
cp config/wechat.example.json config/wechat.local.json
# Edit wechat.local.json: app_id, app_secret, site_url, and root paths.

Common commands:

# Generate JSON only, without calling WeChat.
npm run sync:wechat -- --slug your-post-slug --dry-run --output tmp/preview.json

# Create a draft. If content_root is configured, wechatDraftMediaId can be written back.
npm run sync:wechat -- --slug your-post-slug

# Use a full URL.
python3 scripts/sync_wechat_article.py https://example.com/blog/your-post/

# Update an existing draft.
npm run sync:wechat -- --slug your-post-slug --update-media-id MEDIA_ID

For complete parameters, fields, and security notes, see the repository README.

Relationship With The www.zicode.com Script

The main-site monorepo still keeps a path-specific sync script that points directly at astro-site/, matching this site’s CI and skill documentation. Functionally, it will be periodically aligned with astro-to-wechat, which is the portable, configurable version.

If you fork the open-source repository and want a feature to match the main site exactly, watch changes to scripts/sync_wechat_article.py in the site repository or open an issue to discuss merge strategy.

Future Plans

Short term:

  • Add a theme adaptation guide, including how to change the parser when the article body is not .prose.
  • Improve error messages for common failures such as oversized images or HTML length limits.
  • Add pure-function unit tests for HTML rewriting and path resolution, reducing dependency on real API calls.

Mid term:

  • Allow style mapping to live in configuration, such as JSON, so different sites can reuse the tool with different brand colors.
  • Explore a multi-platform sync abstraction from one intermediate representation, while keeping scope controlled and avoiding a bloated publishing system.

Long term:

  • If WeChat opens more APIs around originality declarations or collections, evaluate automation again. Until then, manual confirmation in the backend remains the safer compliance path.

If you maintain both an Astro blog and a WeChat Official Account, try astro-to-wechat, and feel free to open issues or PRs. The tool is small, but the repeated effort it saves can add up.