specific-ai-cli 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.en.md +177 -0
- package/README.ja.md +177 -0
- package/README.ko.md +177 -0
- package/README.md +174 -2
- package/README.zh-TW.md +177 -0
- package/assets/banner.txt +3 -0
- package/dist/cli/argv.js +98 -0
- package/dist/cli/commands/auth.js +11 -0
- package/dist/cli/commands/config.js +23 -0
- package/dist/cli/commands/doctor.js +134 -0
- package/dist/cli/commands/platforms.js +24 -0
- package/dist/cli/commands/version.js +4 -0
- package/dist/cli/confirm.js +22 -0
- package/dist/cli/dispatch.js +440 -0
- package/dist/cli/help.js +223 -0
- package/dist/cli/hints.js +60 -0
- package/dist/cli/main.js +18 -0
- package/dist/cli/output.js +66 -0
- package/dist/core/auth-handlers.js +41 -0
- package/dist/core/auth-store.js +119 -0
- package/dist/core/capabilities.js +163 -0
- package/dist/core/config.js +121 -0
- package/dist/core/cookies.js +164 -0
- package/dist/core/errors.js +37 -0
- package/dist/core/files.js +89 -0
- package/dist/core/fsutil.js +52 -0
- package/dist/core/hash.js +4 -0
- package/dist/core/http.js +331 -0
- package/dist/core/image.js +280 -0
- package/dist/core/log.js +40 -0
- package/dist/core/login.js +191 -0
- package/dist/core/mp4.js +130 -0
- package/dist/core/normalize.js +207 -0
- package/dist/core/options.js +110 -0
- package/dist/core/paths.js +38 -0
- package/dist/core/pb.js +59 -0
- package/dist/core/py.js +181 -0
- package/dist/core/rand.js +131 -0
- package/dist/core/registry.js +129 -0
- package/dist/core/schemas.js +41 -0
- package/dist/core/stream.js +104 -0
- package/dist/core/toolkit.js +35 -0
- package/dist/core/vm.js +73 -0
- package/dist/core/vocab.js +157 -0
- package/dist/platforms/_shared/impaas.js +410 -0
- package/dist/platforms/bilibili/index.js +216 -0
- package/dist/platforms/bilibili/web/api.js +806 -0
- package/dist/platforms/bilibili/web/client.js +311 -0
- package/dist/platforms/bilibili/web/commands.js +783 -0
- package/dist/platforms/bilibili/web/ddddocr.js +342 -0
- package/dist/platforms/bilibili/web/gaia.js +130 -0
- package/dist/platforms/bilibili/web/geetest-manual.js +154 -0
- package/dist/platforms/bilibili/web/geetest-vision.js +232 -0
- package/dist/platforms/bilibili/web/geetest.js +450 -0
- package/dist/platforms/bilibili/web/normalize.js +234 -0
- package/dist/platforms/bilibili/web/profile.js +135 -0
- package/dist/platforms/bilibili/web/resolve.js +112 -0
- package/dist/platforms/bilibili/web/sign.js +223 -0
- package/dist/platforms/bilibili/web/upos.js +102 -0
- package/dist/platforms/douyin/index.js +142 -0
- package/dist/platforms/douyin/web/api.js +863 -0
- package/dist/platforms/douyin/web/client.js +413 -0
- package/dist/platforms/douyin/web/commands.js +712 -0
- package/dist/platforms/douyin/web/creator.js +601 -0
- package/dist/platforms/douyin/web/crypto.js +247 -0
- package/dist/platforms/douyin/web/dtrait.js +190 -0
- package/dist/platforms/douyin/web/im.js +502 -0
- package/dist/platforms/douyin/web/image.js +5 -0
- package/dist/platforms/douyin/web/live.js +225 -0
- package/dist/platforms/douyin/web/mssdk.js +148 -0
- package/dist/platforms/douyin/web/normalize.js +326 -0
- package/dist/platforms/douyin/web/passport.js +1247 -0
- package/dist/platforms/douyin/web/profile.js +221 -0
- package/dist/platforms/douyin/web/proto.js +26 -0
- package/dist/platforms/douyin/web/resolve.js +88 -0
- package/dist/platforms/douyin/web/sign.js +436 -0
- package/dist/platforms/douyin/web/tos.js +78 -0
- package/dist/platforms/index.js +17 -0
- package/dist/platforms/jd/index.js +67 -0
- package/dist/platforms/jd/web/aks.js +115 -0
- package/dist/platforms/jd/web/api.js +549 -0
- package/dist/platforms/jd/web/chat.js +132 -0
- package/dist/platforms/jd/web/client.js +470 -0
- package/dist/platforms/jd/web/commands.js +557 -0
- package/dist/platforms/jd/web/jcap/helper.js +90 -0
- package/dist/platforms/jd/web/jcap/host.js +107 -0
- package/dist/platforms/jd/web/jcap/lsd.js +300 -0
- package/dist/platforms/jd/web/jcap/solver.js +995 -0
- package/dist/platforms/jd/web/jcap.js +150 -0
- package/dist/platforms/jd/web/js.js +420 -0
- package/dist/platforms/jd/web/login.js +775 -0
- package/dist/platforms/jd/web/normalize.js +297 -0
- package/dist/platforms/jd/web/profile.js +256 -0
- package/dist/platforms/jd/web/util.js +175 -0
- package/dist/platforms/jd/web/webm.js +133 -0
- package/dist/platforms/kuaishou/index.js +98 -0
- package/dist/platforms/kuaishou/web/api.js +694 -0
- package/dist/platforms/kuaishou/web/captcha-crypto.js +134 -0
- package/dist/platforms/kuaishou/web/captcha-fp.js +180 -0
- package/dist/platforms/kuaishou/web/captcha.js +273 -0
- package/dist/platforms/kuaishou/web/client.js +487 -0
- package/dist/platforms/kuaishou/web/commands.js +863 -0
- package/dist/platforms/kuaishou/web/gap.js +144 -0
- package/dist/platforms/kuaishou/web/gdfp.js +376 -0
- package/dist/platforms/kuaishou/web/gql.js +11 -0
- package/dist/platforms/kuaishou/web/live.js +125 -0
- package/dist/platforms/kuaishou/web/normalize.js +241 -0
- package/dist/platforms/kuaishou/web/oracle.js +329 -0
- package/dist/platforms/kuaishou/web/profile.js +259 -0
- package/dist/platforms/kuaishou/web/resolve.js +84 -0
- package/dist/platforms/kuaishou/web/session.js +592 -0
- package/dist/platforms/kuaishou/web/sign.js +568 -0
- package/dist/platforms/taobao/index.js +56 -0
- package/dist/platforms/taobao/web/api.js +96 -0
- package/dist/platforms/taobao/web/client.js +102 -0
- package/dist/platforms/taobao/web/commands.js +172 -0
- package/dist/platforms/taobao/web/im.js +68 -0
- package/dist/platforms/taobao/web/normalize.js +103 -0
- package/dist/platforms/taobao/web/profile.js +85 -0
- package/dist/platforms/taobao/web/resolve.js +51 -0
- package/dist/platforms/taobao/web/sign.js +35 -0
- package/dist/platforms/tiktok/index.js +120 -0
- package/dist/platforms/tiktok/web/api.js +1234 -0
- package/dist/platforms/tiktok/web/client.js +362 -0
- package/dist/platforms/tiktok/web/commands.js +821 -0
- package/dist/platforms/tiktok/web/im.js +133 -0
- package/dist/platforms/tiktok/web/jsrun.js +126 -0
- package/dist/platforms/tiktok/web/normalize.js +286 -0
- package/dist/platforms/tiktok/web/profile.js +169 -0
- package/dist/platforms/tiktok/web/resolve.js +156 -0
- package/dist/platforms/tiktok/web/sign.js +585 -0
- package/dist/platforms/tiktok/web/upload.js +805 -0
- package/dist/platforms/tiktok/web/wire.js +490 -0
- package/dist/platforms/weibo/index.js +91 -0
- package/dist/platforms/weibo/web/api.js +206 -0
- package/dist/platforms/weibo/web/client.js +155 -0
- package/dist/platforms/weibo/web/commands.js +202 -0
- package/dist/platforms/weibo/web/normalize.js +137 -0
- package/dist/platforms/weibo/web/profile.js +246 -0
- package/dist/platforms/weibo/web/resolve.js +60 -0
- package/dist/platforms/weibo/web/sign.js +85 -0
- package/dist/platforms/x/index.js +107 -0
- package/dist/platforms/x/web/api.js +443 -0
- package/dist/platforms/x/web/article.js +212 -0
- package/dist/platforms/x/web/client.js +202 -0
- package/dist/platforms/x/web/commands.js +363 -0
- package/dist/platforms/x/web/graphql.js +108 -0
- package/dist/platforms/x/web/normalize.js +235 -0
- package/dist/platforms/x/web/profile.js +99 -0
- package/dist/platforms/x/web/resolve.js +44 -0
- package/dist/platforms/x/web/transaction.js +171 -0
- package/dist/platforms/xhs/index.js +101 -0
- package/dist/platforms/xhs/web/api.js +278 -0
- package/dist/platforms/xhs/web/client.js +540 -0
- package/dist/platforms/xhs/web/commands.js +1046 -0
- package/dist/platforms/xhs/web/creator-api.js +609 -0
- package/dist/platforms/xhs/web/creator.js +203 -0
- package/dist/platforms/xhs/web/js.js +194 -0
- package/dist/platforms/xhs/web/login.js +230 -0
- package/dist/platforms/xhs/web/normalize.js +166 -0
- package/dist/platforms/xhs/web/profile.js +127 -0
- package/dist/platforms/xhs/web/push.js +361 -0
- package/dist/platforms/xhs/web/resolve.js +69 -0
- package/dist/platforms/xhs/web/state.js +368 -0
- package/dist/platforms/xianyu/index.js +59 -0
- package/dist/platforms/xianyu/web/api.js +312 -0
- package/dist/platforms/xianyu/web/client.js +134 -0
- package/dist/platforms/xianyu/web/commands.js +305 -0
- package/dist/platforms/xianyu/web/im.js +73 -0
- package/dist/platforms/xianyu/web/normalize.js +123 -0
- package/dist/platforms/xianyu/web/profile.js +146 -0
- package/dist/platforms/xianyu/web/resolve.js +48 -0
- package/dist/platforms/xianyu/web/sign.js +79 -0
- package/package.json +77 -4
- package/static/douyin/Live.proto +194 -0
- package/static/douyin/Request.proto +89 -0
- package/static/douyin/Response.proto +46 -0
- package/static/douyin/acrawler_runtime/ac_vm.js +2 -0
- package/static/douyin/acrawler_runtime/browser_window_shape.json +1 -0
- package/static/douyin/acrawler_runtime/canvas_actual_exact.json +1 -0
- package/static/douyin/acrawler_runtime/run_ac_node.js +1005 -0
- package/static/douyin/acrawler_runtime/runner_window_keys_baseline.txt +240 -0
- package/static/douyin/challenge_profile.json +801 -0
- package/static/douyin/challenge_template_runner.js +217 -0
- package/static/douyin/dtrait_profile.json +66 -0
- package/static/douyin/mstoken_common_profile.json +1 -0
- package/static/douyin/mstoken_profile.json +214 -0
- package/static/jd/h5st5_env.js +750 -0
- package/static/jd/h5st5_lib.js +5 -0
- package/static/jd/h5st5_server.js +49 -0
- package/static/jd/jcap/env/env_core.js +512 -0
- package/static/jd/jcap/env/run.js +1307 -0
- package/static/jd/jcap/run/jcap_ujb96b.js +39 -0
- package/static/jd/package.json +4 -0
- package/static/jd/pc_tk_lib.js +1 -0
- package/static/jd/pc_tk_server.js +63 -0
- package/static/jd/summer_cryptico_runner.js +50 -0
- package/static/jd/webm/env/run.js +453 -0
- package/static/jd/webm/run/jdwebm-riskhandle.js +49 -0
- package/static/kuaishou/bundles/weapon/kwf-0.0.2.2cee19b4b7dec496.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-0-0.0.1-obfuscated.4b7f3de1ab0ddcc2.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-1-0.0.1-obfuscated.346ebdb1c78df316.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-10-0.0.1-obfuscated.a33db41897b983af.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-11-0.0.1-obfuscated.5e0a90af726d8a7e.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-12-0.0.1-obfuscated.20ea29345706ec8c.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-13-0.0.1-obfuscated.6b74e9640ff18648.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-14-0.0.1-obfuscated.551e4429d156bf66.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-15-0.0.1-obfuscated.8625b087aaba4a60.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-16-0.0.1-obfuscated.d5ffa4020f3be99a.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-17-0.0.1-obfuscated.6d13e19b22f50a80.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-18-0.0.1-obfuscated.6d31405044166289.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-19-0.0.1-obfuscated.a9a8d925942f19e4.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-2-0.0.1-obfuscated.b849a136ae4a6b3e.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-3-0.0.1-obfuscated.f9e77ebf6186e2ac.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-4-0.0.1-obfuscated.2c3c155aa52ceb0e.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-5-0.0.1-obfuscated.4e553d17691b3c5a.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-6-0.0.1-obfuscated.f73ddd3ff13e1217.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-7-0.0.1-obfuscated.1628b8729beaab7e.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-8-0.0.1-obfuscated.6da6baef7aa81fff.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-9-0.0.1-obfuscated.8645d55e5323b743.js +1 -0
- package/static/kuaishou/fixtures/current_follow_script_813.network-response +2 -0
- package/static/kuaishou/fixtures/live_ws_maps.json +61 -0
- package/static/kuaishou/fixtures/live_ws_proto.json +1342 -0
- package/static/kuaishou/js/cp-kwf.js +1 -0
- package/static/kuaishou/tools/kwf_oracle.js +799 -0
- package/static/kuaishou/tools/kws_oracle.js +885 -0
- package/static/kuaishou/tools/like_token_oracle.js +70 -0
- package/static/taobao/taobao_js_20260407.js +570 -0
- package/static/tiktok/Tiktok_Request.json +597 -0
- package/static/tiktok/package.json +3 -0
- package/static/tiktok/shop_bsid/core.js +1 -0
- package/static/tiktok/shop_bsid/env_core.js +512 -0
- package/static/tiktok/shop_bsid/loader.js +1 -0
- package/static/tiktok/shop_bsid/run.js +753 -0
- package/static/tiktok/shop_bsid/sign.js +100 -0
- package/static/tiktok/shop_bsid/webmssdk.js +1 -0
- package/static/tiktok/shop_bsid/webmssdk_ex.js +1 -0
- package/static/tiktok/signing/env/env_core.js +512 -0
- package/static/tiktok/signing/env/sign.js +455 -0
- package/static/tiktok/signing/sdk_config.json +230 -0
- package/static/tiktok/signing/source/webmssdk.js +1 -0
- package/static/tiktok/signing/source/webmssdk_ex.js +1 -0
- package/static/tiktok/signing/source/webmssdk_legacy.js +1 -0
- package/static/x/graphql.json +7777 -0
- package/static/x/transaction_l1.json +12 -0
- package/static/xhs/package.json +3 -0
- package/static/xhs/xhs_core/js/b1.js +210 -0
- package/static/xhs/xhs_core/js/mns.js +340 -0
- package/static/xhs/xhs_core/js/mns_0101_keystream.json +5 -0
- package/static/xhs/xhs_core/js/mns_keystreams.json +1 -0
- package/static/xhs/xhs_core/js/sign.js +225 -0
- package/static/xhs/xhs_core/js/websectiga_cli.js +220 -0
- package/static/xhs/xhs_core/js/websectiga_env.js +512 -0
- package/static/xhs/xhs_core/js/xs_common.js +79 -0
- package/static/xhs/xhs_creator/js/profile.js +115 -0
- package/static/xhs/xhs_creator/js/rap_fingerprint_creator.json +6 -0
- package/static/xhs/xhs_creator/js/reference_profile.json +282 -0
- package/static/xhs/xhs_creator/js/xhs_creator_sign.js +29 -0
- package/static/xhs/xhs_creator/js/xhs_creator_signature.js +23 -0
- package/static/xhs/xhs_pc/js/aes_decrypt.js +22 -0
- package/static/xhs/xhs_pc/js/aes_encrypt.js +30 -0
- package/static/xhs/xhs_pc/js/b1.js +9 -0
- package/static/xhs/xhs_pc/js/deflate.js +320 -0
- package/static/xhs/xhs_pc/js/mns.js +4 -0
- package/static/xhs/xhs_pc/js/profile.js +141 -0
- package/static/xhs/xhs_pc/js/rap.js +173 -0
- package/static/xhs/xhs_pc/js/rap_cli.js +30 -0
- package/static/xhs/xhs_pc/js/rap_crypto.js +73 -0
- package/static/xhs/xhs_pc/js/rap_fingerprint_template.json +8 -0
- package/static/xhs/xhs_pc/js/reference_profile.json +262 -0
- package/static/xhs/xhs_pc/js/sign.js +6 -0
- package/static/xhs/xhs_pc/js/web_ssk.js +82 -0
- package/static/xhs/xhs_pc/js/xs_common.js +4 -0
- package/static/xianyu/et_f.js +2 -0
- package/static/xianyu/gen_tfstk.js +410 -0
- package/static/xianyu/goofish_js_version_2.js +568 -0
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, createHash } from 'node:crypto';
|
|
2
|
+
import * as rand from '../../../core/rand.js';
|
|
3
|
+
/**
|
|
4
|
+
* 快手的纯算签名(上游 utils/sign/jsval.py、falcon_pure.py、sig3_pure.py、kww_pure.py、webweapon_boot.py)。
|
|
5
|
+
*
|
|
6
|
+
* - `__NS_hxfalcon`(sig4):www 白名单接口、直播站、登录站、cp 少量接口,配套 `caver=2`;
|
|
7
|
+
* - `__NS_sig3`:cp 创作者中心 `/rest/cp`、`rest/v2/creator`、`/rest/kd`;
|
|
8
|
+
* - webweapon 的 AES(`st$1` 与 gdfp `/s/w/c` 的加解密)。
|
|
9
|
+
*
|
|
10
|
+
* 签名器是有状态的(启动时间、调用计数都进签名),对应浏览器的一个页面会话:一条 specific-ai 命令一个实例。
|
|
11
|
+
*/
|
|
12
|
+
// ================================================================ JS 值语义(jsval.py)
|
|
13
|
+
/** ECMAScript ToString(`"" + value`)。 */
|
|
14
|
+
export function jsToString(value) {
|
|
15
|
+
if (value === null || value === undefined)
|
|
16
|
+
return 'null';
|
|
17
|
+
if (typeof value === 'boolean')
|
|
18
|
+
return value ? 'true' : 'false';
|
|
19
|
+
if (typeof value === 'string')
|
|
20
|
+
return value;
|
|
21
|
+
if (typeof value === 'number' || typeof value === 'bigint')
|
|
22
|
+
return String(value);
|
|
23
|
+
if (Array.isArray(value))
|
|
24
|
+
return value.map((v) => (v == null ? '' : jsToString(v))).join(',');
|
|
25
|
+
return '[object Object]';
|
|
26
|
+
}
|
|
27
|
+
/** JSON.stringify(无缩进)。JS 对象的键序本身就是 ECMAScript 的枚举序,与 jsval 的移植一致。 */
|
|
28
|
+
export function jsJson(value) {
|
|
29
|
+
return JSON.stringify(value ?? null);
|
|
30
|
+
}
|
|
31
|
+
/** axios 的 query 编码:encodeURIComponent 后把 `: $ , [ ]` 还原、`%20` 换成 `+`。 */
|
|
32
|
+
export function axiosEncode(value) {
|
|
33
|
+
return encodeURIComponent(jsToString(value))
|
|
34
|
+
.replace(/%3A/gi, ':')
|
|
35
|
+
.replace(/%24/g, '$')
|
|
36
|
+
.replace(/%2C/gi, ',')
|
|
37
|
+
.replace(/%5B/gi, '[')
|
|
38
|
+
.replace(/%5D/gi, ']')
|
|
39
|
+
.replace(/%20/g, '+');
|
|
40
|
+
}
|
|
41
|
+
/** Params.to_query_string:按插入顺序,axios 口径编码。 */
|
|
42
|
+
export function axiosQuery(params) {
|
|
43
|
+
return params.map(([k, v]) => `${axiosEncode(k)}=${axiosEncode(v)}`).join('&');
|
|
44
|
+
}
|
|
45
|
+
// ================================================================ sig4:__NS_hxfalcon(falcon_pure.py)
|
|
46
|
+
export const CAVER = '2';
|
|
47
|
+
/** www 侧 sig4 白名单。 */
|
|
48
|
+
const SIG4_WHITELIST = new Set([
|
|
49
|
+
'/rest/v/profile/get',
|
|
50
|
+
'/rest/v/profile/user/v2',
|
|
51
|
+
'/rest/v/search/user',
|
|
52
|
+
'/rest/v/search/feed',
|
|
53
|
+
'/rest/v/profile/feed',
|
|
54
|
+
'/rest/v/feed/hot',
|
|
55
|
+
'/rest/v/feed/liked',
|
|
56
|
+
'/rest/v/collect/list',
|
|
57
|
+
'/rest/v/profile/private/list',
|
|
58
|
+
]);
|
|
59
|
+
/** 直播站 sig4 名单:签名输入用网关背后的 realUrl(/rest/k/*),发出去的仍是 /live_api/*。 */
|
|
60
|
+
export const LIVE_URL_MAP = {
|
|
61
|
+
'/live_api/baseuser/userinfo/sensitive': '/rest/k/user/info/sensitive',
|
|
62
|
+
'/live_api/search/author': '/rest/k/live/search/user',
|
|
63
|
+
'/live_api/comment/list': '/rest/k/photo/comment/list',
|
|
64
|
+
'/live_api/search/overview': '/rest/k/live/search',
|
|
65
|
+
'/live_api/search/category': '/rest/k/live/game/search/category',
|
|
66
|
+
'/live_api/search/liveStream': '/rest/k/live/game/search/liveStream',
|
|
67
|
+
'/live_api/profile/public': '/rest/k/feed/profile',
|
|
68
|
+
'/live_api/profile/private': '/rest/k/feed/profile',
|
|
69
|
+
'/live_api/profile/liked': '/rest/k/feed/liked',
|
|
70
|
+
'/live_api/web/header/searchHotUserListQuery': '/rest/k/live/search/hot',
|
|
71
|
+
'/live_api/web/header/searchSuggestQuery': '/rest/k/live/search/suggest',
|
|
72
|
+
'/live_api/liveroom/websocketinfo': '/rest/k/live/websocket/info',
|
|
73
|
+
'/live_api/profileInterestMask/list': '/rest/k/pc-live/author/category',
|
|
74
|
+
'/live_api/profile/feedbyid': '/rest/k/photo',
|
|
75
|
+
'/live_api/profile/likestatus': '/rest/k/photo',
|
|
76
|
+
'/live_api/playback/list': '/rest/k/playback/product/list',
|
|
77
|
+
'/live_api/baseuser/author/checkfollow': '/rest/k/user/info',
|
|
78
|
+
'/live_api/baseuser/userinfo/byid': '/rest/k/user/info',
|
|
79
|
+
'/live_api/baseuser/userLogin': '/rest/k/user/info',
|
|
80
|
+
'/live_api/follow/all': '/rest/k/live/relation/follower',
|
|
81
|
+
'/live_api/gameboard/list': '/rest/k/pc-live/live/getByGame',
|
|
82
|
+
'/live_api/home/more': '/rest/k/pc-live/labels/switch',
|
|
83
|
+
'/live_api/playback/detail': '/rest/k/playback/product/play',
|
|
84
|
+
'/live_api/liveroom/like': '/rest/k/live/like',
|
|
85
|
+
'/live_api/liveroom/status': '/rest/wd/live/liveStream/status',
|
|
86
|
+
'/live_api/non-gameboard/list': '/rest/k/pc-live/live/synthesize',
|
|
87
|
+
'/live_api/playback/download': '/rest/k/material/download',
|
|
88
|
+
'/live_api/profile/interestlist': '/rest/k/pc-live/author/profile/reco',
|
|
89
|
+
};
|
|
90
|
+
/** 登录站(id.kuaishou.com)的 sig4 名单,form body 参与签名。 */
|
|
91
|
+
const LOGIN_SIG4 = new Set([
|
|
92
|
+
'/rest/c/infra/ks/qr/start',
|
|
93
|
+
'/rest/c/infra/ks/new/qr/start',
|
|
94
|
+
'/rest/c/infra/ks/qr/scanResult',
|
|
95
|
+
'/rest/c/infra/ks/new/qr/scanResult',
|
|
96
|
+
'/rest/c/infra/ks/qr/acceptResult',
|
|
97
|
+
'/rest/c/infra/ks/new/qr/acceptResult',
|
|
98
|
+
'/pass/bid/web/sns/login/code',
|
|
99
|
+
'/pass/bid/web/sns/quickLoginByKsAuth',
|
|
100
|
+
'/pass/kuaishou/sms/requestMobileCode',
|
|
101
|
+
'/pass/kuaishou/login/mobileCode',
|
|
102
|
+
'/pass/kuaishou/login/qr/callback',
|
|
103
|
+
]);
|
|
104
|
+
/** cp 侧改走 sig4 的接口(子串匹配)。 */
|
|
105
|
+
const CP_SIG4 = [
|
|
106
|
+
'rest/cp/works/v2/common/pc/nearby',
|
|
107
|
+
'rest/cp/works/v2/video/pc/edit/info',
|
|
108
|
+
'rest/cp/works/v2/common/pc/ip2poi',
|
|
109
|
+
'/rest/zt/location/wi/poi/search',
|
|
110
|
+
'/rest/cp/works/v2/video/pc/submit',
|
|
111
|
+
];
|
|
112
|
+
export const CP_PROJECT_INFO = { appKey: 'mMovf2dVDF', debug: false, sampling: 1 };
|
|
113
|
+
export const needSign = (path) => SIG4_WHITELIST.has(path);
|
|
114
|
+
export const liveNeedSign = (url) => url.split('?')[0] in LIVE_URL_MAP;
|
|
115
|
+
export const liveSignUrl = (url) => LIVE_URL_MAP[url.split('?')[0]] ?? url;
|
|
116
|
+
export const loginNeedSign = (url) => LOGIN_SIG4.has(url.split('?')[0]);
|
|
117
|
+
export const cpNeedSign = (url) => CP_SIG4.some((m) => url.split('?')[0].includes(m));
|
|
118
|
+
const SIG3_MARKERS = ['/rest/cp', 'rest/v2/creator', '/rest/kd'];
|
|
119
|
+
export const needSig3 = (url) => SIG3_MARKERS.some((m) => url.includes(m)) && !cpNeedSign(url);
|
|
120
|
+
/**
|
|
121
|
+
* 送入引擎的 signInput(build_sign_input)。www / cp 没有 body 时省略 form / requestBody 两个键;
|
|
122
|
+
* 直播与登录站恒带空对象。
|
|
123
|
+
*/
|
|
124
|
+
export function buildSignInput(path, query, body, contentType = 'application/json', options = {}) {
|
|
125
|
+
const ct = contentType.toLowerCase();
|
|
126
|
+
const isForm = ct.includes('application/x-www-form-urlencoded');
|
|
127
|
+
const isJson = ct.includes('application/json');
|
|
128
|
+
let bodyObj = body;
|
|
129
|
+
if (typeof body === 'string' && body) {
|
|
130
|
+
try {
|
|
131
|
+
bodyObj = JSON.parse(body);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
bodyObj = body;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const truthy = (v) => v != null && v !== '' && !(typeof v === 'object' && Object.keys(v).length === 0);
|
|
138
|
+
const input = { url: path, query: { caver: CAVER, ...query } };
|
|
139
|
+
const formValue = isForm && truthy(bodyObj) ? bodyObj : {};
|
|
140
|
+
const bodyValue = isJson && truthy(bodyObj) ? bodyObj : {};
|
|
141
|
+
if (!(options.omitEmptyBody && !truthy(formValue)))
|
|
142
|
+
input.form = formValue;
|
|
143
|
+
if (!(options.omitEmptyBody && !truthy(bodyValue)))
|
|
144
|
+
input.requestBody = bodyValue;
|
|
145
|
+
if (options.projectInfo)
|
|
146
|
+
input.projectInfo = { ...options.projectInfo };
|
|
147
|
+
return input;
|
|
148
|
+
}
|
|
149
|
+
/** jmpOnw_ms:路径 + 排序后的参数 + JSON body。键名含 `__NS` 的参数跳过。 */
|
|
150
|
+
export function serializeSignInput(data) {
|
|
151
|
+
let path = data.url ?? '';
|
|
152
|
+
if (/^http(s)?:\/\/([\w-]+\.)+[\w-]+/.test(path)) {
|
|
153
|
+
const rest = path.split('//')[1];
|
|
154
|
+
path = rest.includes('/') ? rest.slice(rest.indexOf('/')) : rest;
|
|
155
|
+
}
|
|
156
|
+
path = path.split('?')[0];
|
|
157
|
+
const merged = new Map();
|
|
158
|
+
for (const [k, v] of Object.entries((data.query ?? {})))
|
|
159
|
+
merged.set(String(k), v);
|
|
160
|
+
for (const [k, v] of Object.entries((data.form ?? {})))
|
|
161
|
+
merged.set(String(k), v);
|
|
162
|
+
const items = [];
|
|
163
|
+
for (const [k, v] of merged) {
|
|
164
|
+
if (k.includes('__NS'))
|
|
165
|
+
continue;
|
|
166
|
+
items.push(v == null ? `${k}=` : `${k}=${jsToString(v)}`);
|
|
167
|
+
}
|
|
168
|
+
let out = path + items.sort().join('');
|
|
169
|
+
if (data.requestBody !== undefined && data.requestBody !== null)
|
|
170
|
+
out += jsJson(data.requestBody);
|
|
171
|
+
return out;
|
|
172
|
+
}
|
|
173
|
+
const SIGMA = [
|
|
174
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
|
175
|
+
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
|
|
176
|
+
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
|
|
177
|
+
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
|
|
178
|
+
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
|
|
179
|
+
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
|
|
180
|
+
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
|
|
181
|
+
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
|
|
182
|
+
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
|
|
183
|
+
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
|
|
184
|
+
];
|
|
185
|
+
const BLAKE_IV = [2837534710, 2845986804, 2436420605, 706843635, 719254516, 2557931286, 2596197199, 2432949778];
|
|
186
|
+
const BLAKE_PARAM = 16842784;
|
|
187
|
+
const EO = Buffer.from([
|
|
188
|
+
98, 0, 0, 128, 49, 117, 185, 253, 224, 172, 104, 36, 223, 155, 87, 19, 32, 0, 0, 64, 2, 0, 0, 16, 255, 255, 255, 127, 255, 255, 255, 63, 0, 0, 0,
|
|
189
|
+
240, 0, 0, 0, 192, 0, 0, 0, 128, 255, 255, 255, 15,
|
|
190
|
+
]);
|
|
191
|
+
const CTS_KEY = 'Vuz4fCHxn1CO';
|
|
192
|
+
const CHACHA_CONST = [394484062, 2378328696, 630790222, 1922531795];
|
|
193
|
+
const CHACHA_KEY = [4183807412, 394484062, 1106561997, 2378328696, 630790222, 2546784104, 2891127470, 1922531795];
|
|
194
|
+
const CHACHA_NONCE = [2215853858, 1643070585, 1849059804];
|
|
195
|
+
const BLOB_XOR = 35;
|
|
196
|
+
const GEH = 'e0000000000000';
|
|
197
|
+
const XOR_DIGEST = [45, 211, 69, 192];
|
|
198
|
+
const XOR_ENV = [123, 86, 62, 218];
|
|
199
|
+
const COUNT_MASK = 3131873467n;
|
|
200
|
+
const NOW_MASK = 3360347992n;
|
|
201
|
+
const RAND_SPAN = 281474976710655;
|
|
202
|
+
export const SDK_VERSION_DEFAULT = 43468;
|
|
203
|
+
export const SDK_VERSION_LIVE = 43469;
|
|
204
|
+
const DEFAULT_SCRIPTS_LEN = 24;
|
|
205
|
+
const DEFAULT_SECS_STACK = 'spatchRequest (https://p23-plat.wskwai.com/kos/nlav111422/ks-web/assets/index-bZyTA7JL.js:32:235479)';
|
|
206
|
+
const rotr = (x, n) => ((x >>> n) | (x << (32 - n))) >>> 0;
|
|
207
|
+
const rotl = (x, n) => ((x << n) | (x >>> (32 - n))) >>> 0;
|
|
208
|
+
function blakeG(v, a, b, c, d, x, y) {
|
|
209
|
+
v[a] = (v[a] + v[b] + x) >>> 0;
|
|
210
|
+
v[d] = rotr((v[d] ^ v[a]) >>> 0, 16);
|
|
211
|
+
v[c] = (v[c] + v[d]) >>> 0;
|
|
212
|
+
v[b] = rotr((v[b] ^ v[c]) >>> 0, 12);
|
|
213
|
+
v[a] = (v[a] + v[b] + y) >>> 0;
|
|
214
|
+
v[d] = rotr((v[d] ^ v[a]) >>> 0, 8);
|
|
215
|
+
v[c] = (v[c] + v[d]) >>> 0;
|
|
216
|
+
v[b] = rotr((v[b] ^ v[c]) >>> 0, 7);
|
|
217
|
+
}
|
|
218
|
+
function blakeCompress(h, words, off, counter, length, final) {
|
|
219
|
+
const v = [...h, ...BLAKE_IV];
|
|
220
|
+
v[12] = (v[12] ^ counter) >>> 0;
|
|
221
|
+
if (final)
|
|
222
|
+
v[14] = (v[14] ^ 0xffffffff) >>> 0;
|
|
223
|
+
const m = new Array(16).fill(0);
|
|
224
|
+
for (let i = 0; i < length; i++)
|
|
225
|
+
m[i % 16] = (m[i % 16] ^ words[off + i]) >>> 0;
|
|
226
|
+
for (const s of SIGMA) {
|
|
227
|
+
blakeG(v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
|
|
228
|
+
blakeG(v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
|
|
229
|
+
blakeG(v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
|
|
230
|
+
blakeG(v, 3, 7, 11, 15, m[s[6]], m[s[7]]);
|
|
231
|
+
blakeG(v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
|
|
232
|
+
blakeG(v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
|
|
233
|
+
blakeG(v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
|
|
234
|
+
blakeG(v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
|
|
235
|
+
}
|
|
236
|
+
for (let i = 0; i < 8; i++)
|
|
237
|
+
h[i] = (h[i] ^ v[i] ^ v[i + 8]) >>> 0;
|
|
238
|
+
}
|
|
239
|
+
/** jmpOnw_b2has:BLAKE2s 变体(自定义 IV,按小端字分块),64 位 hex。 */
|
|
240
|
+
export function blakeHex(text) {
|
|
241
|
+
const raw = Buffer.from(text, 'utf8');
|
|
242
|
+
const padded = Buffer.concat([raw, Buffer.alloc((4 - (raw.length % 4)) % 4)]);
|
|
243
|
+
const words = [];
|
|
244
|
+
for (let i = 0; i < padded.length; i += 4)
|
|
245
|
+
words.push(padded.readUInt32LE(i));
|
|
246
|
+
const h = [...BLAKE_IV];
|
|
247
|
+
h[0] = (h[0] ^ BLAKE_PARAM) >>> 0;
|
|
248
|
+
let remain = words.length;
|
|
249
|
+
let off = 0;
|
|
250
|
+
let counter = 0;
|
|
251
|
+
while (remain > 64) {
|
|
252
|
+
counter += 64;
|
|
253
|
+
remain -= 64;
|
|
254
|
+
blakeCompress(h, words, off, counter, 64, false);
|
|
255
|
+
off += 64;
|
|
256
|
+
}
|
|
257
|
+
counter += remain;
|
|
258
|
+
blakeCompress(h, words, off, counter, remain, true);
|
|
259
|
+
return h.map((w) => w.toString(16).padStart(8, '0')).join('');
|
|
260
|
+
}
|
|
261
|
+
/** jmpOnw_cts:三路 LFSR 组合生成器,逐字节 `out = b ^ (keystream + 3)`。 */
|
|
262
|
+
class Cts {
|
|
263
|
+
r0;
|
|
264
|
+
r1;
|
|
265
|
+
r2;
|
|
266
|
+
p0 = EO.readUInt32LE(0);
|
|
267
|
+
f1 = EO.readUInt32LE(16);
|
|
268
|
+
f2 = EO.readUInt32LE(20);
|
|
269
|
+
m0 = EO.readUInt32LE(24);
|
|
270
|
+
m1 = EO.readUInt32LE(28);
|
|
271
|
+
m2 = EO.readUInt32LE(44);
|
|
272
|
+
h0 = EO.readUInt32LE(40);
|
|
273
|
+
h1 = EO.readUInt32LE(36);
|
|
274
|
+
h2 = EO.readUInt32LE(32);
|
|
275
|
+
constructor(key = CTS_KEY) {
|
|
276
|
+
let r0 = EO.readUInt32LE(12);
|
|
277
|
+
let r1 = EO.readUInt32LE(8);
|
|
278
|
+
let r2 = EO.readUInt32LE(4);
|
|
279
|
+
for (let i = 0; i < 4; i++) {
|
|
280
|
+
const byte = key.charCodeAt(i + 4) & 0xff;
|
|
281
|
+
r0 = ((r0 << 8) | byte) >>> 0;
|
|
282
|
+
r1 = ((r1 << 8) | byte) >>> 0;
|
|
283
|
+
r2 = ((r2 << 8) | byte) >>> 0;
|
|
284
|
+
}
|
|
285
|
+
this.r0 = r0 || 324508639;
|
|
286
|
+
this.r1 = r1 || 610839776;
|
|
287
|
+
this.r2 = r2 || 4256789809;
|
|
288
|
+
}
|
|
289
|
+
/** 返回低 8 位(上游的有符号修正不影响低 8 位,最终只用 `& 0xFF`)。 */
|
|
290
|
+
next() {
|
|
291
|
+
let out = 0;
|
|
292
|
+
let b0 = this.r1 & 1;
|
|
293
|
+
let b1 = this.r2 & 1;
|
|
294
|
+
for (let i = 0; i < 8; i++) {
|
|
295
|
+
if (this.r0 & 1) {
|
|
296
|
+
this.r0 = ((this.r0 ^ (this.p0 >>> 1)) | this.h0) >>> 0;
|
|
297
|
+
if (this.r1 & 1) {
|
|
298
|
+
this.r1 = ((this.r1 ^ (this.f1 >>> 1)) | this.h1) >>> 0;
|
|
299
|
+
b0 = 1;
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
this.r1 = ((this.r1 >>> 1) & this.m1) >>> 0;
|
|
303
|
+
b0 = 0;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
this.r0 = ((this.r0 >>> 1) & this.m0) >>> 0;
|
|
308
|
+
if (this.r2 & 1) {
|
|
309
|
+
this.r2 = ((this.r2 ^ (this.f2 >>> 1)) | this.h2) >>> 0;
|
|
310
|
+
b1 = 1;
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
this.r2 = ((this.r2 >>> 1) & this.m2) >>> 0;
|
|
314
|
+
b1 = 0;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
out = ((out << 1) | (b0 ^ b1)) & 0xff;
|
|
318
|
+
}
|
|
319
|
+
return out;
|
|
320
|
+
}
|
|
321
|
+
apply(data) {
|
|
322
|
+
return Uint8Array.from(data, (b) => (b ^ ((this.next() + 3) & 0xff)) & 0xff);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function chachaBlock(state) {
|
|
326
|
+
const w = [...state];
|
|
327
|
+
const q = (a, b, c, d) => {
|
|
328
|
+
w[a] = (w[a] + w[b]) >>> 0;
|
|
329
|
+
w[d] = rotl((w[d] ^ w[a]) >>> 0, 16);
|
|
330
|
+
w[c] = (w[c] + w[d]) >>> 0;
|
|
331
|
+
w[b] = rotl((w[b] ^ w[c]) >>> 0, 12);
|
|
332
|
+
w[a] = (w[a] + w[b]) >>> 0;
|
|
333
|
+
w[d] = rotl((w[d] ^ w[a]) >>> 0, 8);
|
|
334
|
+
w[c] = (w[c] + w[d]) >>> 0;
|
|
335
|
+
w[b] = rotl((w[b] ^ w[c]) >>> 0, 7);
|
|
336
|
+
};
|
|
337
|
+
for (let i = 0; i < 10; i++) {
|
|
338
|
+
q(0, 4, 8, 12);
|
|
339
|
+
q(1, 5, 9, 13);
|
|
340
|
+
q(2, 6, 10, 14);
|
|
341
|
+
q(3, 7, 11, 15);
|
|
342
|
+
q(0, 5, 10, 15);
|
|
343
|
+
q(1, 6, 11, 12);
|
|
344
|
+
q(2, 7, 8, 13);
|
|
345
|
+
q(3, 4, 9, 14);
|
|
346
|
+
}
|
|
347
|
+
return w.map((x, i) => (x + state[i]) >>> 0);
|
|
348
|
+
}
|
|
349
|
+
/** collectDeviceInfo 用的 ChaCha20(常量、密钥、nonce 都是定死的)。 */
|
|
350
|
+
function chacha(data) {
|
|
351
|
+
const state = [...CHACHA_CONST, ...CHACHA_KEY, 1, ...CHACHA_NONCE];
|
|
352
|
+
let block = chachaBlock(state);
|
|
353
|
+
const out = new Uint8Array(data.length);
|
|
354
|
+
let k = 0;
|
|
355
|
+
data.forEach((byte, i) => {
|
|
356
|
+
if (k === 64) {
|
|
357
|
+
state[12] = (state[12] + 1) >>> 0;
|
|
358
|
+
block = chachaBlock(state);
|
|
359
|
+
k = 0;
|
|
360
|
+
}
|
|
361
|
+
out[i] = (byte ^ ((block[k >> 2] >>> ((k & 3) << 3)) & 0xff)) & 0xff;
|
|
362
|
+
k++;
|
|
363
|
+
});
|
|
364
|
+
return out;
|
|
365
|
+
}
|
|
366
|
+
/** po() / _a():小端 n 字节;n >= 4 且值 >= 2^32 时固定为 4 个 0xFF。 */
|
|
367
|
+
function le(value, n) {
|
|
368
|
+
const v = BigInt(value || 0);
|
|
369
|
+
if (n >= 4 && v >= 1n << 32n)
|
|
370
|
+
return [255, 255, 255, 255];
|
|
371
|
+
return Array.from({ length: n }, (_, i) => Number((v >> BigInt(8 * i)) & 0xffn));
|
|
372
|
+
}
|
|
373
|
+
/** jmpOnw_i2h:小端 n 字节的 hex。 */
|
|
374
|
+
function leHex(value, n) {
|
|
375
|
+
const v = BigInt(value);
|
|
376
|
+
let out = '';
|
|
377
|
+
for (let i = 0; i < n; i++)
|
|
378
|
+
out += Number((v >> BigInt(8 * i)) & 0xffn).toString(16).padStart(2, '0');
|
|
379
|
+
return out;
|
|
380
|
+
}
|
|
381
|
+
/** collectDeviceInfo():TLV → 异或 0x23 → ChaCha20 → base64url(`+/=` 换成 `-_.`),前缀 `HUDR_`。 */
|
|
382
|
+
function devicePrefix(scriptsLen, guardCount, secsStack, secsCount) {
|
|
383
|
+
const blob = [45, 61, 0, 2, 68, 0, ...le(scriptsLen, 4), 112, 0, ...le(guardCount, 4), 114, 1, ...le(secsStack.length, 2)];
|
|
384
|
+
for (const ch of secsStack)
|
|
385
|
+
blob.push(ch.codePointAt(0));
|
|
386
|
+
blob.push(115, 0, ...le(secsCount, 4));
|
|
387
|
+
const enc = chacha(blob.map((b) => (b ^ BLOB_XOR) & 0xff));
|
|
388
|
+
return 'HUDR_' + Buffer.from(enc).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '.');
|
|
389
|
+
}
|
|
390
|
+
function hexChecksum(hex) {
|
|
391
|
+
const total = [...Buffer.from(hex, 'hex')].reduce((s, b) => s + b, 0);
|
|
392
|
+
const v = total > 255 ? -total & 0xff : total & 0xff;
|
|
393
|
+
return v.toString(16).padStart(2, '0');
|
|
394
|
+
}
|
|
395
|
+
function container(hex, checksum) {
|
|
396
|
+
const raw = Buffer.from(hex + checksum, 'hex');
|
|
397
|
+
const key = raw[raw.length - 1];
|
|
398
|
+
const out = Buffer.from(raw);
|
|
399
|
+
for (let i = 0; i < out.length - 1; i++)
|
|
400
|
+
out[i] = raw[i] ^ key;
|
|
401
|
+
return out.toString('hex');
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* `__NS_hxfalcon` 签名器。startupRandom 是引擎构造时刻的 unix 毫秒,count 与 KsGuard.count 从 100 起逐次自增。
|
|
405
|
+
*/
|
|
406
|
+
export class HxFalconSigner {
|
|
407
|
+
sdkVersion;
|
|
408
|
+
count = 100;
|
|
409
|
+
guardCount = 100;
|
|
410
|
+
startupRandom;
|
|
411
|
+
constructor(sdkVersion = SDK_VERSION_DEFAULT) {
|
|
412
|
+
this.sdkVersion = sdkVersion;
|
|
413
|
+
this.startupRandom = rand.now();
|
|
414
|
+
}
|
|
415
|
+
sign(input) {
|
|
416
|
+
return this.encode(input, rand.now(), Math.floor(rand.random() * RAND_SPAN));
|
|
417
|
+
}
|
|
418
|
+
encode(input, nowMs, rand48) {
|
|
419
|
+
const prefix = devicePrefix(DEFAULT_SCRIPTS_LEN, this.guardCount, DEFAULT_SECS_STACK, this.count);
|
|
420
|
+
this.guardCount++;
|
|
421
|
+
const serialized = serializeSignInput(input);
|
|
422
|
+
const stream = new Cts().apply(Buffer.from(blakeHex(serialized + prefix).slice(0, 4), 'utf8'));
|
|
423
|
+
const digest = Buffer.from([0, 1, 2, 3].map((i) => (stream[i] ^ XOR_DIGEST[i]) & 0xff)).toString('hex');
|
|
424
|
+
const envRaw = Buffer.from(GEH, 'hex');
|
|
425
|
+
const env = Buffer.from([...envRaw].map((b, i) => (b ^ XOR_ENV[i % 4]) & 0xff)).toString('hex');
|
|
426
|
+
const body = '4b54' +
|
|
427
|
+
leHex(this.sdkVersion, 2) +
|
|
428
|
+
'ab' +
|
|
429
|
+
leHex(this.startupRandom, 6) +
|
|
430
|
+
leHex(rand48, 6) +
|
|
431
|
+
'0100000001' +
|
|
432
|
+
leHex(BigInt(this.count) ^ COUNT_MASK, 4) +
|
|
433
|
+
digest +
|
|
434
|
+
leHex(BigInt(nowMs) ^ NOW_MASK, 6) +
|
|
435
|
+
env +
|
|
436
|
+
hexChecksum(env);
|
|
437
|
+
this.count++;
|
|
438
|
+
return prefix + '$HE_' + container(body, hexChecksum(body));
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
// ================================================================ sig3(sig3_pure.py)
|
|
442
|
+
const SHA256_K = [
|
|
443
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
|
444
|
+
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
445
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
|
446
|
+
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
447
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
|
448
|
+
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
|
449
|
+
];
|
|
450
|
+
const SIG3_IV = [
|
|
451
|
+
[1201087869, 728038316, -1247317401, -375217708, -1820116536, 395408228, 1482956210, -1904517706],
|
|
452
|
+
[-932960537, -839864669, 895983619, 323220038, 1908748190, 778712444, -2022813415, -1089440689],
|
|
453
|
+
].map((row) => row.map((v) => v >>> 0));
|
|
454
|
+
/** 引擎的 d():追加 0x80,切 16 字大端块;长度字段是 (len + 63) * 8。 */
|
|
455
|
+
function sha256Blocks(msg) {
|
|
456
|
+
const m = [...msg, 0x80];
|
|
457
|
+
const total = Math.ceil((m.length / 4 + 2) / 16);
|
|
458
|
+
const out = [];
|
|
459
|
+
for (let i = 0; i < total; i++) {
|
|
460
|
+
const block = [];
|
|
461
|
+
for (let j = 0; j < 16; j++) {
|
|
462
|
+
let word = 0;
|
|
463
|
+
for (let b = 0; b < 4; b++) {
|
|
464
|
+
const idx = i * 64 + j * 4 + b;
|
|
465
|
+
word = (word | ((idx < m.length ? m[idx] : 0) << (24 - 8 * b))) >>> 0;
|
|
466
|
+
}
|
|
467
|
+
block.push(word);
|
|
468
|
+
}
|
|
469
|
+
out.push(block);
|
|
470
|
+
}
|
|
471
|
+
const bits = BigInt((m.length + 63) * 8);
|
|
472
|
+
out[out.length - 1][14] = Number((bits >> 32n) & 0xffffffffn);
|
|
473
|
+
out[out.length - 1][15] = Number(bits & 0xffffffffn);
|
|
474
|
+
return out;
|
|
475
|
+
}
|
|
476
|
+
function sha256Compress(blocks, iv) {
|
|
477
|
+
let h = [...iv];
|
|
478
|
+
for (const block of blocks) {
|
|
479
|
+
const w = [...block];
|
|
480
|
+
for (let t = 16; t < 64; t++) {
|
|
481
|
+
const s0 = rotr(w[t - 15], 7) ^ rotr(w[t - 15], 18) ^ (w[t - 15] >>> 3);
|
|
482
|
+
const s1 = rotr(w[t - 2], 17) ^ rotr(w[t - 2], 19) ^ (w[t - 2] >>> 10);
|
|
483
|
+
w.push((w[t - 16] + s0 + w[t - 7] + s1) >>> 0);
|
|
484
|
+
}
|
|
485
|
+
let [a, b, c, d, e, f, g, hh] = h;
|
|
486
|
+
for (let t = 0; t < 64; t++) {
|
|
487
|
+
const t1 = (hh + (rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)) + ((e & f) ^ (~e & g)) + SHA256_K[t] + w[t]) >>> 0;
|
|
488
|
+
const t2 = ((rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) + ((a & b) ^ (a & c) ^ (b & c))) >>> 0;
|
|
489
|
+
hh = g;
|
|
490
|
+
g = f;
|
|
491
|
+
f = e;
|
|
492
|
+
e = (d + t1) >>> 0;
|
|
493
|
+
d = c;
|
|
494
|
+
c = b;
|
|
495
|
+
b = a;
|
|
496
|
+
a = (t1 + t2) >>> 0;
|
|
497
|
+
}
|
|
498
|
+
h = h.map((x, i) => (x + [a, b, c, d, e, f, g, hh][i]) >>> 0);
|
|
499
|
+
}
|
|
500
|
+
return h;
|
|
501
|
+
}
|
|
502
|
+
/** 4 字节摘要:两轮自定义 IV 的 SHA-256,第二轮直接吃第一轮的大端字节。 */
|
|
503
|
+
export function sig3Digest(md5hex) {
|
|
504
|
+
const first = sha256Compress(sha256Blocks([...Buffer.from(md5hex, 'latin1')]), SIG3_IV[0]);
|
|
505
|
+
const mid = [];
|
|
506
|
+
for (const w of first)
|
|
507
|
+
for (let b = 0; b < 4; b++)
|
|
508
|
+
mid.push((w >>> (24 - 8 * b)) & 0xff);
|
|
509
|
+
const second = sha256Compress(sha256Blocks(mid), SIG3_IV[1]);
|
|
510
|
+
const out = Buffer.alloc(4);
|
|
511
|
+
out.writeUInt32BE(second[0]);
|
|
512
|
+
return out;
|
|
513
|
+
}
|
|
514
|
+
/** request2SortedString:键按 JS 默认序,拼 `k=encodeURI(v)`。 */
|
|
515
|
+
function sortedString(obj) {
|
|
516
|
+
return Object.keys(obj)
|
|
517
|
+
.sort()
|
|
518
|
+
.map((k) => `${k}=${encodeURI(jsToString(obj[k]))}`)
|
|
519
|
+
.join('');
|
|
520
|
+
}
|
|
521
|
+
/** request2Md5:form-data 时 body 并入 query 一起排序,json 时拼 JSON.stringify(body)。 */
|
|
522
|
+
export function request2Md5(query, body, type = 'json') {
|
|
523
|
+
const merged = { ...query };
|
|
524
|
+
let suffix = '';
|
|
525
|
+
if (type === 'form-data')
|
|
526
|
+
Object.assign(merged, body ?? {});
|
|
527
|
+
else
|
|
528
|
+
suffix = jsJson(body ?? {});
|
|
529
|
+
return createHash('md5')
|
|
530
|
+
.update(sortedString(merged) + suffix, 'utf8')
|
|
531
|
+
.digest('hex');
|
|
532
|
+
}
|
|
533
|
+
const SIG3_BUILD = 1653548225;
|
|
534
|
+
/** `__NS_sig3` 签名器:startupRandom 是 unix 秒,count 从 100 起。 */
|
|
535
|
+
export class Sig3Signer {
|
|
536
|
+
count = 100;
|
|
537
|
+
startupRandom = rand.nowSeconds();
|
|
538
|
+
sign(query, body, type = 'json') {
|
|
539
|
+
const head = Buffer.alloc(27);
|
|
540
|
+
head.write('TE', 0, 'latin1');
|
|
541
|
+
head[2] = 0x01;
|
|
542
|
+
head[3] = 0x30;
|
|
543
|
+
head.writeUInt32LE(this.startupRandom >>> 0, 4);
|
|
544
|
+
head.writeUInt32LE(this.count >>> 0, 8);
|
|
545
|
+
sig3Digest(request2Md5(query, body, type)).copy(head, 12);
|
|
546
|
+
head.writeUInt32LE(SIG3_BUILD, 16);
|
|
547
|
+
Buffer.from('01000100000000', 'hex').copy(head, 20);
|
|
548
|
+
this.count++;
|
|
549
|
+
const total = [...head].reduce((s, b) => s + b, 0);
|
|
550
|
+
const key = total > 255 ? -total & 0xff : total & 0xff;
|
|
551
|
+
const out = Buffer.alloc(28);
|
|
552
|
+
for (let i = 0; i < 27; i++)
|
|
553
|
+
out[i] = head[i] ^ key ^ i;
|
|
554
|
+
out[27] = key;
|
|
555
|
+
return out.toString('hex');
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
// ================================================================ webweapon AES
|
|
559
|
+
const CONFIG_KEY = Buffer.from('webweaponconfigs');
|
|
560
|
+
/** AES-128-CBC + PKCS7,IV 等于 key,输出 base64(st$1 / gdfp 的 at())。 */
|
|
561
|
+
export function weaponEncrypt(plain, key = CONFIG_KEY) {
|
|
562
|
+
const c = createCipheriv('aes-128-cbc', key, key);
|
|
563
|
+
return Buffer.concat([c.update(plain, 'utf8'), c.final()]).toString('base64');
|
|
564
|
+
}
|
|
565
|
+
export function weaponDecrypt(blob, key = CONFIG_KEY) {
|
|
566
|
+
const d = createDecipheriv('aes-128-cbc', key, key);
|
|
567
|
+
return Buffer.concat([d.update(Buffer.from(blob, 'base64')), d.final()]).toString('utf8');
|
|
568
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { filter } from '../../core/options.js';
|
|
2
|
+
import { definePlatform, handlers } from '../../core/registry.js';
|
|
3
|
+
/** 懒加载 web 端的 handler:只有执行到这条命令时才加载实现。 */
|
|
4
|
+
const { h, impl } = handlers(() => import('./web/commands.js'));
|
|
5
|
+
export default definePlatform({
|
|
6
|
+
id: 'taobao',
|
|
7
|
+
name: '淘宝',
|
|
8
|
+
aliases: ['tb'],
|
|
9
|
+
item: '商品',
|
|
10
|
+
endpoints: {
|
|
11
|
+
web: {
|
|
12
|
+
login: { methods: ['cookie'], default: 'cookie' },
|
|
13
|
+
commands: {
|
|
14
|
+
'auth login': impl('full', 'authLogin'),
|
|
15
|
+
'auth status': impl('partial', 'authStatus', { note: '上游没有取当前用户的接口:换一次私信 token 校验,账号信息取自 cookie 里的 unb 和昵称' }),
|
|
16
|
+
'user get': impl('partial', 'userGet', { note: '参数只接受商品链接(淘宝 / 天猫商品页或 m.tb.cn 分享短链),返回这件商品的卖家;me 规划中' }),
|
|
17
|
+
'user items': 'none',
|
|
18
|
+
'user collects': 'none',
|
|
19
|
+
'item get': 'none',
|
|
20
|
+
'item search': 'none',
|
|
21
|
+
'item related': 'none',
|
|
22
|
+
'item collect': 'none',
|
|
23
|
+
'item uncollect': 'none',
|
|
24
|
+
'comment list': 'none',
|
|
25
|
+
'feed list': { upstream: 'none', options: { kind: filter.kind('recommend') } },
|
|
26
|
+
'live get': 'none',
|
|
27
|
+
'live list': 'none',
|
|
28
|
+
'live search': 'none',
|
|
29
|
+
'live categories': 'none',
|
|
30
|
+
'live listen': 'none',
|
|
31
|
+
'live history': 'none',
|
|
32
|
+
'live send': 'none',
|
|
33
|
+
'live like': 'none',
|
|
34
|
+
'live rank': 'none',
|
|
35
|
+
'live gifts': 'none',
|
|
36
|
+
'live products': 'none',
|
|
37
|
+
'live media': 'none',
|
|
38
|
+
'live replays': 'none',
|
|
39
|
+
'live start': 'none',
|
|
40
|
+
'live stop': 'none',
|
|
41
|
+
'keyword suggest': 'none',
|
|
42
|
+
'msg list': 'none',
|
|
43
|
+
'msg history': impl('full', 'msgHistory'),
|
|
44
|
+
'msg send': impl('full', 'msgSend', { note: '--item 联系商品卖家,--conversation 回复已有会话;不支持 --to(没有按用户发起会话的接口)' }),
|
|
45
|
+
'msg listen': impl('full', 'msgListen'),
|
|
46
|
+
'msg read': 'none',
|
|
47
|
+
'msg revoke': 'none',
|
|
48
|
+
'msg delete': 'none',
|
|
49
|
+
'media upload': impl('full', 'mediaUpload'),
|
|
50
|
+
'history list': 'none',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
app: 'planned',
|
|
54
|
+
pc: 'planned',
|
|
55
|
+
},
|
|
56
|
+
});
|