SuperAPI AI Directory

soia-pkm-clip-gzh

General & other

View repository

Updated 10-16 08:11

About this skill

soia-pkm-clip-gzh

clip familyOfficial Account batch member: Unlike soia-pkm-clip-wechat(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." It is only for archivingaccounts you manage yourself—not a general-purpose Official Account scraper.

Client-readable description

What this skill can do

Batch-archive articles already published by a WeChat Official Account managed by the user into an Obsidian vault. Supports three routes: the official API, the Official Account backend interface, and logged-in Cookie sessions, and deduplicates 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 main workflow, and execute the minimal reliable steps The client will see Obsidian/vault file changes, terminal logs, generated artifact paths, and the 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 need 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. Describe 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 is triggered, then checks dependencies, configuration, permissions, and risky actions.
  3. For actions that can be dry-run or previewed, provide a preview first; for actions involving deletion, overwriting, sending, publishing, or writing remote state, seek client confirmation first.
  4. After execution, verify the real 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-skills -g -a '*' -s soia-pkm-clip-gzh -y

Configuration conventions:

~/.config/soia-skills/soia-open-skills/soia-pkm/soia-pkm-clip-gzh/config.yml
SOIA_PKM_CLIP_GZH_CONFIG_FILE=<custom-config-path>
  • 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 can only go into a private config.yml, the process environment, or the provider's own login state; it must not be written into the repo, vault body, or logs.
  • Strong dependencies, optional dependencies, and third-party skill relationships must be based on the "Dependencies / Prerequisites / Resources / Boundaries" description following this SKILL.md ; if it is not clearly written, first supplement the description or ask the client, and 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 then output the above receipt uniformly; 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 · Login-state Cookie
Coverage Only articles published via the **Draft 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 a capture/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: 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 be blocked directly at the permission threshold, see below). To read all history (including old articles sent manually via mass send),prefer Route C—the credential only requires capturing one pair of token/Cookie, which is less error-prone than Route B requiring three paired tickets; this is the recommended route in this skill for reading the historical articles of your own account. Route B is kept as a fallback when Route C fails (for example, if one day both interfaces are revised). All three routes only run on accounts you yourself can log in to/manage; when landing, accounts that have been run will be automatically deduplicated by url (skipping each other's already-archived articles).

Route C is further divided into two subpaths internally; the script automatically chooses based on whether you pass --name/--fakeid :If not passed (default), use appmsgpublish to read the account you are currently logged in as; 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 text/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; only after the client confirms the scope is correct should full scraping be released. 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.

Confirmation before use (consent)

Route B / Route C use 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 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-open-skills/soia-pkm/soia-pkm-clip-gzh/consent.json)。
  • Already exists and disclaimerVersion matches the current version (v1) → skip display, continue directly, and merely mention in the log "Terms of use already 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 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 complete example body could not be obtained during scraping; field names have been cross-checked and found consistent with multiple independent posts in the WeChat Open Community—recommend first using --dry-run --limit 3 to calibrate with a live test)
Get permanent material list POST /cgi-bin/material/batchget_material?access_token=,body {type:"news",offset,count}(count officially documented 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 via the Draft Box "Publish" action. Content that only went through "Mass Send" but was never clicked "Publish", as well as old-version graphic messages from before the Draft 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" 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 documentation for interfaces related to "Publish Capability" states:Starting from 2025-07, the calling permissions for these interfaces will be revoked for individual-entity accounts, unverified enterprise-entity accounts, and accounts that do not support verification. Most individual subscription accounts will most likely fail on this entire route, and a verified service account/subscription account is required (same account threshold as the soia-pkm-publish of draft/add ).
  • Conclusion: Route A is more suitable 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 an alternative.

Credentials and limitations

  • WECHAT_APP_ID / WECHAT_APP_SECRET: For the acquisition path and IP allowlist configuration, see soia-pkm-publish SKILL.md section "How to obtain and configure the WeChat Official Account AppID / AppSecret"; not repeated here—both skills use the same set of Official Account developer credentials.
  • Private configuration auto-detection:$SOIA_PKM_CLIP_GZH_CONFIG_FILE(or compatible alias $SOIA_PKM_CLIP_GZH_ENV_FILE)> ~/.config/soia-skills/soia-open-skills/soia-pkm/soia-pkm-clip-gzh/config.yml. The configuration file uses YAML env: mapping, see example config.example.yml; the key does not go into the vault or 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 the freepublish/batchget field, which has not been 100% verified against the official example body).
  • --account-name: neither interface returns the official account nickname; used 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.

Route 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 Route B, it can read all history (including old articles manually mass-sent), but the credentials only require capturing one pair of token/Cookie, which is less error-prone than Route B requiring three matching tickets (key/pass_ticket/appmsg_token).

Route 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 need 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 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 both different—these should be interfaces called by the old and new versions of the mp backend frontend respectively, and the script contains two independent implementations (list_own_account() vs fetch_article_list()), which cannot be mixed.

⚠️ Limitations and risks (be sure to 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, 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 usually expire within a few hours, there is no refresh mechanism, and after expiration base_resp.ret will return non-0 (the script already gives targeted prompts according to the table below; the two subpaths share the same set of prompts, cross-checked against community reverse engineering, no official documentation found, and it still falls under "pending user testing calibration"):

    ret Meaning Handling
    200003 invalid session token/cookie has expired; log in to the backend again, capture the request, and replace it
    200013 freq control Rate limiting triggered; community reports usually require waiting a long time, 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: when landing, author use --account-name / WECHAT_ACCOUNT_NAME as a fallback; it is not guaranteed to equal the article's real bylined author. If you need 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; according to community reports, even with throttling of 5 seconds/page, scraping on the order of nearly a thousand articles may still trigger freq control. For batch scraping an entire 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 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 's 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 in the "Network" panel whose request parameter is named 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): read your own currently logged-in account, using 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 the 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 page through and pull the list to print titles + links,do not fetch the body, do 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 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 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 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 the technical route of fetching profile_ext with cookies. It 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 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 "is recommended only for learning and research purposes." Scraping the historical articles of other people's 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. After it expires, if an error occurs, you must log in again, capture packets, and replace it.
  • Risk of rate limiting: The script defaults to an interval of --sleep 1.5 seconds between each request; do not set it too fast. For batch scraping a full year of history, it is recommended to run in batches (with --limit), and do not pull several hundred articles all 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 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 conventions (shared by all three routes)

  • Transit area, not the endpoint: what lands in the vaultInbox layer, not the final location after soia-pkm-organize is put in place—organize stockpile it here before going live, then put it in place in the 40/50 zone along with the "works library refactor".
  • Default output directory:Inbox/gzh-articles/<年>/(relative path within 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/—use --out <vault-relative-dir> or the env.OBSIDIAN_GZH_OUT in the private config to override (the default deliberately does not hardcode a specific Chinese path, complying with this repository's SKILL_SPEC.md "no hardcoded personal paths" rule).
  • Filename:<出版日期>-公众号-<账号显示名>-<标题>.md, and on name conflicts, add a short suffix 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 item this skill adds beyond 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 repository); 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 repeatedly.

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 put in place from Inbox/gzh-articles .

Closed-loop position

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

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