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.
Files changed (277) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +177 -0
  3. package/README.ja.md +177 -0
  4. package/README.ko.md +177 -0
  5. package/README.md +174 -2
  6. package/README.zh-TW.md +177 -0
  7. package/assets/banner.txt +3 -0
  8. package/dist/cli/argv.js +98 -0
  9. package/dist/cli/commands/auth.js +11 -0
  10. package/dist/cli/commands/config.js +23 -0
  11. package/dist/cli/commands/doctor.js +134 -0
  12. package/dist/cli/commands/platforms.js +24 -0
  13. package/dist/cli/commands/version.js +4 -0
  14. package/dist/cli/confirm.js +22 -0
  15. package/dist/cli/dispatch.js +440 -0
  16. package/dist/cli/help.js +223 -0
  17. package/dist/cli/hints.js +60 -0
  18. package/dist/cli/main.js +18 -0
  19. package/dist/cli/output.js +66 -0
  20. package/dist/core/auth-handlers.js +41 -0
  21. package/dist/core/auth-store.js +119 -0
  22. package/dist/core/capabilities.js +163 -0
  23. package/dist/core/config.js +121 -0
  24. package/dist/core/cookies.js +164 -0
  25. package/dist/core/errors.js +37 -0
  26. package/dist/core/files.js +89 -0
  27. package/dist/core/fsutil.js +52 -0
  28. package/dist/core/hash.js +4 -0
  29. package/dist/core/http.js +331 -0
  30. package/dist/core/image.js +280 -0
  31. package/dist/core/log.js +40 -0
  32. package/dist/core/login.js +191 -0
  33. package/dist/core/mp4.js +130 -0
  34. package/dist/core/normalize.js +207 -0
  35. package/dist/core/options.js +110 -0
  36. package/dist/core/paths.js +38 -0
  37. package/dist/core/pb.js +59 -0
  38. package/dist/core/py.js +181 -0
  39. package/dist/core/rand.js +131 -0
  40. package/dist/core/registry.js +129 -0
  41. package/dist/core/schemas.js +41 -0
  42. package/dist/core/stream.js +104 -0
  43. package/dist/core/toolkit.js +35 -0
  44. package/dist/core/vm.js +73 -0
  45. package/dist/core/vocab.js +157 -0
  46. package/dist/platforms/_shared/impaas.js +410 -0
  47. package/dist/platforms/bilibili/index.js +216 -0
  48. package/dist/platforms/bilibili/web/api.js +806 -0
  49. package/dist/platforms/bilibili/web/client.js +311 -0
  50. package/dist/platforms/bilibili/web/commands.js +783 -0
  51. package/dist/platforms/bilibili/web/ddddocr.js +342 -0
  52. package/dist/platforms/bilibili/web/gaia.js +130 -0
  53. package/dist/platforms/bilibili/web/geetest-manual.js +154 -0
  54. package/dist/platforms/bilibili/web/geetest-vision.js +232 -0
  55. package/dist/platforms/bilibili/web/geetest.js +450 -0
  56. package/dist/platforms/bilibili/web/normalize.js +234 -0
  57. package/dist/platforms/bilibili/web/profile.js +135 -0
  58. package/dist/platforms/bilibili/web/resolve.js +112 -0
  59. package/dist/platforms/bilibili/web/sign.js +223 -0
  60. package/dist/platforms/bilibili/web/upos.js +102 -0
  61. package/dist/platforms/douyin/index.js +142 -0
  62. package/dist/platforms/douyin/web/api.js +863 -0
  63. package/dist/platforms/douyin/web/client.js +413 -0
  64. package/dist/platforms/douyin/web/commands.js +712 -0
  65. package/dist/platforms/douyin/web/creator.js +601 -0
  66. package/dist/platforms/douyin/web/crypto.js +247 -0
  67. package/dist/platforms/douyin/web/dtrait.js +190 -0
  68. package/dist/platforms/douyin/web/im.js +502 -0
  69. package/dist/platforms/douyin/web/image.js +5 -0
  70. package/dist/platforms/douyin/web/live.js +225 -0
  71. package/dist/platforms/douyin/web/mssdk.js +148 -0
  72. package/dist/platforms/douyin/web/normalize.js +326 -0
  73. package/dist/platforms/douyin/web/passport.js +1247 -0
  74. package/dist/platforms/douyin/web/profile.js +221 -0
  75. package/dist/platforms/douyin/web/proto.js +26 -0
  76. package/dist/platforms/douyin/web/resolve.js +88 -0
  77. package/dist/platforms/douyin/web/sign.js +436 -0
  78. package/dist/platforms/douyin/web/tos.js +78 -0
  79. package/dist/platforms/index.js +17 -0
  80. package/dist/platforms/jd/index.js +67 -0
  81. package/dist/platforms/jd/web/aks.js +115 -0
  82. package/dist/platforms/jd/web/api.js +549 -0
  83. package/dist/platforms/jd/web/chat.js +132 -0
  84. package/dist/platforms/jd/web/client.js +470 -0
  85. package/dist/platforms/jd/web/commands.js +557 -0
  86. package/dist/platforms/jd/web/jcap/helper.js +90 -0
  87. package/dist/platforms/jd/web/jcap/host.js +107 -0
  88. package/dist/platforms/jd/web/jcap/lsd.js +300 -0
  89. package/dist/platforms/jd/web/jcap/solver.js +995 -0
  90. package/dist/platforms/jd/web/jcap.js +150 -0
  91. package/dist/platforms/jd/web/js.js +420 -0
  92. package/dist/platforms/jd/web/login.js +775 -0
  93. package/dist/platforms/jd/web/normalize.js +297 -0
  94. package/dist/platforms/jd/web/profile.js +256 -0
  95. package/dist/platforms/jd/web/util.js +175 -0
  96. package/dist/platforms/jd/web/webm.js +133 -0
  97. package/dist/platforms/kuaishou/index.js +98 -0
  98. package/dist/platforms/kuaishou/web/api.js +694 -0
  99. package/dist/platforms/kuaishou/web/captcha-crypto.js +134 -0
  100. package/dist/platforms/kuaishou/web/captcha-fp.js +180 -0
  101. package/dist/platforms/kuaishou/web/captcha.js +273 -0
  102. package/dist/platforms/kuaishou/web/client.js +487 -0
  103. package/dist/platforms/kuaishou/web/commands.js +863 -0
  104. package/dist/platforms/kuaishou/web/gap.js +144 -0
  105. package/dist/platforms/kuaishou/web/gdfp.js +376 -0
  106. package/dist/platforms/kuaishou/web/gql.js +11 -0
  107. package/dist/platforms/kuaishou/web/live.js +125 -0
  108. package/dist/platforms/kuaishou/web/normalize.js +241 -0
  109. package/dist/platforms/kuaishou/web/oracle.js +329 -0
  110. package/dist/platforms/kuaishou/web/profile.js +259 -0
  111. package/dist/platforms/kuaishou/web/resolve.js +84 -0
  112. package/dist/platforms/kuaishou/web/session.js +592 -0
  113. package/dist/platforms/kuaishou/web/sign.js +568 -0
  114. package/dist/platforms/taobao/index.js +56 -0
  115. package/dist/platforms/taobao/web/api.js +96 -0
  116. package/dist/platforms/taobao/web/client.js +102 -0
  117. package/dist/platforms/taobao/web/commands.js +172 -0
  118. package/dist/platforms/taobao/web/im.js +68 -0
  119. package/dist/platforms/taobao/web/normalize.js +103 -0
  120. package/dist/platforms/taobao/web/profile.js +85 -0
  121. package/dist/platforms/taobao/web/resolve.js +51 -0
  122. package/dist/platforms/taobao/web/sign.js +35 -0
  123. package/dist/platforms/tiktok/index.js +120 -0
  124. package/dist/platforms/tiktok/web/api.js +1234 -0
  125. package/dist/platforms/tiktok/web/client.js +362 -0
  126. package/dist/platforms/tiktok/web/commands.js +821 -0
  127. package/dist/platforms/tiktok/web/im.js +133 -0
  128. package/dist/platforms/tiktok/web/jsrun.js +126 -0
  129. package/dist/platforms/tiktok/web/normalize.js +286 -0
  130. package/dist/platforms/tiktok/web/profile.js +169 -0
  131. package/dist/platforms/tiktok/web/resolve.js +156 -0
  132. package/dist/platforms/tiktok/web/sign.js +585 -0
  133. package/dist/platforms/tiktok/web/upload.js +805 -0
  134. package/dist/platforms/tiktok/web/wire.js +490 -0
  135. package/dist/platforms/weibo/index.js +91 -0
  136. package/dist/platforms/weibo/web/api.js +206 -0
  137. package/dist/platforms/weibo/web/client.js +155 -0
  138. package/dist/platforms/weibo/web/commands.js +202 -0
  139. package/dist/platforms/weibo/web/normalize.js +137 -0
  140. package/dist/platforms/weibo/web/profile.js +246 -0
  141. package/dist/platforms/weibo/web/resolve.js +60 -0
  142. package/dist/platforms/weibo/web/sign.js +85 -0
  143. package/dist/platforms/x/index.js +107 -0
  144. package/dist/platforms/x/web/api.js +443 -0
  145. package/dist/platforms/x/web/article.js +212 -0
  146. package/dist/platforms/x/web/client.js +202 -0
  147. package/dist/platforms/x/web/commands.js +363 -0
  148. package/dist/platforms/x/web/graphql.js +108 -0
  149. package/dist/platforms/x/web/normalize.js +235 -0
  150. package/dist/platforms/x/web/profile.js +99 -0
  151. package/dist/platforms/x/web/resolve.js +44 -0
  152. package/dist/platforms/x/web/transaction.js +171 -0
  153. package/dist/platforms/xhs/index.js +101 -0
  154. package/dist/platforms/xhs/web/api.js +278 -0
  155. package/dist/platforms/xhs/web/client.js +540 -0
  156. package/dist/platforms/xhs/web/commands.js +1046 -0
  157. package/dist/platforms/xhs/web/creator-api.js +609 -0
  158. package/dist/platforms/xhs/web/creator.js +203 -0
  159. package/dist/platforms/xhs/web/js.js +194 -0
  160. package/dist/platforms/xhs/web/login.js +230 -0
  161. package/dist/platforms/xhs/web/normalize.js +166 -0
  162. package/dist/platforms/xhs/web/profile.js +127 -0
  163. package/dist/platforms/xhs/web/push.js +361 -0
  164. package/dist/platforms/xhs/web/resolve.js +69 -0
  165. package/dist/platforms/xhs/web/state.js +368 -0
  166. package/dist/platforms/xianyu/index.js +59 -0
  167. package/dist/platforms/xianyu/web/api.js +312 -0
  168. package/dist/platforms/xianyu/web/client.js +134 -0
  169. package/dist/platforms/xianyu/web/commands.js +305 -0
  170. package/dist/platforms/xianyu/web/im.js +73 -0
  171. package/dist/platforms/xianyu/web/normalize.js +123 -0
  172. package/dist/platforms/xianyu/web/profile.js +146 -0
  173. package/dist/platforms/xianyu/web/resolve.js +48 -0
  174. package/dist/platforms/xianyu/web/sign.js +79 -0
  175. package/package.json +77 -4
  176. package/static/douyin/Live.proto +194 -0
  177. package/static/douyin/Request.proto +89 -0
  178. package/static/douyin/Response.proto +46 -0
  179. package/static/douyin/acrawler_runtime/ac_vm.js +2 -0
  180. package/static/douyin/acrawler_runtime/browser_window_shape.json +1 -0
  181. package/static/douyin/acrawler_runtime/canvas_actual_exact.json +1 -0
  182. package/static/douyin/acrawler_runtime/run_ac_node.js +1005 -0
  183. package/static/douyin/acrawler_runtime/runner_window_keys_baseline.txt +240 -0
  184. package/static/douyin/challenge_profile.json +801 -0
  185. package/static/douyin/challenge_template_runner.js +217 -0
  186. package/static/douyin/dtrait_profile.json +66 -0
  187. package/static/douyin/mstoken_common_profile.json +1 -0
  188. package/static/douyin/mstoken_profile.json +214 -0
  189. package/static/jd/h5st5_env.js +750 -0
  190. package/static/jd/h5st5_lib.js +5 -0
  191. package/static/jd/h5st5_server.js +49 -0
  192. package/static/jd/jcap/env/env_core.js +512 -0
  193. package/static/jd/jcap/env/run.js +1307 -0
  194. package/static/jd/jcap/run/jcap_ujb96b.js +39 -0
  195. package/static/jd/package.json +4 -0
  196. package/static/jd/pc_tk_lib.js +1 -0
  197. package/static/jd/pc_tk_server.js +63 -0
  198. package/static/jd/summer_cryptico_runner.js +50 -0
  199. package/static/jd/webm/env/run.js +453 -0
  200. package/static/jd/webm/run/jdwebm-riskhandle.js +49 -0
  201. package/static/kuaishou/bundles/weapon/kwf-0.0.2.2cee19b4b7dec496.js +1 -0
  202. package/static/kuaishou/bundles/weapon/kws-0-0.0.1-obfuscated.4b7f3de1ab0ddcc2.js +1 -0
  203. package/static/kuaishou/bundles/weapon/kws-1-0.0.1-obfuscated.346ebdb1c78df316.js +1 -0
  204. package/static/kuaishou/bundles/weapon/kws-10-0.0.1-obfuscated.a33db41897b983af.js +1 -0
  205. package/static/kuaishou/bundles/weapon/kws-11-0.0.1-obfuscated.5e0a90af726d8a7e.js +1 -0
  206. package/static/kuaishou/bundles/weapon/kws-12-0.0.1-obfuscated.20ea29345706ec8c.js +1 -0
  207. package/static/kuaishou/bundles/weapon/kws-13-0.0.1-obfuscated.6b74e9640ff18648.js +1 -0
  208. package/static/kuaishou/bundles/weapon/kws-14-0.0.1-obfuscated.551e4429d156bf66.js +1 -0
  209. package/static/kuaishou/bundles/weapon/kws-15-0.0.1-obfuscated.8625b087aaba4a60.js +1 -0
  210. package/static/kuaishou/bundles/weapon/kws-16-0.0.1-obfuscated.d5ffa4020f3be99a.js +1 -0
  211. package/static/kuaishou/bundles/weapon/kws-17-0.0.1-obfuscated.6d13e19b22f50a80.js +1 -0
  212. package/static/kuaishou/bundles/weapon/kws-18-0.0.1-obfuscated.6d31405044166289.js +1 -0
  213. package/static/kuaishou/bundles/weapon/kws-19-0.0.1-obfuscated.a9a8d925942f19e4.js +1 -0
  214. package/static/kuaishou/bundles/weapon/kws-2-0.0.1-obfuscated.b849a136ae4a6b3e.js +1 -0
  215. package/static/kuaishou/bundles/weapon/kws-3-0.0.1-obfuscated.f9e77ebf6186e2ac.js +1 -0
  216. package/static/kuaishou/bundles/weapon/kws-4-0.0.1-obfuscated.2c3c155aa52ceb0e.js +1 -0
  217. package/static/kuaishou/bundles/weapon/kws-5-0.0.1-obfuscated.4e553d17691b3c5a.js +1 -0
  218. package/static/kuaishou/bundles/weapon/kws-6-0.0.1-obfuscated.f73ddd3ff13e1217.js +1 -0
  219. package/static/kuaishou/bundles/weapon/kws-7-0.0.1-obfuscated.1628b8729beaab7e.js +1 -0
  220. package/static/kuaishou/bundles/weapon/kws-8-0.0.1-obfuscated.6da6baef7aa81fff.js +1 -0
  221. package/static/kuaishou/bundles/weapon/kws-9-0.0.1-obfuscated.8645d55e5323b743.js +1 -0
  222. package/static/kuaishou/fixtures/current_follow_script_813.network-response +2 -0
  223. package/static/kuaishou/fixtures/live_ws_maps.json +61 -0
  224. package/static/kuaishou/fixtures/live_ws_proto.json +1342 -0
  225. package/static/kuaishou/js/cp-kwf.js +1 -0
  226. package/static/kuaishou/tools/kwf_oracle.js +799 -0
  227. package/static/kuaishou/tools/kws_oracle.js +885 -0
  228. package/static/kuaishou/tools/like_token_oracle.js +70 -0
  229. package/static/taobao/taobao_js_20260407.js +570 -0
  230. package/static/tiktok/Tiktok_Request.json +597 -0
  231. package/static/tiktok/package.json +3 -0
  232. package/static/tiktok/shop_bsid/core.js +1 -0
  233. package/static/tiktok/shop_bsid/env_core.js +512 -0
  234. package/static/tiktok/shop_bsid/loader.js +1 -0
  235. package/static/tiktok/shop_bsid/run.js +753 -0
  236. package/static/tiktok/shop_bsid/sign.js +100 -0
  237. package/static/tiktok/shop_bsid/webmssdk.js +1 -0
  238. package/static/tiktok/shop_bsid/webmssdk_ex.js +1 -0
  239. package/static/tiktok/signing/env/env_core.js +512 -0
  240. package/static/tiktok/signing/env/sign.js +455 -0
  241. package/static/tiktok/signing/sdk_config.json +230 -0
  242. package/static/tiktok/signing/source/webmssdk.js +1 -0
  243. package/static/tiktok/signing/source/webmssdk_ex.js +1 -0
  244. package/static/tiktok/signing/source/webmssdk_legacy.js +1 -0
  245. package/static/x/graphql.json +7777 -0
  246. package/static/x/transaction_l1.json +12 -0
  247. package/static/xhs/package.json +3 -0
  248. package/static/xhs/xhs_core/js/b1.js +210 -0
  249. package/static/xhs/xhs_core/js/mns.js +340 -0
  250. package/static/xhs/xhs_core/js/mns_0101_keystream.json +5 -0
  251. package/static/xhs/xhs_core/js/mns_keystreams.json +1 -0
  252. package/static/xhs/xhs_core/js/sign.js +225 -0
  253. package/static/xhs/xhs_core/js/websectiga_cli.js +220 -0
  254. package/static/xhs/xhs_core/js/websectiga_env.js +512 -0
  255. package/static/xhs/xhs_core/js/xs_common.js +79 -0
  256. package/static/xhs/xhs_creator/js/profile.js +115 -0
  257. package/static/xhs/xhs_creator/js/rap_fingerprint_creator.json +6 -0
  258. package/static/xhs/xhs_creator/js/reference_profile.json +282 -0
  259. package/static/xhs/xhs_creator/js/xhs_creator_sign.js +29 -0
  260. package/static/xhs/xhs_creator/js/xhs_creator_signature.js +23 -0
  261. package/static/xhs/xhs_pc/js/aes_decrypt.js +22 -0
  262. package/static/xhs/xhs_pc/js/aes_encrypt.js +30 -0
  263. package/static/xhs/xhs_pc/js/b1.js +9 -0
  264. package/static/xhs/xhs_pc/js/deflate.js +320 -0
  265. package/static/xhs/xhs_pc/js/mns.js +4 -0
  266. package/static/xhs/xhs_pc/js/profile.js +141 -0
  267. package/static/xhs/xhs_pc/js/rap.js +173 -0
  268. package/static/xhs/xhs_pc/js/rap_cli.js +30 -0
  269. package/static/xhs/xhs_pc/js/rap_crypto.js +73 -0
  270. package/static/xhs/xhs_pc/js/rap_fingerprint_template.json +8 -0
  271. package/static/xhs/xhs_pc/js/reference_profile.json +262 -0
  272. package/static/xhs/xhs_pc/js/sign.js +6 -0
  273. package/static/xhs/xhs_pc/js/web_ssk.js +82 -0
  274. package/static/xhs/xhs_pc/js/xs_common.js +4 -0
  275. package/static/xianyu/et_f.js +2 -0
  276. package/static/xianyu/gen_tfstk.js +410 -0
  277. 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
+ });