SuperAPI AI Directory

soia-pkm-clip-wechat-account

Office & productivity

View repository

Updated 10-16 10:17

About this skill

soia-pkm-clip-wechat-account

clip family'sOfficial Account batch member: unlike soia-pkm-clip-wechat-article(single-article, paste-URL archiving), this skill targets the batch scenario of "pulling my own Official Account's historical articles into the vault all at once". It is only for archivingthat you manage yourselfOfficial Accounts—it is not a general-purpose Official Account crawler.

Customer-readable description

What this skill can do

Batch-archive articles already published on a WeChat Official Account managed by the user into an Obsidian vault. Supports three routes: the official API, the WeChat Official Account backend interface, and logged-in-session Cookie, and deduplicates by URL

What the customer wants What the skill does What the customer can see
Complete the work covered by this skill Read the user request, necessary context, and this skill's body workflow, and execute the minimal reliable steps The customer will see Obsidian/vault file changes, terminal logs, generated artifact paths, and a final receipt.
Missing dependencies, permissions, configuration, or key Stop actions that require external state and clearly state what is missing Installation commands, application addresses, configuration paths, or questions requiring customer confirmation
Execution completed Summarize successes, skips, failures, file changes, and verification results A completion receipt that can be copied into a ticket/log

How the customer uses it

  1. State the goal in natural language and provide the necessary inputs: files, URLs, repos, workspaces, proposals, vaults, or platform account status.
  2. The Agent first determines whether this skill is triggered, then checks dependencies, configuration, permissions, and risky actions.
  3. For actions that can be dry-run or previewed, provide a preview first; when deletion, overwriting, sending, publishing, or writing remote state is involved, seek customer confirmation first.
  4. After execution, verify the real output; do not substitute "looks successful" for evidence.
  5. The final reply must give the customer a visible summary: what was done, a log summary, file changes, issues, and next steps.

Dependencies and installation

Install this skill (a single skill):

npx skills add soia-team/soia-open-skills -g -a '*' -s soia-pkm-clip-wechat-account -y

Configuration conventions:

~/.config/soia-skills/soia-open-skills/soia-pkm/soia-pkm-clip-wechat-account/config.yml
SOIA_PKM_CLIP_WECHAT_ACCOUNT_CONFIG_FILE=<custom-config-path>
# 兼容别名:$SOIA_PKM_CLIP_GZH_CONFIG_FILE(以及原有 $SOIA_PKM_CLIP_GZH_ENV_FILE)
  • If this skill does not require private configuration, you may skip creating config.yml。
  • If an API key, cookie, session, provider home, or local path is required, it may only be placed in a private config.yml, the process environment, or the provider's own login state; it must not be written into the repository, vault body, or logs.
  • Strong dependencies, optional dependencies, and third-party skill relationships must be governed by this SKILL.md subsequent "dependencies / prerequisites / resources / boundaries" description; if it is not clearly written, first supplement the description or ask the customer—do not guess.
  • A third-party skill may only declare dependencies and installation methods; it must not directly modify third-party skill files.

Logs and completion receipt

Every execution must let the customer see the process and results. Minimum receipt format:

完成:<一句话说明本次完成了什么>。

日志摘要:
- started: <检查到的输入/配置/依赖,不打印秘密值>
- processed: <数量或范围>
- created/updated: <数量或路径>
- skipped/failed: <数量和原因>

文件变化:
- <绝对路径或“未改动文件”>

验证:
- <运行过的检查、命令或人工核对点>

问题与下一步:
- <缺 key / 缺依赖 / 需要客户确认 / 建议下一条命令;没有则写“无”>

Delivery order: batch archiving must first write all articles to disk, then uniformly output the above receipt; it must not be reversed;author rely on --account-name fallback,content_complete: false and other uncertain fields must be explicitly marked as "unverified" in the receipt; do not fabricate.

How to choose among the three routes

Route A · Official API Route C · Official Account backend interface Route B · Logged-in Cookie
Coverage Only articles sent via the **Drafts "Publish"** All history, including old articles sent manually via mass send All history, including old articles sent manually via mass send
Account requirements Requires a verified service account/subscription account Any Official Account, as long as you can log in to the backend Any Official Account, as long as you can log in to the backend
Credentials AppID/AppSecret, valid long-term + IP whitelist token/Cookie, expires in a few hours key/pass_ticket/appmsg_token, expires in a few hours to a few days
Difficulty of obtaining credentials One-time configuration (generated in the backend) Capture 1 pair of values (token+Cookie) Capture 3 tickets + Cookie; easy to miss one or mismatch the pair
Degree of officialness Documented at developers.weixin.qq.com Unofficial, community reverse-engineered (source-level verification) Unofficial, community reverse-engineered, not found in official documentation
Script scripts/fetch_api.py scripts/fetch_mp.py scripts/fetch_cookie.py

Recommendation: first run Route A to scout (the one-time cost of setting up credentials is low, and they can be reused long-term for incremental sync; most personal unverified accounts will hit the permission threshold directly, see below). To read all history (including old articles sent manually via mass send),prefer Route C—the credentials only require capturing one pair of token/Cookie, which is less error-prone than Route B requiring three tickets to be paired, and is the recommended route in this skill for reading historical articles of your own account. Route B is kept as a fallback when Route C fails (for example, if both interfaces are revised someday). All three routes only run on accounts you can log in to/manage yourself; when accounts that have been run are landed, they will be automatically deduplicated by url (skipping articles already archived by each other).

Route C is further divided into two sub-paths internally; the script automatically selects based on whether you pass --name/--fakeid :If not passed (default), use appmsgpublish to read the account you are currently logged in to; if passed, --name/--fakeid only then use searchbiz+appmsg to read a specified/someone else's account—see the Route C subsection below for the reason.

Pre-execution confirmation and authorization

Confirmation strategy

Before formally scraping the body/writing files, first use a small sample such as --dry-run --limit 3 to scout, and report to the client the Official Accounts, order of magnitude of article count, and time range expected to be processed this time; after the client confirms the scope is correct, then open up full scraping. Explicitly invoking this skill and passing parameters such as --name/--limit are only recommended inputs and do not constitute a reason to skip this confirmation—the only skip condition is that the client's current sentence explicitly says "scrape directly/no need to confirm"; after skipping, the receipt must state the scope assumptions used this time.

Pre-use confirmation (consent)

Routes B / C go through the Official Account backendunofficial interfaces, which carries ToS risk. On first use of this skill, or when the version declared in this section changes, the following statement must first be shown and the client's explicit consent obtained before continuing:

Some routes of this skill (Route B / Route C) use unofficial interfaces of the WeChat Official Account backend, which are not documented in the official WeChat documentation and carry ToS risk; they are limited to archiving accounts you manage yourself, and must not be used to scrape other people's Official Accounts. (Disclaimer version v1)

  • After the customer agrees, write {accepted: true, acceptedAt: <ISO 时间戳>, disclaimerVersion: "v1"} into consent.jsonunder the private configuration directory (at the same level as config.yml :~/.config/soia-skills/soia-open-skills/soia-pkm/soia-pkm-clip-wechat-account/consent.json)。
  • already exists and disclaimerVersion matches the current version (v1) → skip the display, continue directly, and merely mention in the log "Terms of Use confirmed (<acceptedAt>)".
  • Customer refuses → stop, do not perform any scraping action.
  • Route A (official API) uses interfaces documented in the official documentation and is not subject to this consent constraint.

Route A · Official API

Interface basis (verified 2026-07 on developers.weixin.qq.com + WeChat Open Community, field names have been written into the script comments)

Interface Method Request Key response fields Basis
Get access_token GET /cgi-bin/token?grant_type=client_credential&appid=&secret= access_token, expires_in Official documentation(complete request/response examples obtained)
Get successful publish list POST /cgi-bin/freepublish/batchget?access_token=,body {offset,count,no_content} total_count,item_count,item[].article_id,item[].update_time,item[].content.news_item[].{title,author,digest,content,content_source_url,thumb_media_id,show_cover_pic,need_open_comment,only_fans_can_comment,url,is_deleted} Official documentation(complete example body could not be obtained during scraping; field names have been cross-checked and found consistent with multiple independent posts on the WeChat Open Community —suggest first using --dry-run --limit 3 to calibrate with a real test once)
Get permanent material list POST /cgi-bin/material/batchget_material?access_token=,body {type:"news",offset,count}(count officially specified as 1-20) total_count,item_count,item[].media_id,item[].update_time,item[].content.news_item[].{title,thumb_media_id,show_cover_pic,author,digest,content,url,content_source_url} Official documentation(complete request/response examples obtained)

⚠️ Coverage limitation (be sure to read first)

  • freepublish/batchget Only returns articles sent via the draft box's "publish" action. Content that only went through "mass send" but was never clicked to "publish", as well as old-version graphic messages from before the draft box feature went live, currently officiallyhave no API at all that can retrieve them — this is not an implementation flaw of this skill, but a platform limitation cross-confirmed by multiple posts on the WeChat Open Community.
  • material/batchget_material(type=news) returns permanent graphic materials; according to community feedback, once an article is officially "published" via the draft box, it may disappear from this material list. The two interfaces are in acomplementary relationship, and even taking the union does not equal "the entire published history".
  • The "Publishing Capabilities" related interface documentation states:As of 2025-07, the calling permissions for these interfaces will be revoked for personal entity accounts, uncertified enterprise entity accounts, and accounts that do not support certification. Most personal subscription accounts will most likely fail the entire route, and a certified service account/subscription account is required (the same account threshold as soia-pkm-publish-wechat-draft 's draft/add ).
  • Conclusion: Route A is better suited for "spot-checking/verifying/incrementally syncing already-published content"; do not expect it to be a "full backup". To read the entire history (including old articles manually mass-sent), prefer Route C, with Route B as a fallback.

Credentials and restrictions

  • WECHAT_APP_ID / WECHAT_APP_SECRET: The acquisition path and IP whitelist configuration are the same as soia-pkm-publish-wechat-draft SKILL.md's section "How to obtain and configure the WeChat Official Account AppID / AppSecret", and are not repeated here — the two skills use the same set of Official Account developer credentials.
  • Private configuration auto-detection:$SOIA_PKM_CLIP_WECHAT_ACCOUNT_CONFIG_FILE(compatible alias $SOIA_PKM_CLIP_GZH_CONFIG_FILE, as well as the original $SOIA_PKM_CLIP_GZH_ENV_FILE)> ~/.config/soia-skills/soia-open-skills/soia-pkm/soia-pkm-clip-wechat-account/config.yml. The configuration file uses YAML env: mapping, see examples in config.example.yml; keys do not go into the vault, nor into this open-source skill repository.

Usage

python3 scripts/fetch_api.py [--out <vault内相对目录>] [--limit N] [--dry-run] \
    [--vault <path>] [--account-name <公众号显示名>] [--force] [--page-size 20]
  • --dry-run: only fetch the list and print it, without writing files —for the first run, a dry-run is strongly recommended, to verify that total_count/item_count and the number of articles retrieved match expectations (especially since the freepublish/batchget field has not been 100% verified against the official example body).
  • --account-name: neither interface returns the official account nickname; use it for file names/source information; if not provided, "公众号" is used as a placeholder in the file name.
  • --force: articles already archived under url are skipped by default; only with this added will they be overwritten and rewritten.

Path C · Official account backend interface (recommended: read all history of your own account)

Reuses the mp.weixin.qq.com backend(author logs into the backend, not the reader-side profile_ext) interface; like Path B, it can read all history (including old articles sent manually via mass messaging), but the credentials only require capturing a pair of token/Cookie, which is less error-prone than Path B requiring a matching set of three tickets (key/pass_ticket/appmsg_token).

Path C internally has two sub-paths,scripts/fetch_mp.py automatically switches based on whether you pass --name/--fakeid :

Default (do not pass --name/--fakeid) pass --name or --fakeid
Interface appmsgpublish(author-side "Publish Records") searchbiz(name → fakeid) + appmsg?action=list_ex(fakeid → list)
Whose account is read The account you are currently logged into, no need to specify name/fakeid The account corresponding to the specified name/fakeid (can be another account you also manage)
Applicable scenarios Most common scenario: you just want to back up your own account You are logged into the backend with one account, but want to read another account you also manage and whose name/fakeid you know
Verification status Tested: ran successfully on a real account on 2026-07-09,ret=0,total_count=152 source-level verification, no official documentation found, marked "pending user testing calibration"

Why the default path does not use searchbiz:searchbiz is a fuzzy search of the official account list by name, designed for "finding someone else's account"; searching with your own official account's name often fails to find yourself (returns an empty list), so it is not suitable as the default path for "I am already logged in, I am this account".appmsgpublish directly reads the publish records of the account corresponding to the current login state, so this problem does not exist.

Interface basis (source-level verification + appmsgpublish already tested)

Interface Method Request Key response fields Basis
appmsgpublish (default, reads your own account) GET /cgi-bin/appmsgpublish,params sub=list&begin=<0,20,40,...>&count=20&type=101_1&free_publish_type=1&sub_action=list_ex&token=&lang=zh_CN&f=json&ajax=1 base_resp.ret;publish_page(JSON string,json.loads followed by {total_count, publish_list[]}); each publish_list[].publish_info(is another layer of JSON string),json.loads followed by {appmsgex[]}, each appmsgex[] contains title/link/update_time/digest cv-cat/WechatOAApis — utils/wx_utils.py source-level verification,and verified by the user on a real account on 2026-07-09
searchbiz (name → fakeid) GET /cgi-bin/searchbiz,params action=search_biz&begin=0&count=5&query=<公众号名>&token=&lang=zh_CN&f=json&ajax=1 base_resp.ret,list[].{alias,fakeid,nickname,round_head_img,service_type} wnma3mz/wechat_articles_spider — ArticlesUrls.py::official_info() and cv-cat/WechatOAApis — utils/wx_utils.py::get_fakeid_params() two independent repositories with completely identical parameters
appmsg (fakeid → article list) GET /cgi-bin/appmsg,params action=list_ex&begin=<0,5,10,...>&count=5&fakeid=<fakeid>&type=9&query=&token=&lang=zh_CN&f=json&ajax=1 base_resp.ret,app_msg_cnt,app_msg_list[].{aid,appmsgid,cover,digest,itemidx,link,title,update_time}(does not include the author field) wnma3mz/wechat_articles_spider — ArticlesUrls.py::__get_articles_data()

appmsgpublish and appmsg?action=list_ex are not the same interface; both the response structure and the pagination step differ—they should be interfaces called by the old and new versions of the mp backend frontend respectively, and the script has two independent implementations (list_own_account() vs fetch_article_list()), which cannot be mixed.

⚠️ Limitations and risks (read first)

  • Unofficial interface: like route B, it has no official documentation backing it and may be adjusted at any time.appmsgpublish Although it has been tested and passed, it is likewise not an interface included in the official documentation.

  • Only for archiving official accounts that you manage yourself:--name After a hit, it will print nickname/fakeid and prompt for confirmation; if it is not your own account, press Ctrl+C to interrupt; the default path reads the currently logged-in account itself, so there is no risk of mistaking the account.

  • token/Cookie will expire: community reports usually expire within a few hours, there is no refresh mechanism, and after expiration base_resp.ret will return non-0 (the script gives targeted prompts according to the table below; the two subpaths share the same set of prompts, cross-checked by community reverse engineering, no official documentation found, and still subject to "pending user calibration by actual testing"):

    ret Meaning Handling
    200003 invalid session token/cookie has expired; log in to the backend again, capture packets, and replace
    200013 freq control rate limiting triggered; community reports usually require waiting a long time, do not rerun immediately
    200040 invalid csrf token token and Cookie are suspected to have been captured from different login sessions
  • Neither subpath list interface returns the author field: when landing, author uses --account-name / WECHAT_ACCOUNT_NAME as a fallback, which is not guaranteed to equal the article's real byline author; for the exact author, manually check the original article page.

  • Rate limiting risk: by default the script sets the request interval per page/per article to --sleep 3 seconds; community reports say that even with throttling of 5 seconds/page, scraping nearly a thousand articles may still trigger freq control; for batch scraping a full year of history, it is recommended to use --limit to run in batches.

Credential acquisition

Log in to mp.weixin.qq.com backend, open any article edit page or article list page, press F12 to open the browser developer tools "Network" panel, and from theaddress bar URL copy the token parameter, and from the request headers copy the complete Cookie string. Fill it in according to config.example.yml into the private config.yml of env.WECHAT_MP_TOKEN / env.WECHAT_MP_COOKIE。

⚠️ Note token you need to take the one in the address bar URL (the numeric string),notthe ticket whose request parameter name in the "Network" panel is appmsg_token —the two are different tickets, andappmsgpublish recognizes the address bar token。

Usage

# 默认:不传 --name/--fakeid,读你自己当前登录的账号(appmsgpublish,推荐)
python3 scripts/fetch_mp.py [--out <目录>] [--limit N] [--dry-run] \
    [--vault <path>] [--account-name <显示名>] [--force] [--sleep 3] [--page-size 20]

# 传 --name 或 --fakeid:读指定/别人的号(searchbiz + appmsg?action=list_ex)
python3 scripts/fetch_mp.py --name <公众号名> [--out <目录>] [--limit N] [--dry-run] \
    [--vault <path>] [--account-name <显示名>] [--force] [--sleep 3] [--page-size 5]
  • Do not pass --name/--fakeid(default, recommended): reads your own currently logged-in account, using appmsgpublish,--page-size default 20.
  • Pass --name and --fakeid(choose one of the two): uses searchbiz+appmsg?action=list_ex,--page-size default 5;--name will first run searchbiz search and take the first matching result; when fakeid is known, directly passing --fakeid to skip the search step is more reliable.
  • --dry-run: only paginate to pull the list and print title+link,does not fetch the body and does not write files——Faster than Route B's dry-run (Route B's dry-run still fetches all article bodies). For the first run, it's recommended to do a dry-run first to verify whether the article count and titles match expectations.
  • --page-size When not passed, the default value is automatically taken according to the subpath used (20 for your own account / 5 for a specified account). Both are stable values that have been verified/community-tested; do not increase them.
  • After each list item gets link , the script directly GETs the article's public page and parses #js_content(the same article body fetching approach as Route B / soia-pkm-clip-wechat-article single-article archiving; no additional cookie is needed).

Route B · Logged-in Cookie

API basis (unofficial, community reverse engineering, cross-checked)

GET https://mp.weixin.qq.com/mp/profile_ext
    ?action=getmsg&__biz=<biz>&f=json&offset=<offset>&count=10&is_ok=1
    &scene=124&uin=<uin,通常固定777>&key=<key>&pass_ticket=<pass_ticket>
    &wxtoken=&appmsg_token=<appmsg_token>&x5=0
Header: Cookie: <登录态 Cookie>

响应:{"ret":0,"errmsg":"ok","msg_count":N,"can_msg_continue":0|1,
       "general_msg_list":"<JSON 字符串,需二次 json.loads>","next_offset":N,...}
general_msg_list 解析后:
  {"list":[{"comm_msg_info":{"datetime":<unix ts>},
            "app_msg_ext_info":{"title","author","content_url","is_multi",
              "multi_app_msg_item_list":[同字段的合集子项]}}]}

Basis sources: cross-checked reverse-engineering records of the same API from multiple independent technical blogs (Cnblogs/CSDN/Zhihu, 2019-2024). The field names are consistent with each other, butnone are official documentation, and developers.weixin.qq.com does not include this API. The script has been implemented accordingly and this has been written into the code comments, but it is still marked as "pending user real-world calibration"—on the first run, be sure to --dry-run --limit 3。

Note: the original requirement given to the user included a reference implementation link (zjp1997720/wechat-article-search); in actual testing, that project actually calls Sogou WeChat Search (weixin.sogou.com), and does not use the technical route of fetching profile_ext with cookies. It does not match the goal of "reading the full history," so Route B did not copy it and instead switched to the above profile_ext solution that has been independently reproduced by multiple parties.

⚠️ Limitations and risks (be sure to read first)

  • Unofficial API: There is no official documentation backing it, and WeChat may adjust the implementation at any time and make it stop working; if problems occur, there is no official support channel.
  • Only for archiving official accounts that you manage yourself: Multiple reference materials suggest that this kind of reverse-engineered call "should be used only for learning and research purposes." Scraping the historical articles of someone else's official account may violate WeChat's Terms of Service. This script is designed as self-service backup for your own account; do not use it to scrape other people's accounts.
  • Tickets expire:key/pass_ticket/appmsg_token is a session ticket. Community reports range from a few hours to a few days; there is no refresh mechanism. After it expires, if an error occurs, you must log in again, capture packets, and replace it.
  • Rate-limiting risk: By default, the script waits --sleep 1.5 seconds between requests; do not set it too fast. For batch scraping a full year of history, it is recommended to run it in batches (with --limit), and do not pull hundreds of articles at once.

Credential acquisition

Log in to mp.weixin.qq.com backend, open any historical article of that official account (or profile_ext?action=home), find same-domain requests in the browser developer tools "Network" panel, copy __biz/key/pass_ticket/appmsg_tokenfrom the request URL, and copy the complete Cookie string from the request headers. Fill it into the private config.example.yml according to config.yml。

Usage

python3 scripts/fetch_cookie.py --biz <__biz> [--out <目录>] [--limit N] [--dry-run] \
    [--vault <path>] [--account-name <显示名>] [--force] [--sleep 1.5]
  • The list API returns 10 items per page (the community-tested upper limit). The script automatically pages by can_msg_continue/next_offset .
  • After each list item gets content_url , the script directly GETs the article's public page and parses #js_content(the same article body fetching approach as soia-pkm-clip-wechat-article single-article archiving; no additional cookie is needed).
  • --dry-run will still page through the entire list, fetch all article bodies, and then print (without writing files). When the volume is large, it takes a long time—if you want to quickly verify the list itself, first look at the per-page output printed to stderr 拿到 N 条,累计 M。

Implementation specification (shared by all three routes)

  • Transit area, not the endpoint: what lands in the vaultInbox layer, not the final location after soia-pkm-organize-article-moc is put in place—organize stockpile it here before going live, then relocate it to the 40/50 zone together with the "works library refactor".
  • Default output directory:Inbox/gzh-articles/<年>/(a relative path inside the vault, the generic default in the code). For this vault, it is recommended to set it to your own staging area, e.g. <vault-inbox-dir>/gzh-articles/—override it with --out <vault-relative-dir> or the env.OBSIDIAN_GZH_OUT in your private config (the default deliberately does not hardcode a specific Chinese path, following this repo's SKILL_SPEC.md "no hardcoded personal paths" rule).
  • Filename:<出版日期>-公众号-<账号显示名>-<标题>.md, and on name conflicts a short suffix is added based on url characteristics.
  • frontmatter:tags:[公众号原创, 待归位]、source: 公众号自有、url、title、author、published_at、captured_at、route: api|cookie|mp-backend、content_complete、topics: [](topics is an extra item this skill adds relative to the user's original field list, to make it convenient for subsequent organize to directly fill in the category; leave it as an empty array when there is no content, and it does not affect other fields).
  • Body: HTML → Markdown goes through stdlib html.parser(zero third-party dependencies, consistent with the other scripts in this repo); 100% layout fidelity is not guaranteed, and complex inline styles/WeChat-exclusive components will be flattened into plain text + image links.content_complete: false indicates that body extraction failed or is empty, and the original link needs to be manually checked.
  • Idempotent: all three scripts recursively search under url in out_dir by--force overwrites); running the three paths alternately will not land the same article twice.

After archiving

Following the clip family convention, AI can subsequently:

  1. Add a "## Summary" (30-80 characters) to each article.
  2. Determine/backfill frontmatter topics。
  3. After the works library refactor is settled, hand it to organize to batch-relocate from Inbox/gzh-articles .

Closed-loop position

★clip-gzh(批量收) → organize(归位/分类) → distill(收藏→观点) → compose(观点→文) → publish(发)

and soia-pkm-clip-wechat-article(single-article archiving) are complementary: clip-wechat handles "see a forwarded link, save an article on the fly"; clip-gzh handles "batch-migrating the historical backlog of my own official account". The two are not fully consistent in their landing conventions (clip-gzh has an extra route/content_complete field), so they are not merged for now; whether to converge them into one set will be evaluated together during the works library refactor.