SuperAPI AI Directory

soia-pkm-clip-wechat-account

Office & productivity

View repository

Updated 10-16 09:05

About this skill

soia-pkm-clip-wechat-account

clip family'sOfficial Account batch member: unlike soia-pkm-clip-wechat-article(single article, archive by pasting a URL), this skill is aimed at the batch scenario of "pulling my own official account's historical articles into the vault all at once". Only for archivingofficial accounts you manage yourself—not a general-purpose WeChat Official Account scraper.

Client-readable description

What this skill can do

Batch-archive articles already published on the WeChat Official Account that the user manages themselves into an Obsidian vault. Supports three routes: the official API, the WeChat Official Account backend interface, and logged-in session cookies, with deduplication by URL

The client wants The skill will do The client 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 client will see Obsidian/vault file changes, terminal logs, generated artifact paths, and a final receipt.
Missing dependencies, permissions, configuration, or keys Stop actions that require external state, and clearly state what is missing Install commands, application addresses, configuration paths, or questions that require client confirmation
Execution complete Summarize successes, skips, failures, file changes, and verification results A completion receipt that can be copied into a ticket/log

How the client uses it

  1. State the goal in natural language and provide the necessary inputs: files, URLs, repo, workspace, proposal, vault, or platform account status.
  2. The Agent first determines whether this skill applies, 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, ask for client confirmation first.
  4. After execution, verify the actual output; do not substitute "looks successful" for evidence.
  5. The final reply must give the client a visible summary: what was done, log summary, file changes, issues, and next steps.

Dependencies and installation

Install this skill (single skill):

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

Configuration conventions:

~/.config/soia-skills/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 need private configuration, you may skip creating config.yml。
  • If an API key, cookie, session, provider home, or local path is needed, it may only go into 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 based on the "dependencies / prerequisites / resources / boundaries" description later in this SKILL.md ; if it is not clearly stated, add the description first or ask the client—do not guess.
  • Third-party skills may only declare dependencies and installation methods; do not directly modify third-party skill files.

Logs and completion receipt

Every execution must let the client 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, and only then output the above receipt uniformly; the reverse is not allowed;author rely on --account-name fallback,content_complete: false and other uncertain fields must be explicitly marked "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 box "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 engineering (source-level verification) Unofficial, community reverse engineering, not found in official documentation
Script scripts/fetch_api.py scripts/fetch_mp.py scripts/fetch_cookie.py

Recommendation: Run Route A first to scout (setting up the credentials once is low-cost and can be reused long-term for incremental sync; most personal unverified accounts will directly hit the permission threshold, 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's need to pair three tickets, and is the recommended route in this skill for reading your own account's historical articles. Route B is kept as a backup for when Route C fails (for example, if both interfaces are revised someday). All three routes only run on accounts you yourself can log in to/manage; accounts that have been run will, on landing, be url automatically deduplicated (articles already archived are skipped mutually).

Route C is further divided into two sub-paths; the script automatically chooses based on whether you pass --name/--fakeid :If not passed (default), it uses appmsgpublish, reading the account you are currently logged in to; if passed, --name/--fakeid only then does it use searchbiz+appmsg, reading the 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 text/writing files, first use --dry-run --limit 3 or a similar small sample 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; only after the client confirms the scope is correct should full scraping be released. Explicitly invoking this skill and passing --name/--limit and other parameters 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 scope assumptions used this time must be stated in the receipt.

Consent before use

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 displayed 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 official WeChat documentation and carry ToS risk; use only to archive accounts you manage yourself, and do not use them to scrape other people's Official Accounts. (Statement 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-pkm-clip-wechat-account/consent.json)。
  • Already exists and disclaimerVersion matches the current version (v1) → skip the display and continue directly, mentioning only once in the log "Terms of Use already confirmed (<acceptedAt>)".
  • Customer refuses → stop, do not perform any scraping actions.
  • 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 against 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(the full example body could not be obtained at scrape time; field names have been cross-checked and confirmed against multiple independent posts in the WeChat Open Community——it is recommended to first use --dry-run --limit 3 for a real-world calibration test)
Get permanent material list POST /cgi-bin/material/batchget_material?access_token=,body {type:"news",offset,count}(count is officially marked 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 limitations (must read first)

  • freepublish/batchget Only returns articles sent through the "publish" action in the drafts box. Content that only went through "mass send" but was never clicked as "publish", as well as legacy graphic messages from before the drafts box feature launched, currently haveno 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 in the WeChat Open Community.
  • material/batchget_material(type=news) returns permanent graphic materials; according to community feedback, once an article is officially "published" through the drafts box, it may disappear from this material list. The two interfaces are in acomplementary relationship, and even taking the union does not equal "all published history".
  • The documentation for "publish capability" related interfaces notes:As of 2025-07, the calling permissions for these interfaces will be revoked for personal entity accounts, unverified enterprise entity accounts, and accounts that do not support verification. Most personal subscription accounts will likely fail the entire route, and verified service accounts/subscription accounts are required (the same account threshold as soia-pkm-publish-wechat-draft 's draft/add ).
  • Conclusion: Route A is better suited for "spot checks/verification/incremental sync of already published content"; do not expect it to be a "full backup". To read all history (including old articles manually mass-sent), prefer Route C, with Route B as a fallback.

Credentials and limitations

  • WECHAT_APP_ID / WECHAT_APP_SECRET: the acquisition path and IP whitelist configuration are the same as soia-pkm-publish-wechat-draft SKILL.md "How to obtain and configure a WeChat Official Account AppID / AppSecret" section, not repeated here——the two skills use the same set of Official Account development 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-pkm-clip-wechat-account/config.yml. The config file uses YAML env: mapping, see example in config.example.yml; keys don't go into the vault or into this open-source skill repo.

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, no file writing—for the first run, strongly recommend dry-run first, verify total_count/item_count and whether the number of articles retrieved matches expectations (especially the freepublish/batchget field has not been 100% verified against the official example body).
  • --account-name: both interfaces don't return the official account nickname, used for filename/source info; if not provided, "公众号" is used as a placeholder in the filename.
  • --force: articles already archived by url are skipped by default; only with this will they be overwritten and rewritten.

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

Reuse 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 credentials only require capturing one pair of token/Cookie, which is less error-prone than Path B which requires pairing 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 (not passing --name/--fakeid) Passing --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're currently logged into, no need to specify name/fakeid The account corresponding to the specified name/fakeid (can be another account you also manage)
Use cases Most common case: just want to back up your own account You're logged into the backend with some account, but want to read another account you also manage and whose name/fakeid you know
Verification status Already tested: ran successfully on a real account on 2026-07-09,ret=0,total_count=152 source-code-level verification, no official documentation found, marked "pending user testing calibration"

Why the default path doesn't use searchbiz:searchbiz : it does fuzzy search of 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 empty list), so it's not suitable as the default path for "I'm 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 doesn't exist.

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

Interface Method Request Response key fields Basis
appmsgpublish (default, read 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-code-level verification,and verified on a real account by the user 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 have 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; the response structure and pagination step are different—they should be interfaces called by the old and new frontend versions of the MP backend respectively, and the script has two independent implementations (list_own_account() vs fetch_article_list()), which cannot be mixed.

⚠️ Limitations and Risks (Must 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 verified by actual testing, it is likewise not an interface included in official documentation.

  • Only for archiving official accounts that you manage yourself:--name After a match, it will print nickname/fakeid and prompt for confirmation. If it is not your own account, interrupt with Ctrl+C; 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 say it usually expires within a few hours, with no refresh mechanism. After expiration, base_resp.ret will return non-zero (the script already gives targeted prompts according to the table below; both subpaths share the same set of prompts, cross-checked by community reverse engineering, no official documentation found, and it still falls under "pending user actual-test calibration"):

    ret Meaning Handling
    200003 invalid session token/cookie has expired; log in to the backend again, capture packets, and replace them
    200013 freq control Rate limiting has been triggered; community reports usually require waiting a long time, so do not rerun immediately
    200040 invalid csrf token The token and Cookie are suspected to have not been captured from the same login session
  • Neither subpath list interface returns the author field: At implementation time, author falls back to --account-name / WECHAT_ACCOUNT_NAME , which is not guaranteed to equal the article's real byline author. If you need the exact author, manually check the original article page.

  • Rate-limiting risk: By default, the script requests at an interval of --sleep 3 seconds per page/article; community reports say that even with 5 seconds/page throttling, 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 the 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 fromthe address bar URL copy the token parameter, and from the request headers copy the complete Cookie string. Fill it into the private config.example.yml according to config.yml 's env.WECHAT_MP_TOKEN / env.WECHAT_MP_COOKIE。

⚠️ Note token should be the one in the address bar URL (the numeric string),notthe ticket in the "Network" panel whose request parameter name is appmsg_token —the two are different tickets,appmsgpublish 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): read your own currently logged-in account, use appmsgpublish,--page-size , default 20.
  • Pass --name and --fakeid(choose one of the two): use searchbiz+appmsg?action=list_ex,--page-size , default 5;--name will first run searchbiz search and take the first matching result; when fakeid is already known, pass --fakeid directly to skip the search step, which is more reliable.
  • --dry-run: only page through the list and print titles + links,does not scrape the body or 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 sub-path being used (20 for your own account / 5 for a specified account); both are stable values that have been verified/community-tested, so don't increase them.
  • After each list item gets link , the script directly GETs the article's public page and parses #js_content(the same body-fetching approach as Route B / soia-pkm-clip-wechat-article single-article archiving, no extra cookie needed).

Route B · Logged-in Cookie

Interface basis (unofficial, community reverse-engineered, 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 interface 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 interface. The script has been implemented accordingly and this is 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 requirements 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 cookies to scrape profile_ext technical route, which does not match the goal of "reading all 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 interface: 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 all note that this kind of reverse-engineered call is "recommended only for learning and research purposes"; scraping the historical articles of others' official accounts 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.
  • The ticket will 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, so after it expires and errors occur, you must log in again, capture packets, and replace it.
  • Rate-limiting risk: by default, the script waits --sleep 1.5 seconds between each request; don't make it too fast; for batch scraping a full year of history, it is recommended to run in batches (with --limit), and don't pull hundreds of articles all at once.

Credential acquisition

Log in to the 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 interface has 10 items per page (the community-tested upper limit); the script automatically paginates 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 body-fetching approach as soia-pkm-clip-wechat-article single-article archiving, no extra cookie needed).
  • --dry-run It 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 printed output in stderr 拿到 N 条,累计 M。

Implementation specification (shared by all three routes)

  • Transit area, not the destination: what lands in the vaultInbox layer, not the soia-pkm-organize-article-moc final location after being filed back—organize stockpile it here before launch, then file it back to the 40/50 zones together with the "Works Library Restructuring".
  • Default output directory:Inbox/gzh-articles/<年>/(a relative path within the vault, the generic default in the code). This vault is recommended to be set as your own staging area, for example <vault-inbox-dir>/gzh-articles/—use --out <vault-relative-dir> or the env.OBSIDIAN_GZH_OUT in private config to override (the default deliberately does not hardcode specific Chinese paths, complying with this repository's SKILL_SPEC.md "no hardcoded personal paths" rule).
  • Filename:<出版日期>-公众号-<账号显示名>-<标题>.md, and when a name conflict occurs, add a short suffix based on URL features.
  • frontmatter:tags:[公众号原创, 待归位]、source: 公众号自有、url、title、author、published_at、captured_at、route: api|cookie|mp-backend、content_complete、topics: [](topics is one item this skill adds beyond the user's original field list, to facilitate subsequent organize directly filling in the category, leaving it as an empty array when there is no content, without affecting other fields).
  • Body: HTML → Markdown goes through stdlib html.parser(zero third-party dependencies, consistent with the repository's other scripts), and 100% layout fidelity is not guaranteed; complex inline styles/official-account-specific 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 verified.
  • Idempotent: all three scripts recursively search under url by out_dir to see whether it has already been archived, and if it already exists, they skip by default (--force only then overwrites), so running the three paths alternately will not land the same article twice.

After archiving

following the clip family conventions, AI can subsequently:

  1. Add a "## Summary" (30-80 characters) to each article.
  2. Determine/backfill frontmatter topics。
  3. after the works library restructuring is settled, hand it to organize to batch file back 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 "seeing a forwarded link and saving an article on the fly"; clip-gzh handles "batch migrating the historical backlog from my own official account". The two are not completely consistent in their landing conventions (clip-gzh has extra route/content_complete fields), so they are not merged for now, and whether to converge them into one set will be evaluated together when the works library is restructured.