Files
catbus/docs/upstream-map.md
cv-cat dc078b248b catbus:一个命令操作小红书、抖音、TikTok、B 站、快手、微博、闲鱼、淘宝、京东、X
10 个平台的 web 端全部用 TypeScript 移植自 cv-cat 的开源项目,请求与上游逐字节对拍。
统一的命令语法与 JSON 输出、按 平台 × 端 × 账号 隔离的登录态、给 AI Agent 用的技能与 llms.txt。
2026-10-06 19:24:55 +08:00

39 KiB
Raw Permalink Blame History

上游移植对照(references/ → catbus)

移植某个平台时查这份文档:上游的鉴权、登录、API 方法、要复制的 JS 资产、游客态和长连接的代码位置。

  • 行号是写这份文档时的位置,只用来定位。动手前以 src/platforms/<p>/UPSTREAM 记录的 commit 为准,重新核对一遍。
  • catbus 的规范见 AGENTS.md,能力矩阵见 capabilities.md。
  • 「上游凭证来源」只用于理解上游代码和在本机跑上游对比;catbus 不读这些位置。

1. 通用

  • 10 个上游都是 Python。

    • HTTP 以 curl_cffi 为主,用来模拟 Chrome 的 TLS / JA3 与 HTTP/2 指纹。
    • Weibo、Xianyu、Taobao 用的是 requests。
  • 签名 JS 在上游里有两种调用方式:

    • subprocess 调 node;
    • PyExecJS,共三处:XHS 的 xhs_creator_util.py、Xianyu 的 utils/goofish_utils.py、Taobao 的 utils/taobao_utils.py。

    catbus 里统一改成 node:vm 执行(见 AGENTS.md 7.4)。

  • 上游 JS 实际 require 的第三方 npm 模块只有三个,其余都是 Node 内置模块:

    • jsdom:JD;
    • crypto-js:XHS;
    • @napi-rs/canvas:JD;KS 的 kwf_oracle.js 里是 try/catch 的可选依赖。

1.1 Python → TS 替换对照

上游 Python catbus
curl_cffi、requests wreq-js(core/http.ts)
websockets、websocket-client wreq-js 的 WebSocket(core/stream.ts)
protobuf + pb2 protobufjs
blackboxprotobuf(无 schema 解码) protobufjs 的 Reader
onnxruntime onnxruntime-web
opencv、scikit-image、scipy、numpy @techstark/opencv-js,其余手工移植
opencv 的视频探测(cv2.VideoCapture 取帧率、帧数、宽高) core/mp4.ts 读 MP4 / MOV 的盒子(xhs、tiktok);截帧要解码,不做
Pillow @napi-rs/canvas
ddddocr 移植到 onnxruntime-web 上
pydantic zod
loguru catbus 的 stderr logger
subprocess node x.js、PyExecJS node:vm(见 AGENTS.md 7.4)
qrcode qrcode

1.2 游客态生成

对应 AGENTS.md 5.2 的 guest.json。

平台 生成位置 内容
xhs xhs_utils/common_util.py:34 generate_a1、:42 generate_web_id a1、webId
douyin dy_apis/login_api.py:861 register_ttwid;utils/dy_util.py:107 generate_msToken、:185 generate_webid;utils/mstoken.py:308 ttwid、msToken、webid
tiktok 没有生成器,msToken 来自 cookie(builder/auth.py:156) 游客态需要在 catbus 里补
bilibili builder/auth.py:52 BiliAuth.anonymous;utils/device.py buvid3 / buvid4 等
kuaishou ks_apis/login_api.py:732 bootstrap_device_fingerprint 设备指纹 cookie
weibo 上游没有:WeiboMobileApis 不带 cookie 访问 m.weibo.cn 现在不带 cookie 会被 302 到访客页。catbus 访问 m.weibo.cn 时用新浪访客系统 genvisitor2 下发的访客 SUB / SUBP(登录后也是,登录 cookie 属于 .weibo.com);这只是 m.weibo.cn 的访问方式,web 端没有游客态
xianyu utils/build_cookies.py:52 build_initial_cookies 先调一次 mtop 拿 _m_h5_tk,再调一次拿 cookie2;含 tfstk
taobao taobao_apis.py:29 get_token _m_h5_tk
jd utils/device_token.py:112 get_device_fields 设备字段
x builder/auth.py:26 GUEST_ACTIVATE_URL = https://api.x.com/1.1/guest/activate.json,:87 XAuth.guest_token 惰性换取 guest token,约 3 小时有效。上游的读接口并不带它;web 端不支持游客态,catbus 没有移植

1.3 长连接

对应 live listen / msg listen。

用途 位置 地址
douyin 私信 dy_apis/douyin_recv_msg.py:42 wss://frontier-im.douyin.com/ws/v2
douyin 直播 dy_live/server.py:178 wss://webcast100-ws-web-hl.douyin.com/webcast/im/push/v2/
tiktok 私信 api/tiktok_chat.py:21 wss://im-ws.tiktok.com/ws/v2
tiktok 直播 api/tiktok_web.py:5597 wss://webcast-ws.tiktok.com/webcast/im/ws_proxy/
xhs 直播 apis/xhs_live.py:31 wss://apppush-rws.xiaohongshu.com/rwp
kuaishou 直播 ks_apis/live_api.py:1169、ks_apis/live_ws.py 的 LiveDanmakuClient 由接口下发
xianyu 私信 goofish_live.py:18 wss://wss-goofish.dingtalk.com/
taobao 私信 taobao_live.py:19 wss://wss-cntaobao.dingtalk.com/
jd 客服 jd_apis/jd_chat_ws.py:5 wss://ws1-dd.jd.com/

2. 各仓库

xhs — Spider_XHS

  • 鉴权
    • xhs_utils/xhs_auth.py 的 XHSUnifiedAuth 包含两个子鉴权:.pc 为 XHSPcAuth,.creator 为 XHSCreatorAuth,都继承自 xhs_core/auth.py 的 XHSAuth。
    • 构造方式:from_qrcode_login、from_phone_login、from_cookie。
    • 必需的 cookie 是 a1 和 web_session,按 host 分域存储。创作者中心对应 catbus 的 creator scope。
  • 登录:apis/xhs_pc_login_apis.py 的 XHSLoginApi 和 apis/xhs_creator_login_apis.py 的 XHSCreatorLoginApi,都有 qrcode_login、phone_login。
  • 上游凭证来源:.env 的 COOKIES。
  • API(实例方法)
    • apis/xhs_pc_apis.py 的 XHS_Apis:get_note_info(url)、get_user_info、get_user_me、get_user_all_notes、get_user_all_like_note_info、get_user_all_collect_note_info、search_note(note_time 对应 --time)、search_some_note、search_user、get_search_keyword、get_note_all_comment、get_homefeed_recommend、get_unread_message、get_all_metions、get_note_no_water_video/img
    • apis/xhs_creator_apis.py 的 XHS_Creator_Apis:post_note、upload_media、get_all_posted_notes。视频的时长、宽高、帧率上游用 opencv(extract_video_cover_and_metadata),catbus 读 MP4 盒子算出同样的值;首帧封面要解码,catbus 不做,视频发布要 --cover
    • 直播与私信:apis/xhs_live.py 的 XHSLiveAPI(HTTP:直播间、get_chats / get_group_chats、get_message_history / get_group_message_history、get_following、已读 / 撤回 / 删除)和 XHSWebSocket(RWP 长连:私信收发、直播间事件、send_room_text 发弹幕)
      • msg send 先走长连(send_private_message),等发送回执最多 10 秒(上游只发不等);长连没发出去(连不上、握手失败)才走 HTTP 短链 send_short_link_message。帧已发出却没等到回执时报 NETWORK(detail.sent 为 true),不重发,免得对方收到两条。live send 的长连退路同样处理
    • 创作者中心的登录态由主站会话桥接(xhs_utils/xhs_auth.py 的 XHSUnifiedAuth.creator_auth → XHSCreatorAuth.from_pc_auth):catbus 存进 creator scope 复用,失效时自动重新桥接一次。桥接在凭证副本上做,全部成功后才写回,中途失败时凭证不变
    • item publish --poi-name [--poi]:上游按地点名搜索后取第一个;catbus 按 --poi-name 搜,给了 --poi 时取 id 相同的那个,否则取名称完全相同的,都没有时报 USAGE(按 id 搜不到地点,所以 --poi 不能单独用)
    • 蒲公英 apis/xhs_pugongying_apis.py、千帆 apis/xhs_qianfan_apis.py:不移植(AGENTS 4.12)
  • JS 资产(npm 依赖 crypto-js)
    • xhs_utils/xhs_core/js/:sign.js、xs_common.js、b1.js、mns.js、websectiga_cli.js、websectiga_env.js,以及 mns keystream 的 JSON
    • xhs_utils/xhs_pc/js/:sign.js、xs_common.js、b1.js、mns.js、rap.js、rap_cli.js、rap_crypto.js、web_ssk.js、aes_encrypt.js、aes_decrypt.js、deflate.js、profile.js,以及模板 / profile 的 JSON
    • xhs_utils/xhs_creator/js/:xhs_creator_sign.js、xhs_creator_signature.js、profile.js,以及指纹 / profile 的 JSON
  • 注意:拿笔记详情需要 xsec_token,所以优先传 URL;catbus 输出的 xhs url 都带上它。

douyin — DouYin_Spider

  • 鉴权
    • builder/auth.py 的 DouyinAuth.perepare_auth(cookieStr, web_protect_, keys_)(方法名原文如此拼写)。
    • 私信签名还需要 ticket、ts_sign、client_cert、private_key。catbus 里放在凭证文件的 tokens / device。
    • 写接口和 passport 请求带 x-tt-session-dtrait:外层由 utils/dtrait.py 按请求 path 现算,内层是绑定设备的特征 blob。上游 DouyinAuth.session_dtrait_header 取内层的顺序是 DY_DTRAIT_BLOB(抓到的 blob)> 设备档案(DY_DTRAIT_PROFILE 指定的,否则是随包的 utils/dtrait_profile.json,由 utils/dtrait_features.py 的 build_blob 现算)。默认档案来自上游 fix-dtrait-blob 分支(还没合进 master,catbus 以它为基线,见 UPSTREAM),是从 Chrome 153 DevTools 取证固化的。
    • 哪些请求必须按 path 现算(上游 Header.with_session_dtrait,strict=True、allow_static=False:缺素材就报错,不用成品头):发布(create_v2)、评论(publish_comment 的 with_bd(require_dtrait=True))、收藏(collect_aweme)、收藏夹移动(move_collect_aweme / remove_collect_aweme)。digg 仍是算得出就带(成品头也行)。catbus 对应 Douyin.withSessionDtrait、withBd(..., { requireDtrait: true })。
    • catbus 对应:凭证 device 里的 dtrait_blob、dtrait_profile(都可选,用 --cookie 的 JSON 对象导入),没有时用 static/douyin/dtrait_profile.json;build_blob 移植在 web/dtrait.ts。Math 指纹的入参全是字面常量,直接写 Chrome 的结果:Node 24 自带的 V8 在 atanh(0.5)、log(3)、expm1(1) 上与 Chrome 差 1 ULP(实测),macOS 的 libm 在 tan(-1e300) 上与 V8 差 1 ULP(对拍脚本把它固定成 V8 的值)。
  • 登录:dy_apis/login_api.py 的 DYLoginApi:qrcode_login、send_sms_code、phone_login、save_credential、get_login_auth。
    • 短信默认走 passport-web(/passport/web/send_code、sms_login);DY_PHONE_LOGIN_PROFILE=sso 时改走 login.douyin.com 页的 SSO 链(bootstrap_phone_auth、_send_sms_code_sso → /send_activation_code/v2/、_phone_login_sso → /quick_login/v2/)。上游把它当显式开关,不是失败后的退路;catbus 对应 auth login --method sms --sso。
  • 上游凭证来源:.env 的 DY_COOKIES、DY_LIVE_COOKIES、DY_TICKET、DY_TS_SIGN、DY_CLIENT_CERT、DY_PRIVATE_KEY、DY_DTRAIT_BLOB、DY_DTRAIT_PROFILE(设备档案的路径)、DY_SESSION_DTRAIT(成品头,只用于非严格的请求,如 digg;发布、评论、收藏、收藏夹移动不接受)。
  • API(静态方法,第一个参数为 auth)
    • dy_apis/douyin_api.py 的 DouyinAPI:get_work_info(url)、get_user_info(user_url)、get_user_all_work_info、get_work_all_comment、search_general_work(五个筛选值 sort_type / publish_time / filter_duration / search_range / content_type 作为紧凑 JSON 放进 filter_selected,content_type 2 是图文 → --type image;search_source 为 tab_search)、search_video_work(视频频道,筛选值进 query,→ item search --type video)、search_user(douyin_user_fans / douyin_user_type)、search_live、get_user_favorite、get_collect_list(上游只取 cursor=0 的第一页;catbus 按响应的 cursor / has_more 翻页,--folder 也逐页找)、move_collect_aweme / remove_collect_aweme(→ item collect / uncollect --folder)、get_user_follower_list/following、get_notice_list(notice_group → notice list --group)、get_feed、get_live_info、digg、publish_comment、collect_aweme、create_conversation、send_msg/image/video/file、send_share_aweme/share_photos/share_web/user_card(→ msg send --share)
    • 直播:get_live_room_enter(→ live get,其中的 stream_url → live media)、get_webcast_detail(im/fetch,带回的最近 15 条 → live history)、get_live_contribution_rank / get_live_thousand_ticket_rank(→ live rank --ranking)、diggLiveRoom(count → live like --count)、sendMsgInRoom
    • 商品:get_live_production / get_all_live_production(直播间商品)、get_live_production_detail(→ product get)、get_product_comments(→ 商品评价,需要 product_id 和 shop_id;tag_id 来自 get_product_comment_counter,→ --label)
    • dy_apis/douyin_creator_api.py 的 DouyinCreatorAPI:post_images、post_video(poi / mix_id / hot_spot / allow_download / 图集 cover_index、cover_uri)、get_preview_video_list(work_list → item list)
    • TOS 上传:创作者中心的 upload_video_direct / upload_video_parts 与私信的 _upload_source 流程相同,catbus 合成一份 web/tos.ts;两边上游的差别(origin / referer、content-crc32 头的位置、uploadid 是否 quote、成功码的宽严)作为两套预设保留
    • dy_apis/douyin_im_media.py 的 DouyinIMMedia:upload_image / upload_video / upload_file,以及卡片的 build_share_aweme_content / build_share_photos_content / build_share_web_content / build_user_card_content
    • 没有移植:send_sticker(表情的 CDN 元数据要调用方给,上游没有取表情列表的接口)、send_audio(PC 端没有语音上传协议)、get_conversation_list、get_live_pk_*(AGENTS 4.12)
  • JS 与 proto 资产(无第三方 npm 依赖)
    • static/{Live,PK,Request,Response}.proto,旁边是对应的 pb2
    • utils/acrawler_runtime/:ac_vm.js、run_ac_node.js、browser_window_shape.json、canvas_actual_exact.json
    • utils/challenge_template_runner.js
    • utils/challenge_profile.json、utils/mstoken_common_profile.json、utils/mstoken_profile.json、utils/dtrait_profile.json
  • 注意:newsign/package.json 声明了 jsdom、canvas、sdenv、jsrsasign,但没有任何代码引用,是遗留文件,不用管。

tiktok — TiktokApis

  • 鉴权
    • builder/auth.py 的 TiktokAuth.from_cookie(cookie_str)。
    • 以存在 sessionid、sid_tt 或 multi_sids 之一判断已登录。
  • 登录:api/tiktok_login.py 的 TiktokLoginAPI 只有占位,调用即抛 NotImplementedError。所以只支持 cookie 登录。
  • 上游凭证来源:demo.py 读 TIKTOK_COOKIE。
  • API:api/tiktok.py 的 TiktokAPI,继承自 api/tiktok_web.py 的 TiktokWebAPI,方法接收 cookies_str:
    • get_user_posted(secUid)、get_user_info(user_url)、get_video_detail(video_url)、get_all_comments、post_comment
    • 商城:get_shop_product_detail(→ product get)、get_shop_product_reviews / get_all_shop_product_reviews(→ 商品评价)
    • 其他:收藏、关注与粉丝列表、post_item_digg、post_follow_user、私信(api/tiktok_chat.py)、creator_publish、creator_publish_photos
    • 收藏夹:post_collection_create / post_collection_modify_info 的 collection_status 是网页 IDL 的 CollectionStatus(1 私密、3 公开),上游默认 1;post_collection_modify_items(→ folder add)只能加入,commitIds 一次一个;post_collection_move_items 是在收藏夹之间移动(AGENTS 4.12 不提供);get_collection_candidate_item_list 只是网页「加入收藏夹」对话框的候选列表,不单独做命令
    • 通知:get_notice_multi 默认 group 500(动态:赞 / 评论 / @ / 关注),get_inbox_notice_list 默认 group 661(网页收件箱的系统通知 All,template_notice 结构),都 → notice list
    • 直播:enter_live_room、check_live_rooms 用于只给房间号的 live get / like / rank / send / media;拉流地址上游不解析,catbus 读 liveRoom.streamData 或房间对象的 stream_url
  • 不移植:_prepare_creator_media 里用 ffmpeg 读任意容器、截首帧(catbus 要求 --cover,只读 MP4);get_following_request_list;旧版私信 WS;直播页的活动 / 游戏 / podcast 等辅助接口
  • JS 与 proto 资产
    • static/Tiktok_Request_pb2.py:只有 pb2,没有 .proto。需要从 pb2 里的序列化 descriptor 导出 schema 给 protobufjs。
    • signing/env/:env_core.js、sign.js
    • signing/source/:webmssdk.js、webmssdk_ex.js、webmssdk_legacy.js
    • signing/sdk_config.json
    • reverse/tiktok_shop_bsid/env/:7 个 JS,商城签名用
    • 要移植成 TS 的 Python 签名:signing/ 下的 aws_v4.py、live_wire.py、protobuf.py、pure.py、shop_bsid.py、ticket_guard.py
  • 注意:上游用了 protobuf3_to_dict,移植后用 protobufjs 的 toObject 代替。

bilibili — BilibiliApis

  • 鉴权
    • builder/auth.py 的 BiliAuth:from_cookie、from_qrcode_login、from_sms_login、from_session(path)(读 session.json,支持自动刷新)、anonymous。
    • is_login 看 SESSDATA,csrf 取自 bili_jct。
  • 登录:apis/bili_login_apis.py 的 BiliLoginApi:qrcode_login、sms_login、password_login、refresh_cookies、logout。
    • refresh_cookies 对应 catbus 的自动刷新;logout 对应 auth logout 的服务端登出(10 个平台里有服务端登出的只有 B 站和快手)。
  • 上游凭证来源:仓库根目录的 session.json(utils/session.py 写入),cookies.txt 是兼容镜像。
  • API(静态方法,第一个参数为 auth)
    • apis/bili_apis.py 的 BiliApi:get_nav、search_type、get_video_info(bvid/aid)、get_video_detail、get_play_url、get_player_info、get_danmaku_seg、get_replies(oid)、get_user_info(mid)、get_user_videos、get_all_user_videos、get_rcmd_feed、get_popular、report_heartbeat
    • apis/bili_creator_apis.py:post_video、submit_archive(转载来源、同步动态、禁止转载)、delete_archive、get_my_archives、post_dynamic、remove_dynamic、save_article_draft、submit_article、get_article_draft、delete_article_draft
    • apis/bili_interact_apis.py:like、add_coin(also_like)、favour(add_media_ids, del_media_ids)、triple、add_reply(type_, root, parent)、delete_reply(type_)、send_danmaku(color, fontsize, mode)
    • apis/bili_live_apis.py:get_room_init、get_room_by_mid、get_room_info、send_gift、send_danmaku(color, mode, reply_mid, reply_uname)、start_live、stop_live
  • catbus 的对应(不直观的几处)
    • item related 取 get_video_detail 返回的 Related;item search --type article 是 search_type(search_type='article')。
    • 评论区类型按参数识别:稿件 1、专栏(cv 号)12、动态 17。动态只对纯文字和转发动态成立:图文动态的评论区是 type 11 + 相簿 rid,专栏动态是 type 12 + cv 号,从动态 ID 换过去要动态详情接口,上游没有,所以 comment list / add / delete 标 ◐。
    • item subtitles 用 get_player_info 返回的 subtitle.subtitles[].subtitle_url 下载字幕 JSON;上游的 get_subtitle_view 返回 protobuf,没有用。
    • folder items 是非上游的:上游没有收藏夹内容接口,按网页端补的 x/v3/fav/resource/list(api.favResources),没有对拍。item uncollect 不带 --folder 时,用 get_fav_folders 多带 type=2&rid=<aid> 的请求(非上游,api.favFoldersOf)按 fav_state 找收着它的收藏夹,没有 fav_state 时从全部收藏夹移出。
    • article publish:上游的 save_article_draft / submit_article 把 banner_url 固定为空串,没有专栏封面,所以没有 --cover。
    • upos 上传:上游四段都走 http_util.request,cookie 是整份 B 站 cookie,发往 upos 节点(bilivideo.com)时也带,catbus 照做。
    • 直播间参数传主播时,先用 get_room_by_mid 换房间号。
    • 推荐流翻页时,把上一批的 av_<aid>(已关注为 av_n_<aid>)作为 last_showlist 带上;格式见 bilibili-API-collect,上游只给了参数。
    • 长连接 live/server.py:op 3 的人气值和没有专门映射的 cmd 都输出为 Event other。
    • delete_archive 需要极验结果(validate / seccode / challenge),上游没有自动过验证,catbus 的 item delete 标 ◐,不带验证时报 RISK_CONTROL。
  • JS 资产
    • tools/sc_encrypt_bridge.js:apis/bili_gaia_apis.py:662 调用,做风控指纹的加密上报。
    • 它还需要 _gt/bili-sc-sdk.js(bili_gaia_apis.py:67)。这是从 CDN 下载的第三方 SDK,上游不入库,多数环境里不存在。缺它时上游只发明文上报,已实证写接口照样成功。
    • catbus 的做法:只移植明文上报这一路,不复制 sc_encrypt_bridge.js。
  • 验证码:极验点选,tools/geetest_solve.py、utils/geetest_hybrid.py、utils/geetest_metric.py、utils/geetest_ocr.py,依赖 ddddocr、scipy、numpy、Pillow。
    • catbus 的对应:web/geetest.ts(utils/geetest_w.py 的 w 加密,geetest_solve.py 的 fetch / auto / _post_ajax / _refresh / solve),web/geetest-vision.ts(geetest_ocr / geetest_metric / geetest_hybrid,匈牙利算法照抄 scipy 的 rectangular_lsap),web/ddddocr.ts(ddddocr 1.6.1 的检测与识别,模型在 @cv-cat/catbus-assets-ocr,PIL 的 LANCZOS 与转灰度重写成 TS),web/geetest-manual.ts(tools/geetest_helper.py 的 serve:自动识别失败时在终端里改为本地页面手动验证)。
    • RSA 段(PKCS#1 v1.5)的随机填充改从 core/rand.ts 取,对拍时 gen.py 把上游的 rsa_encrypt_key 换成用框架随机字节的同一套填充。
    • geetest_vision.prepare() 只是把切图落盘给人看,不影响请求,没有移植。

kuaishou — KuaiShou-Spider

  • 鉴权
    • builder/auth.py 的 KuaishouAuth:prepare_auth(cookie_str)、initialize()。
    • kwscode / kwssectoken 只有 6 分钟有效期,每次使用时现算,不落盘。
    • cookie 分三个站点:www、cp(创作者)、live。发布需要 kuaishou.web.cp.api_st 和 kuaishou.web.cp.api_ph。
  • 登录:ks_apis/login_api.py 的 KuaishouLoginAPI:login_by_qr、request_mobile_code、login_by_mobile_code、sts_login、pass_token_login、login_browser_session。
    • sts_login / pass_token_login 用来换取子站点凭证,登录时一并完成。
  • 服务端登出:ks_apis/live_api.py 的 logout_session(passport login/logout → /live_api/baseuser/userLogout)+ builder/auth.py 的 apply_live_logout。上游只放行一个写死的房间页 Referer。
  • 上游凭证来源:.env 的 KS_COOKIES。
  • API
    • ks_apis/kuaishou_api.py 的 KuaishouAPI:get_feed_hot(new-reco 推荐流 → feed list --kind recommend)、get_work_info、get_video_detail、get_comment_list、get_all_comment、get_sub_comment_list、get_profile、get_profile_feed、get_user_all_work、search_feed、search_user、get_relation、get_liked_list、get_collect_list、get_history_list、graphql
    • 直播:ks_apis/live_api.py 的 KuaishouLiveAPI(礼物:gift_list 首屏与 sort_type=0 的“更多礼物”,emoji_all_gifts 完整礼物字典);弹幕:ks_apis/live_ws.py 的 LiveDanmakuClient
    • 发布:ks_apis/publish_api.py 的 KuaishouPublishAPI;发布后 video_photo_list(query_type=2, post_publish=True) 取 publishId,video_publish_refresh 取发布状态
    • 翻页(catbus 补充):get_feed_hot 每页都回 pcursor="1",catbus 的游标写成 <pcursor>:<已取页数>,请求时只用 pcursor;video_photo_list 的时间范围服务端限一年,item list 在一年窗口里按 nextCursor 翻,翻完把窗口前移一年(游标 <cursor>:<窗口终点>),某个窗口没有作品时停止
    • 不在 _LIVE_CURRENT_PATHS 放行名单里的直播接口(comment_add、comment_like、profile_like、liveroom_status 等)上游运行时直接拒绝发包,不移植
  • 上游基线:当前移植的是 feat/fix-slider-fingerprint-http2 分支的 552cf60(还没合进上游 master),比 master d4fcfd0 多一个提交:刷新验证码指纹、gdfp 接受 HTTP/1.1。
  • 浏览器档案:utils/fingerprint.py 的 get_profile()(UA、平台、语言、时区、CPU、WebGL、屏幕 / 可用区 / 内外窗口几何)→ catbus web/profile.ts 的 PROFILE。请求头、gdfp manMachine 载荷、captchaExtraParam 都从它取值,不各自写死。
  • 滑块验证码:KuaishouAPI._post / _get / graphql 遇到 400002(GraphQL 是 errors + data.captcha.url)时调 _pass_captcha 过一次滑块,再重新序列化同一条 Cookie 线序重发。
    • builder/auth.py 的 prepare_captcha_context:验证码 iframe 以 verification-captcha 产品名单独引导 webweapon,Cookie 线序 captcha(kwpsecproductname 出现两次)
    • utils/captcha.py:find_gap_x(cv2 边缘图 + 带掩码的模板匹配,catbus 用 @techstark/opencv-js)、build_trajectory、SlidingCaptcha
    • utils/captcha_fp.py(gpuInfo / captchaExtraParam;key35 / key36 是稳定的浏览器哈希,只有 key18–key26 每次现算)、utils/gdfp_manmachine.py(SDK_INIT → core / whole /n/a/b)、utils/sign/captcha_crypto.py(三路 LFSR + 32 字节头)
    • 对应 catbus 的 web/captcha.ts、gap.ts、captcha-fp.ts、gdfp.ts、captcha-crypto.ts
    • 未移植:KuaishouAuth.set_captcha_fingerprint(传给 SlidingCaptcha 的 fingerprint,用浏览器现抓的 gpuInfo / captchaExtraParam 代替随包默认值):只有 Python API,没有 CLI 入口。catbus 始终用 captcha-fp.ts 的默认值。
  • HTTP 版本检查:上游对 gdfp(/s/w/c、manMachine、vision、gameLive)检查协商出的协议,现在默认接受 HTTP/1.1,设 KS_STRICT_GDFP_HTTP2=1 才只认 HTTP/2;utils/transport.py 的 _curl_http_version_value 也接受字符串形式的枚举值。catbus 不查协议版本(wreq-js 拿不到协商出的协议),与上游的默认行为一致。
  • JS 与 proto 资产
    • reverse/bundles/weapon/:21 个 JS(kwf,以及 kws-0 到 kws-19)
    • reverse/js/cp-kwf.js
    • reverse/tools/:kwf_oracle.js、kws_oracle.js、like_token_oracle.js
    • reverse/fixtures/live_ws_proto.json:直播 proto,可直接作为 protobufjs Root.fromJSON 的输入
    • reverse/fixtures/live_ws_maps.json:utils/live_proto.py 用到的映射表
  • Python 侧签名:utils/sign/weapon_oracle.py、like_token.py,要移植成 TS。

weibo — WeiboApis

  • 鉴权:没有鉴权对象,各方法直接接收 cookies_str。
  • 登录:上游没有,只支持 cookie 登录。
  • 上游凭证来源:无。仓库另带一个 FastAPI 服务 app.py,不移植。
  • API
    • apis/weibo_apis.py 的 WeiboApis:get_self_info、getUserInfo、getUserPosted、getWordComments、getWorkInfo、searchSome、get_user_all_posted
    • apis/weibo_mobile_apis.py 的 WeiboMobileApis(m.weibo.cn,上游不带 cookie;catbus 带访客 cookie,见 1.2):getWorkInfo(item get,comment list 缺作者 uid 时也用它查)、searchSome(item search:访客从第二页起是登录墙,只取第一页)。
    • apis/weibo_creator_apis.py 的 WeiboCreaterApis.post_weibo。type(请求里的 visible):0 公开、1 仅自己、6 朋友圈(互相关注)、10 粉丝,对应 --visibility public / private / friends / fans
  • 没有移植的
    • WeiboApis.getWorkInfo(url):请求 weibo.com/<uid>/<mblogid>,只取页面里的 window.$CONFIG。现在的 weibo.com 是单页应用,页面只是一个壳,$CONFIG 是全站配置(登录时多一个当前用户 user,get_self_info 取的就是它),没有微博正文;正文由前端另外请求 /ajax/statuses/show,上游没有这个接口。上游自己的 app.py 取详情也走 WeiboMobileApis.getWorkInfo。
    • WeiboApis.searchSome:只发请求,解析被注释掉,永远返回 None。
  • JS 资产:static/weibo.js 存在,但 Python 里没有引用,不复制。

xianyu — XianYuApis

  • 鉴权:没有独立的鉴权类。build_initial_cookies() 生成初始 cookie(含 tfstk,扫码登录前用),XianyuApis(cookies, device_id) 直接接收 cookie 字典;mtop 签名用 _m_h5_tk。
  • 登录:goofish_apis.py 里的函数 qrcode_login()。
  • 上游凭证来源:无。
  • API
    • goofish_apis.py 的 XianyuApis:get_token(私信长连的 accessToken)、refresh_token(mtop.taobao.idlemessage.pc.loginuser.get:cookie 登录的校验、auth status、user get me,以及 msg listen 每 10 分钟的续期,对应上游 user_alive)、get_item_info(item_id)、upload_media、get_public_channel(按标题和图片推荐类目,public 内部调用)、get_default_location、public(发布商品;price.original_price → 私有选项 --original-price)
    • 私信:goofish_live.py 的 XianyuLive(async WebSocket):create_chat(item_id 默认 891198795482,→ msg send --to;--to 加 --item 时用指定商品)、send_msg、list_all_conversations(cid)(→ msg history:同一条连接上按 nextCursor 翻页,结果从旧到新;--limit 截在一页中间时游标为 <这一页的起始游标>+<已输出条数>,续翻时重取这一页、跳过已输出的)、main / heart_beat(→ msg listen)
    • 连接、注册、ack、心跳、翻页与淘宝逐行相同,catbus 共用 src/platforms/_shared/impaas.ts
  • JS 资产(catbus 原样复制到 static/xianyu/)
    • static/goofish_js_version_2.js:utils/goofish_utils.py 经 PyExecJS 调用的 generate_sign、generate_mid、generate_uuid、generate_device_id、decrypt(base64 + MessagePack)
    • utils/gen_tfstk.js + utils/et_f.js:生成 cookie tfstk(上游起 node 子进程)
    • static/goofish_js_origin_version_2.js、static/goofish_js_version_1.js 上游没有用到,没有复制
  • 注意:上游用相对 CWD 的路径读 JS。catbus 从 static/xianyu/ 按包内路径加载,不受 CWD 影响。

taobao — TaoBaoApis

与闲鱼同属阿里系,结构几乎一致:mtop 签名 + 钉钉 wss 私信。

  • 鉴权:没有独立的鉴权类,TaobaoApis(cookies, device_id) 直接接收 cookie 字典。get_token() 调 mtop.taobao.login.token.get.h5,签名用 _m_h5_tk。
  • 登录:上游没有,只支持 cookie 登录。
  • 上游凭证来源:无。
  • API
    • taobao_apis.py 的 TaobaoApis:get_token、get_goods_uid_encrypt_uid(goods_url)(从商品页 HTML 里取卖家 uid / encrypt_uid,供 msg send --item 与 user get <商品链接> 用;上游没有按用户 ID 查询的接口)、upload_media
    • 私信:taobao_live.py 的 taobaoLive(cookies_str)(async WebSocket):list_all_conversations(cid)(→ msg history:同一条连接上按 nextCursor 翻页,结果从旧到新;--limit 截断时的游标同闲鱼)、create_chat、send_msg(sender_nick 是 cookie _nk_ 的原值,不解码)、main / heart_beat(→ msg listen)
    • 连接、注册、ack、心跳、翻页与闲鱼逐行相同,catbus 共用 src/platforms/_shared/impaas.ts
    • utils/taobao_utils.py:generate_sign、generate_mid、generate_uuid、generate_device_id、decrypt(实际是 base64 + MessagePack,由上游 JS 解码;import 了 blackboxprotobuf 但没用)、trans_cookies
  • JS 资产:static/taobao_js_20260407.js
  • 注意
    • 上游没有商品详情和商品搜索接口。item_detail_url 字段是从闲鱼代码带过来的,没有使用。矩阵里 item get / item search 是 ○。
    • 与闲鱼一样,上游用相对 CWD 的路径读 JS;catbus 按包内路径加载。

jd — JdApis

  • 鉴权
    • builder/auth.py 的 JdAuth:is_login 要求有登录票据 thor(PC 扫码下发)或 pt_key(M 端),再加上 pin(依次取 pin、pt_pin、_pst)。
    • 按 origin 保存 localStorage,catbus 里放在凭证文件的 device。
    • update_cookies(persist=)、flush()。
  • 登录:jd_apis/jd_login_api.py 的 JdLoginAPI.qr_login,以及 jd_apis/jd_sms_login_api.py 的 JdSmsLoginAPI.login。
  • 上游凭证来源:default_auth_path(),可用 JDAPIS_AUTH_FILE 覆盖。
    • Windows:%LOCALAPPDATA%/JdApis/auth.json
    • macOS:~/Library/Application Support/JdApis/auth.json
    • Linux:$XDG_STATE_HOME/JdApis/auth.json
  • API:jd_apis/jd_api.py 的 JdAPI
    • get_product_detail(sku)、get_product_comments、search、search_wares、get_browse_history、get_follow_products、get_cart_num、check_session、diagnose
    • get_order_list(:1348,→ 扩展 order list)、get_recommend_coupon(sku)(:929,→ 扩展 coupon list)
    • 客服:聊天相关方法,以及 jd_apis/jd_chat_ws.py 的 JdChatWS
    • 参数对应:各接口的 area → 私有选项 --area;get_order_list(date_range) 的 1 / 2 / 年份 → order list --range 3m|this_year|<年份>;get_product_comments(count) → comment list --limit;get_chat_info / send_hello / send_text 的 order_id → msg send --order;短信登录 login(area_code) 的国家码 → 从 --phone 的 + / 00 前缀识别
    • diagnose(:800):业务接口重试后仍 403 空 body 时只跑第一步 check_session,失效报 AUTH_EXPIRED,仍登录报 RISK_CONTROL(rate_limit);不再发 hotwords / getCartNum 探针
    • call_api 命中 _read_disposal(:413,处置常常只在 x-rp-content 响应头里,body 为空、状态码 200)时照样返回原始结果:catbus 把解开的处置挂到结果上,报 RISK_CONTROL(captcha)
    • 上游只返回原始 JSON、不判业务失败。catbus 的映射:非 JSON 响应、网关错误 {code, echo}(如 601)、success: false → UPSTREAM;登录墙(code 3、购物车的 pin is null、浏览历史的 resultCode: -100)→ AUTH_EXPIRED。code 非 0 只对核对过正常值的接口(hotwords、getAidInfo、getChatSessionLog)判失败,其余接口正常响应里 code 的含义没核对过(getRecommendCoupon 会回只有 {"code":"1"} 的响应)
    • 没有移植:get_diviner(:942)的首请求要 securityToken,分页变体要「类目 p」和 shopId,上游注释要求传浏览器抓包值、没有调用处,也没有从详情接口推出 p 的方法,所以 item related 仍用 relsearch 的相关词搜索
  • JS 资产(npm 依赖 jsdom、@napi-rs/canvas)
    • static/:h5st5_env.js、h5st5_lib.js、h5st5_server.js、pc_tk_lib.js、pc_tk_server.js、summer_cryptico_runner.js
    • static/webm/env/run.js、static/webm/run/jdwebm-riskhandle.js
    • static/jcap/env/:env_core.js、run.js;网络桥 http_bridge.py 移植为 src/platforms/jd/web/jcap/helper.ts 的 bridge
    • static/jcap/run/:jcap_ujb96b.js;图像求解 captcha_solver.py 移植为 jcap/solver.ts(由 helper.ts 的 solve 调用)。run.js 起这两个 Python 子进程的地方由预加载的 jcap/host.ts 换成 helper.js
    • static/jcap/run/models/:orientation_model_v2_0.9882.onnx、u2netp.onnx,共约 81 MB,放进 @cv-cat/catbus-assets-jd,不复制到 static/
    • 调用这些 JS 的 Python:utils/h5st5.py、utils/device_token.py、utils/jcap_solver.py、utils/summer_cryptico.py、utils/webm.py
  • 注意:上游 JS 用 createRequire(<项目根>/package.json) 解析 npm 模块。catbus 在 vm 里注入的 require 指向自己的 node_modules(见 AGENTS.md 7.4)。
  • 验证码:JCAP,依赖 onnxruntime、opencv、scikit-image、scipy。

x — XApis

  • 鉴权
    • builder/auth.py 的 XAuth.prepare_auth(cookies_str),需要 auth_token 和 ct0。
    • 支持 guest token(见 1.2),web 端不支持游客态,不移植;prepare_auth 未登录时本地生成 ct0 的分支同样不移植。
  • 登录:x_apis/login_api.py
    • XLoginApi.login_by_password
    • XJetfuelLoginApi:login_by_password、login_by_password_pure,后者需要 Castle profile。
    • 账密登录不移植(依赖 Castle token):catbus 只支持从浏览器导入 cookie。
  • 上游凭证来源:.env 的 X_COOKIES、X_USERNAME、X_PASSWORD、X_PROXY,以及 Castle profile 的 CASTLE_PURE_RL_FILE、CASTLE_PURE_ONLY。
  • API(静态方法,第一个参数为 auth)
    • x_apis/x_api.py 的 XAPI:get_work_info、get_work_comments、get_all_work_comments、search_work、get_user_info(user_name)、get_user_post_note、get_user_all_post_note、get_home_timeline、get_viewer
      • search_work 的 product:Top / Latest 对应 item search --sort general|latest,People 对应 user search,Media 对应 item search --type video|image(结果里图片和视频混在一起,catbus 按 Item.kind 过滤);Lists 不做。
    • x_apis/x_write_api.py 的 XWriteAPI:post_tweet(正文权重超过 280 时走 create_note_tweet,即 CreateNoteTweet 长推)、post_thread、delete_tweet、favorite、retweet、bookmark、follow/unfollow
      • 权重算法在 utils/x_util.py 的 tweet_weight(twitter-text v3)。
      • create_tweet 的 quote_url 对应 item publish --quote,post_thread 对应 item publish --thread。comment add 也走 post_tweet(带 reply_to,同 main.py post --reply-to),超长时同样自动长推。
      • _rest_post(关注)的业务拒绝(158 / 160 / 161 / 162)是 HTTP 403 + errors,catbus 先按错误码映射,没有错误码的 401 / 403 才当作登录态失效。
    • x_apis/x_article_api.py 的 XArticleAPI(文章,Premium 长文):create_draft、update_title、update_content、update_cover、publish、delete、list_articles、upload_image,编排入口 post_article
      • 对应扩展命令 article publish(post_article 的 publish=True)、article delete;list_articles(草稿 / 已发布列表)没有暴露。
      • Markdown → Draft.js content_state 在 utils/article_util.py,catbus 移植到 web/article.ts。
      • 正文插图的相对路径按 base_dir(main.py 传 Markdown 文件所在目录)解析:catbus 用 --text @file 的文件目录(dispatch 记在 textFile)。上游的封面也按 base_dir 解析,catbus 的 --cover 是命令行参数,按当前目录。
      • extract_tweet_id(发布时生成的文章推文):catbus 在 stderr 提示这条推文的链接,输出仍是文章的 {id url}。
    • 媒体上传:x_apis/x_media_api.py 的 XMediaAPI.upload(with_metadata=False 时不登记元数据,文章图片用)
    • 私信:x_apis/x_dm_api.py 的 XChatAPI:get_initial_chat_page(msg list)、get_conversation_page(msg history)、get_users_by_ids(msg list 补对方资料)
      • get_initial_chat_page 的 max_local_sequence_id / message_pull_version 是增量同步的水位(取某个序号之后的新消息事件),不是翻页游标。翻更早的会话要 GetInboxPageRequestQuery(continue_cursor),上游没有,所以 msg list 只取首页。
  • JS 资产(账密登录不移植,Castle 与 ui_metrics 不复制)
    • static/castle/:castle.js、castle.umd-BneRArir.js、env_core.js、rolldown-runtime-XXLRXQBO.js、run.js、castle_meta.json、chromium_zlib.meta.json
    • static/ui_metrics.js
    • static/graphql.json(GraphQL 操作表)、static/transaction_l1.json
    • 调用这些 JS 的 Python:utils/castle_token.py、utils/ui_metrics.py