BlogLoom Publisher Skill — WeChat Official Account (gzh) Channel Capabilities and Publishing Flow
Audience: developers and AI agents who need to understand, maintain or extend the BlogLoom publishing skill. Channel implementation:skills/blogloom-publisher-skill/scripts/channels/gzh/Channel SOP:references/channels/gzh/sop.mdCommon contract:references/00-tool-contract.mdThe flow follows the provenauto-sync-blogWeChat implementation and has been verified against a real Official Account backend.
1. Channel overview
Key point: visiting
cgi-bin/home directly shows “请重新登录” (please log in again). You must click the home page’s “登录” entry #jumpUrl to enter the backend with a token (if the session is valid, it enters directly). The not-logged-in state also issues cookies such as wxuin / ua_id, and these must not be used to detect login, or you get false positives.
The WeChat editor does not understand Markdown, so the body must first be rendered into HTML with inline styles via md.doocs.org, then copied as rich text back into the editor.
2. Action matrix
Publishing an Official Account article directly requires admin QR scan / review and is not suitable for automatic rollback, sotest publishuseschannel.testMode='draft', i.e. save draft → delete immediately. A channel may declaresuccessStatuses: ['SUBMITTED']; the core layer merges it into the success states if present.
3. Unified invocation
--xxx, parsed by gzh/index.js’s augmentPayload; the core CLI is unaware of them):
4. SOP field mapping (Markdown → Official Account)
5. Scenario flows in detail
5.1 gzh checkLogin — detect login state
Steps
enterAdminHome: opencgi-bin/home, click the home page’s “登录” entry#jumpUrlto reach the token-bearing backend home;- If the account name
.weui-desktop_nameappears (e.g. “长路Java”) → logged in; - Otherwise check the real session cookies
slave_sid/slave_user/data_ticket(only issued after login); - If none → not logged in.
Fix history: early versions usedwxuin, which caused not-logged-in to be misjudged as logged in; the whitelist now only accepts session cookies, and you must click#jumpUrlfirst to reach the backend.
5.2 gzh login — QR login
Steps
- Open
cgi-bin/home(headed mode); the channel’sprepareLoginclicks#jumpUrlto reach the QR page; - Scan with the admin WeChat to complete login;
- Poll the login cookies every 2s; success when
slave_sidetc. appear (10 minutes max by default; adjustable via--timeout/PUBLISHER_LOGIN_TIMEOUT); - Login state is saved to
~/.blogloom-publisher/chrome-profile-gzhand can later be reused with--mode headless.
5.3 gzh publishDraft — configure then save as draft
Steps
- Enter the backend home:
enterAdminHomeclicks#jumpUrl; - Enter the editor: Content management
.weui-desktop-menu_create→ Drafts.weui-desktop-sub-menu__item a→ “新的创作”button:has-text("新的创作")→ “文章”.weui-desktop-dropdown__list-ele:has-text("文章")(opens the editor in a new tab, switching automatically); - Fill the title:
.title-editor__input .ProseMirror(contenteditable, not the hidden#titletextarea), typed with the keyboard; - Fill the author:
#js_author_area #author(required, otherwise the later Original dialog cannot be checked); - Write the body (md.doocs styling):
- Open a conversion page at md.doocs.org → paste the Markdown → wait for rendering → click “复制” (
button:has-text("复制"), button:has-text("Copy")) to write the rich-text HTML to the clipboard; - Paste back into the content ProseMirror
.rich_media_content .ProseMirror(note this differs from the title ProseMirror); - After pasting, WeChat shows a “内容结构检测” (content structure check) dialog → click “继续插入”
button:has-text("继续插入")for the content to actually be written; - If conversion fails, automatically falls back to writing the raw Markdown (
paste → cdp → keyboard);
- Open a conversion page at md.doocs.org → paste the Markdown → wait for rendering → click “复制” (
- Config area (see 5.5 for the default strategies): cover / description / original / reward / collection / creation source;
- Save draft: click “保存为草稿”
button:has-text("保存为草稿")→status=DRAFT_SAVED.
5.4 gzh publish — publish
On top of the draft filling + configuration, click .js_bot_bar .mass_send (labeled “发表”) at the bottom:
- Mass-send dialog
.new_mass_send_dialog: if--group-sendis not enabled, turn the mass-send switch off; - Click the dialog’s primary button
.weui-desktop-btn_primary; - Second confirmation
.double_check_dialog→ continue publishing; - Mass sending requires admin QR scan, returning
status=SUBMITTED(orPUBLISHEDwhen a success toast is detected).
--dry-run: completes all filling and configuration but does not click final publish.
5.5 Default configuration strategies (preparePublish, completed automatically before saving the draft)
The Original/Reward dialogs are custom WeChat components: they respond unreliably to synthetic automation clicks.chooseOriginuses a “fill author → close and reopen with retries” strategy;enableRewardAndPayhas an overall timeout (it won’t stall the main flow). Paid is disabled per the requirement.
5.5.1 Channel-level publishing config (conf/gzh.conf)
The article title/tags/category/summary/body come from the blog’s top SOP metadata; the channel-level options in the table above come from conf/gzh.conf, read by default at runtime:
Priority: CLI args > metadata extra > conf default. See scripts/lib/channel-conf.js for the loader.
5.6 gzh delete — delete a post
Steps
- Go to Content management → Drafts;
- Locate the card
.weui-desktop-cardby--title(or bydata-appid); - Hover the card → the inline “删除”
.weui-desktop-link:has-text("删除"); - In the confirmation popover
.weui-desktop-popover, click the primary buttonbutton.weui-desktop-btn_primary:has-text("删除"); - Poll and verify the card has disappeared from the list →
DELETED, otherwiseDELETE_UNVERIFIED.
Fix history: deletion is not in a ”…” menu but an inline “编辑 / 删除”; the confirm button must be the actualbutton(not the.weui-desktop-btn_wrpouter div).
5.7 gzh test publish — publish draft → delete flow self-test
channel.testMode='draft': save draft → delete the draft immediately, returning status=DRAFT_SAVED_AND_DELETED to verify the flow works and leaves no residual content.
6. Selector reference (channels/gzh/selectors.js)
The md.doocs conversion selectors are centralized in
channels/gzh/format.js (editable: .cm-content, copyButton).
When the site changes, only edit the selector files; business-flow code stays untouched.
7. Error codes and branches
Exit codes:
0 success / 1 execution error / 2 checkLogin not logged in or login timed out.
8. Verification and maintenance
- Unit tests cover:
checkLogin / prepareLogin / enterEditor / setTitle / setAuthor / setContent (incl. continue-insert) / preparePublish / publish / saveDraft / deleteBlog / augmentPayload; contentStrategy=md-style-pastemeans the md.doocs styling conversion succeeded;- Real-device verification conclusion: title / author / body styling / cover / description(≤120) / original / collection / creation source / save draft / delete /
test publishall pass; reward is best-effort, paid is disabled.