10 个平台的 web 端全部用 TypeScript 移植自 cv-cat 的开源项目,请求与上游逐字节对拍。 统一的命令语法与 JSON 输出、按 平台 × 端 × 账号 隔离的登录态、给 AI Agent 用的技能与 llms.txt。
39 KiB
39 KiB
上游移植对照(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。
- HTTP 以
-
签名 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 |
上游没有: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 的creatorscope。
- 登录:
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/imgapis/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 存进creatorscope 复用,失效时自动重新桥接一次。桥接在凭证副本上做,全部成功后才写回,中途失败时凭证不变 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 的 JSONxhs_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 的 JSONxhs_utils/xhs_creator/js/:xhs_creator_sign.js、xhs_creator_signature.js、profile.js,以及指纹 / profile 的 JSON
- 注意:拿笔记详情需要
xsec_token,所以优先传 URL;catbus 输出的 xhsurl都带上它。
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。
- 短信默认走 passport-web(
- 上游凭证来源:
.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_type2 是图文 →--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,旁边是对应的 pb2utils/acrawler_runtime/:ac_vm.js、run_ac_node.js、browser_window_shape.json、canvas_actual_exact.jsonutils/challenge_template_runner.jsutils/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.jssigning/source/:webmssdk.js、webmssdk_ex.js、webmssdk_legacy.jssigning/sdk_config.jsonreverse/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_heartbeatapis/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_draftapis/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 都输出为 Eventother。 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()只是把切图落盘给人看,不影响请求,没有移植。
- catbus 的对应:
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(passportlogin/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、屏幕 / 可用区 / 内外窗口几何)→ catbusweb/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、SlidingCaptchautils/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.jsreverse/tools/:kwf_oracle.js、kws_oracle.js、like_token_oracle.jsreverse/fixtures/live_ws_proto.json:直播 proto,可直接作为 protobufjsRoot.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_postedapis/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:生成 cookietfstk(上游起 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
- Windows:
- API:
jd_apis/jd_api.py的JdAPIget_product_detail(sku)、get_product_comments、search、search_wares、get_browse_history、get_follow_products、get_cart_num、check_session、diagnoseget_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.jsstatic/webm/env/run.js、static/webm/run/jdwebm-riskhandle.jsstatic/jcap/env/:env_core.js、run.js;网络桥http_bridge.py移植为src/platforms/jd/web/jcap/helper.ts的bridgestatic/jcap/run/:jcap_ujb96b.js;图像求解captcha_solver.py移植为jcap/solver.ts(由helper.ts的solve调用)。run.js 起这两个 Python 子进程的地方由预加载的jcap/host.ts换成helper.jsstatic/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.pyXLoginApi.login_by_passwordXJetfuelLoginApi: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_viewersearch_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.jsonstatic/ui_metrics.jsstatic/graphql.json(GraphQL 操作表)、static/transaction_l1.json- 调用这些 JS 的 Python:
utils/castle_token.py、utils/ui_metrics.py