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,223 @@
1
+ import { constants, publicEncrypt } from 'node:crypto';
2
+ import { hmacSha256Hex, md5Hex } from '../../../core/hash.js';
3
+ import { jsonDumps, pyStr, urlencode } from '../../../core/py.js';
4
+ import * as rand from '../../../core/rand.js';
5
+ import { PROFILE } from './profile.js';
6
+ /** B 站的纯算签名与设备值(上游 utils/wbi.py、ticket.py、bv.py、murmur3.py、dm_img.py、device.py、correspond.py)。 */
7
+ export const md5 = (text) => md5Hex(text);
8
+ // ---------------------------------------------------------------- WBI
9
+ const MIXIN_KEY_ENC_TAB = [
10
+ 46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7,
11
+ 16, 24, 55, 40, 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11, 36, 20, 34, 44, 52,
12
+ ];
13
+ /** wbi 图片 URL 里的文件名(不带扩展名)。 */
14
+ export function extractKey(url) {
15
+ return url.slice(url.lastIndexOf('/') + 1).split('.')[0];
16
+ }
17
+ export function mixinKey(imgKey, subKey) {
18
+ const raw = (imgKey.includes('/') ? extractKey(imgKey) : imgKey) + (subKey.includes('/') ? extractKey(subKey) : subKey);
19
+ return MIXIN_KEY_ENC_TAB.map((i) => raw[i]).join('').slice(0, 32);
20
+ }
21
+ /**
22
+ * 给 query 补上 w_rid 与 wts。排序与剔除 `!'()*` 只作用于签名串,
23
+ * 发出去的参数保持原顺序,末尾依次追加 w_rid、wts。
24
+ */
25
+ export function encWbi(params, key, wts = rand.nowSeconds()) {
26
+ const toSign = [...params.filter(([k]) => k !== 'wts'), ['wts', wts]];
27
+ toSign.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
28
+ const query = urlencode(toSign.map(([k, v]) => [k, pyStr(v).replace(/[!'()*]/g, '')]));
29
+ const signed = params.filter(([k]) => k !== 'w_rid' && k !== 'wts');
30
+ return [...signed, ['w_rid', md5(query + key)], ['wts', wts]];
31
+ }
32
+ // ---------------------------------------------------------------- bili_ticket
33
+ export const TICKET_KEY_ID = 'ec02';
34
+ export function ticketHexSign(ts) {
35
+ return hmacSha256Hex('XgwSnGZ1p', `ts${ts}`);
36
+ }
37
+ // ---------------------------------------------------------------- av / bv
38
+ const XOR_CODE = 23442827791579n;
39
+ const MASK_CODE = 2251799813685247n;
40
+ const MAX_AID = 1n << 51n;
41
+ const ALPHABET = 'FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf';
42
+ const ENCODE_MAP = [8, 7, 0, 5, 1, 3, 2, 4, 6];
43
+ const DECODE_MAP = [...ENCODE_MAP].reverse();
44
+ const BASE = 58n;
45
+ export function av2bv(aid) {
46
+ const chars = Array(ENCODE_MAP.length).fill('');
47
+ let tmp = (MAX_AID | BigInt(aid)) ^ XOR_CODE;
48
+ for (const i of ENCODE_MAP) {
49
+ chars[i] = ALPHABET[Number(tmp % BASE)];
50
+ tmp /= BASE;
51
+ }
52
+ return 'BV1' + chars.join('');
53
+ }
54
+ export function bv2av(bvid) {
55
+ if (!bvid.startsWith('BV1'))
56
+ throw new Error(`不是合法的 bvid:${bvid}`);
57
+ const body = bvid.slice(3);
58
+ if (body.length !== ENCODE_MAP.length)
59
+ throw new Error(`bvid 长度不对:${bvid}`);
60
+ let tmp = 0n;
61
+ for (const i of DECODE_MAP)
62
+ tmp = tmp * BASE + BigInt(ALPHABET.indexOf(body[i]));
63
+ return String((tmp & MASK_CODE) ^ XOR_CODE);
64
+ }
65
+ // ---------------------------------------------------------------- murmur3_x64_128
66
+ const MASK64 = (1n << 64n) - 1n;
67
+ const C1 = 0x87c37b91114253d5n;
68
+ const C2 = 0x4cf5ad432745937fn;
69
+ const rotl = (x, r) => ((x << r) | (x >> (64n - r))) & MASK64;
70
+ function fmix64(k) {
71
+ k ^= k >> 33n;
72
+ k = (k * 0xff51afd7ed558ccdn) & MASK64;
73
+ k ^= k >> 33n;
74
+ k = (k * 0xc4ceb9fe1a85ec53n) & MASK64;
75
+ k ^= k >> 33n;
76
+ return k;
77
+ }
78
+ function readLE64(data, offset, length = 8) {
79
+ let v = 0n;
80
+ for (let i = length - 1; i >= 0; i--)
81
+ v = (v << 8n) | BigInt(data[offset + i]);
82
+ return v;
83
+ }
84
+ /** buvid_fp:murmur3_x64_128 的 32 位十六进制摘要。 */
85
+ export function murmur3Hex(input, seed = 31) {
86
+ const data = Buffer.from(input, 'utf8');
87
+ const length = data.length;
88
+ const nblocks = Math.floor(length / 16);
89
+ let h1 = BigInt(seed) & MASK64;
90
+ let h2 = h1;
91
+ for (let i = 0; i < nblocks; i++) {
92
+ let k1 = readLE64(data, i * 16);
93
+ let k2 = readLE64(data, i * 16 + 8);
94
+ k1 = (k1 * C1) & MASK64;
95
+ k1 = rotl(k1, 31n);
96
+ k1 = (k1 * C2) & MASK64;
97
+ h1 ^= k1;
98
+ h1 = rotl(h1, 27n);
99
+ h1 = (h1 + h2) & MASK64;
100
+ h1 = (h1 * 5n + 0x52dce729n) & MASK64;
101
+ k2 = (k2 * C2) & MASK64;
102
+ k2 = rotl(k2, 33n);
103
+ k2 = (k2 * C1) & MASK64;
104
+ h2 ^= k2;
105
+ h2 = rotl(h2, 31n);
106
+ h2 = (h2 + h1) & MASK64;
107
+ h2 = (h2 * 5n + 0x38495ab5n) & MASK64;
108
+ }
109
+ const tailAt = nblocks * 16;
110
+ const tailLen = length - tailAt;
111
+ if (tailLen > 8) {
112
+ let k2 = readLE64(data, tailAt + 8, tailLen - 8);
113
+ k2 = (k2 * C2) & MASK64;
114
+ k2 = rotl(k2, 33n);
115
+ k2 = (k2 * C1) & MASK64;
116
+ h2 ^= k2;
117
+ }
118
+ if (tailLen > 0) {
119
+ let k1 = readLE64(data, tailAt, Math.min(tailLen, 8));
120
+ k1 = (k1 * C1) & MASK64;
121
+ k1 = rotl(k1, 31n);
122
+ k1 = (k1 * C2) & MASK64;
123
+ h1 ^= k1;
124
+ }
125
+ h1 ^= BigInt(length);
126
+ h2 ^= BigInt(length);
127
+ h1 = (h1 + h2) & MASK64;
128
+ h2 = (h2 + h1) & MASK64;
129
+ h1 = fmix64(h1);
130
+ h2 = fmix64(h2);
131
+ h1 = (h1 + h2) & MASK64;
132
+ h2 = (h2 + h1) & MASK64;
133
+ return h1.toString(16).padStart(16, '0') + h2.toString(16).padStart(16, '0');
134
+ }
135
+ // ---------------------------------------------------------------- dm_img_*
136
+ /** `json.dumps(v, separators=(',', ':'))`(ensure_ascii 为默认的 True)。 */
137
+ export const dumps = (v) => jsonDumps(v, { separators: [',', ':'] });
138
+ const b64 = (text) => Buffer.from(text, 'utf8').toString('base64').replace(/=+$/, '');
139
+ /** 鼠标采样。缺省 0 条(空数组)才是浏览器刚加载时的行为。 */
140
+ function dmImgList(count) {
141
+ const samples = [];
142
+ let x = rand.randint(1000, 4000);
143
+ let y = rand.choice([rand.randint(150, 2000), -rand.randint(200, 5000)]);
144
+ let timestamp = rand.randint(3000, 40000);
145
+ let z = 0;
146
+ for (let i = 0; i < count; i++) {
147
+ samples.push({ x, y, z, timestamp, k: rand.randint(60, 128), type: i % 2 });
148
+ const stepX = rand.randint(-400, 500);
149
+ const stepY = rand.randint(-300, 400);
150
+ x += stepX;
151
+ y += stepY;
152
+ z = Math.max(0, z + Math.abs(stepX) + Math.abs(stepY) - rand.randint(0, 300));
153
+ timestamp += rand.randint(96, 120);
154
+ }
155
+ return dumps(samples);
156
+ }
157
+ function dmImgInter(withDs) {
158
+ const offset = rand.randint(1, 350);
159
+ const inter = {
160
+ ds: [],
161
+ wh: [rand.randint(7500, 7900), rand.randint(9000, 9200), rand.randint(0, 120)],
162
+ of: [offset, offset * 2, offset],
163
+ };
164
+ if (withDs) {
165
+ inter.ds = [
166
+ {
167
+ t: 7,
168
+ c: b64('vui_button vui_pagenation--btn vui_pagenation--btn-side'),
169
+ p: [rand.randint(3000, 7500), rand.randint(10, 200), rand.randint(3000, 7500)],
170
+ s: [rand.randint(300, 500), rand.randint(600, 900), rand.randint(900, 1200)],
171
+ },
172
+ ];
173
+ }
174
+ return dumps(inter);
175
+ }
176
+ /** 四个 dm_img 参数,参与 WBI 签名,必须在签名前加上。 */
177
+ export function dmImgParams(sampleCount = 0, withDs = false) {
178
+ return [
179
+ ['dm_img_list', dmImgList(sampleCount)],
180
+ ['dm_img_str', b64(PROFILE.webglVersion)],
181
+ ['dm_cover_img_str', b64((PROFILE.webglRenderer + PROFILE.webglVendor).slice(0, -1))],
182
+ ['dm_img_inter', dmImgInter(withDs)],
183
+ ];
184
+ }
185
+ // ---------------------------------------------------------------- 设备值
186
+ const UUID_CHARS = [...'123456789ABCDEF', '10'];
187
+ /** _uuid:五段随机 + 5 位毫秒尾数 + infoc。 */
188
+ export function uuidInfoc() {
189
+ const parts = [8, 4, 4, 4, 12].map((n) => Array.from({ length: n }, () => rand.choice(UUID_CHARS)).join(''));
190
+ return parts.join('-') + String(rand.now() % 100000).padEnd(5, '0') + 'infoc';
191
+ }
192
+ export function randomHex(length, upper = true) {
193
+ return rand.string(length, upper ? '0123456789ABCDEF' : '0123456789abcdef');
194
+ }
195
+ /** b_lsid:8 位随机十六进制 + '_' + 毫秒时间戳的十六进制。 */
196
+ export function bLsid() {
197
+ return `${randomHex(8)}_${rand.now().toString(16).toUpperCase()}`;
198
+ }
199
+ export function sid() {
200
+ return rand.string(8, '0123456789abcdefghijklmnopqrstuvwxyz');
201
+ }
202
+ /** 搜索接口的 qv_id。 */
203
+ export function qvId() {
204
+ return rand.string(32, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
205
+ }
206
+ export function liveBuvid() {
207
+ return `AUTO${rand.nowSeconds()}${rand.string(9, '0123456789')}`;
208
+ }
209
+ // ---------------------------------------------------------------- 续期与登录
210
+ const CORRESPOND_KEY = `-----BEGIN PUBLIC KEY-----
211
+ MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLgd2OAkcGVtoE3ThUREbio0Eg
212
+ Uc/prcajMKXvkCKFCWhJYJcLkcM2DKKcSeFpD/j6Boy538YXnR6VhcuUJOhH2x71
213
+ nzPjfdTcqMz7djHum0qSZA0AyCBDABUqCrfNgCiJ00Ra7GmRj+YCK1NJEuewlb40
214
+ JNrRuoEUXpabUzGB8QIDAQAB
215
+ -----END PUBLIC KEY-----`;
216
+ /** correspondPath:RSA-OAEP(SHA-256) 加密 `refresh_{毫秒时间戳}`,小写十六进制。 */
217
+ export function correspondPath(ts = rand.now()) {
218
+ return publicEncrypt({ key: CORRESPOND_KEY, padding: constants.RSA_PKCS1_OAEP_PADDING, oaepHash: 'sha256' }, Buffer.from(`refresh_${ts}`)).toString('hex');
219
+ }
220
+ /** 账密登录:用服务端公钥 RSA-OAEP(SHA-256) 加密 salt+密码,base64。 */
221
+ export function encryptPassword(salt, password, publicKeyPem) {
222
+ return publicEncrypt({ key: publicKeyPem, padding: constants.RSA_PKCS1_OAEP_PADDING, oaepHash: 'sha256' }, Buffer.from(salt + password)).toString('base64');
223
+ }
@@ -0,0 +1,102 @@
1
+ import { SpecificAIError } from '../../../core/errors.js';
2
+ import { parseJson } from '../../../core/http.js';
3
+ import { PROFILE } from './profile.js';
4
+ /**
5
+ * upos 分片上传(上游 utils/upos.py):preupload → 初始化分片 → 逐片 PUT → 合并。
6
+ * 上游四段都走 http_util.request:带风控退避重试,cookie 是整份 B 站 cookie,发往 upos 节点(bilivideo.com)时也带。
7
+ */
8
+ const PREUPLOAD = 'https://member.bilibili.com/preupload';
9
+ const PROFILE_NAME = 'ugcfx/bup';
10
+ function uposHeaders(auth = '') {
11
+ const h = [
12
+ ['user-agent', PROFILE.ua],
13
+ ['origin', 'https://member.bilibili.com'],
14
+ ['referer', 'https://member.bilibili.com/'],
15
+ ];
16
+ if (auth)
17
+ h.push(['x-upos-auth', auth]);
18
+ return h;
19
+ }
20
+ /** 请求并解析 JSON,`OK` 不是 1 时报 UPSTREAM(上游 raise RuntimeError)。不是 JSON 时 parseJson 报 UPSTREAM。 */
21
+ async function uposJson(b, stage, req) {
22
+ const res = await b.request(req);
23
+ const body = await parseJson(res);
24
+ if (body?.OK !== 1)
25
+ throw new SpecificAIError('UPSTREAM', `${stage}失败(HTTP ${res.status})`, { detail: { status: res.status, body } });
26
+ return body;
27
+ }
28
+ export async function uploadVideo(b, file) {
29
+ const size = file.data.length;
30
+ const cookies = Object.fromEntries(b.http.prepare({ url: PREUPLOAD }).cookies);
31
+ const pre = await uposJson(b, 'preupload', {
32
+ url: PREUPLOAD,
33
+ headers: uposHeaders(),
34
+ cookies,
35
+ query: [
36
+ ['name', file.filename],
37
+ ['size', size],
38
+ ['r', 'upos'],
39
+ ['profile', PROFILE_NAME],
40
+ ['ssl', 0],
41
+ ['version', '2.14.0'],
42
+ ['build', 2140000],
43
+ ['webVersion', '2.14.0'],
44
+ ],
45
+ timeout: 30,
46
+ });
47
+ const endpoint = String(pre.endpoint).startsWith('//') ? 'https:' + pre.endpoint : pre.endpoint;
48
+ const key = String(pre.upos_uri).replace('upos://', '');
49
+ const url = `${endpoint}/${key}`;
50
+ const chunkSize = Number(pre.chunk_size || 10 * 1024 * 1024);
51
+ const chunks = Math.max(1, Math.ceil(size / chunkSize));
52
+ // 这四个 query 字段来自投稿页 JS 的 uploadsQuery,少任何一个都会被 upos 以 InvalidArgument 拒绝
53
+ const init = await uposJson(b, '初始化分片', {
54
+ method: 'POST',
55
+ url: `${url}?uploads&output=json`,
56
+ headers: uposHeaders(pre.auth),
57
+ cookies,
58
+ query: [
59
+ ['profile', PROFILE_NAME],
60
+ ['filesize', size],
61
+ ['partsize', chunkSize],
62
+ ['biz_id', pre.biz_id],
63
+ ],
64
+ timeout: 60,
65
+ });
66
+ const parts = [];
67
+ for (let i = 0; i < chunks; i++) {
68
+ const start = i * chunkSize;
69
+ const data = file.data.subarray(start, start + chunkSize);
70
+ const end = start + data.length;
71
+ const res = await b.request({
72
+ method: 'PUT',
73
+ url: `${url}?partNumber=${i + 1}&uploadId=${init.upload_id}&chunk=${i}&chunks=${chunks}&size=${data.length}&start=${start}&end=${end}&total=${size}&output=json`,
74
+ headers: uposHeaders(pre.auth),
75
+ cookies,
76
+ body: data,
77
+ timeout: 300,
78
+ });
79
+ if (res.status !== 200) {
80
+ throw new SpecificAIError('UPSTREAM', `分片 ${i + 1}/${chunks} 上传失败:HTTP ${res.status}`, { detail: { status: res.status, body: (await res.text()).slice(0, 200) } });
81
+ }
82
+ parts.push({ partNumber: i + 1, eTag: 'etag' });
83
+ b.ctx.log.info(`上传 ${i + 1}/${chunks}`);
84
+ }
85
+ await uposJson(b, '分片合并', {
86
+ method: 'POST',
87
+ url,
88
+ headers: uposHeaders(pre.auth),
89
+ cookies,
90
+ query: [
91
+ ['output', 'json'],
92
+ ['name', file.filename],
93
+ ['profile', PROFILE_NAME],
94
+ ['uploadId', init.upload_id],
95
+ ['biz_id', pre.biz_id],
96
+ ],
97
+ json: { parts },
98
+ timeout: 120,
99
+ });
100
+ const base = key.slice(key.lastIndexOf('/') + 1);
101
+ return { filename: base.includes('.') ? base.slice(0, base.lastIndexOf('.')) : base, biz_id: pre.biz_id, key };
102
+ }
@@ -0,0 +1,142 @@
1
+ import { z } from 'zod';
2
+ import { filter, PRODUCT } from '../../core/options.js';
3
+ import { definePlatform, handlers } from '../../core/registry.js';
4
+ import { checkMsgSend } from '../../core/vocab.js';
5
+ /** 懒加载 web 端的 handler:只有执行到这条命令时才加载实现。 */
6
+ const load = () => import('./web/commands.js');
7
+ /** 读取类命令:缺 UIFID 时自动补上,整条命令重跑一次(commands.retryWithUifid)。 */
8
+ const { impl: read } = handlers(load, (m, handler) => m.retryWithUifid(handler));
9
+ /**
10
+ * 登录与写操作不整条重跑:重跑会把已经做完的步骤再做一遍,例如私信的文字已经发出、之后取分享卡片时撞上
11
+ * "Uifid Not Found",重跑就会把文字再发一次;登录重跑会再出一次二维码。缺 UIFID 时照常报 RISK_CONTROL。
12
+ */
13
+ const { impl: write } = handlers(load);
14
+ /** 平台私有选项(AGENTS 4.7 的 douyin 行)。 */
15
+ const folder = (summary) => ({ folder: z.string().optional().describe(summary) });
16
+ /** `msg send`:比词表多了 --file / --share 也算消息内容。--to 与 --item 同时用时由 handler 报 UNSUPPORTED。 */
17
+ const msgSendCheck = (a, o) => checkMsgSend(a, o, { content: ['file', 'share'] });
18
+ export default definePlatform({
19
+ id: 'douyin',
20
+ name: '抖音',
21
+ aliases: ['dy'],
22
+ item: '作品',
23
+ endpoints: {
24
+ web: {
25
+ login: { methods: ['qrcode', 'sms', 'cookie'], default: 'qrcode' },
26
+ commands: {
27
+ 'auth login': write('full', 'authLogin', {
28
+ options: { sso: z.boolean().optional().describe('短信登录改走 login.douyin.com 页的 SSO 链(sms)') },
29
+ }),
30
+ 'auth status': read('full', 'authStatus'),
31
+ 'user get': read('full', 'userGet'),
32
+ 'user search': read('full', 'userSearch', {
33
+ options: {
34
+ fans: z.enum(['0_1k', '1k_1w', '1w_10w', '10w_100w', '100w_']).optional().describe('粉丝数区间'),
35
+ userType: z.enum(['common', 'enterprise', 'personal']).optional().describe('用户类型:普通 / 企业 / 个人认证'),
36
+ },
37
+ }),
38
+ 'user items': read('full', 'userItems'),
39
+ 'user likes': read('full', 'userLikes'),
40
+ // 上游没有收藏作品列表(get_collect_list 返回的是收藏夹)
41
+ 'user collects': 'none',
42
+ 'user followers': read('full', 'userFollowers'),
43
+ 'user following': read('full', 'userFollowing'),
44
+ 'user follow': 'none',
45
+ 'user unfollow': 'none',
46
+ 'item get': read('full', 'itemGet'),
47
+ 'item search': read('full', 'itemSearch', {
48
+ // 综合频道(上游 search_general_work)的筛选值放在 filter_selected 里,--type image 是它的 content_type 2(图文)
49
+ note: '--type video 走视频频道搜索,其余走综合频道;--type image 只要图文',
50
+ options: {
51
+ sort: filter.sort('general', 'popular', 'latest'),
52
+ time: filter.time('all', 'day', 'week', 'half_year'),
53
+ type: filter.type('all', 'video', 'image'),
54
+ length: z.enum(['all', 'short', 'medium', 'long']).optional().describe('视频时长:1 分钟内 / 1–5 分钟 / 5 分钟以上'),
55
+ range: z.enum(['all', 'seen', 'unseen', 'following']).optional().describe('搜索范围:看过 / 没看过 / 关注的人'),
56
+ },
57
+ }),
58
+ 'item related': 'none',
59
+ 'item list': read('partial', 'itemList', { note: '取发布页的作品预览(work_list);定时未发布的作品 status 为 draft' }),
60
+ 'item media': read('full', 'itemMedia'),
61
+ 'item download': read('full', 'itemDownload'),
62
+ 'item like': write('full', 'itemLike'),
63
+ 'item unlike': write('full', 'itemUnlike'),
64
+ 'item collect': write('full', 'itemCollect', { options: folder('收藏后移进这个收藏夹(ID 或名字)') }),
65
+ 'item uncollect': write('full', 'itemUncollect', { options: folder('只从这个收藏夹移出,仍保留收藏(ID 或名字)') }),
66
+ 'item publish': write('full', 'itemPublish', {
67
+ supports: ['title', 'text', 'image', 'video', 'cover', 'tag', 'topic', 'mention', 'poi', 'visibility', 'schedule'],
68
+ options: {
69
+ poiName: z.string().optional().describe('地点名称,配合 --poi'),
70
+ series: z.string().optional().describe('加入合集(合集 ID)'),
71
+ hotspot: z.string().optional().describe('关联热点(热点词)'),
72
+ noDownload: z.boolean().optional().describe('不允许别人下载'),
73
+ },
74
+ }),
75
+ 'item delete': 'none',
76
+ 'product get': read('partial', 'productGet', { note: '上游的商品接口只给详情图、规格和跳转链接:只有标题、图片和价格,其余字段为空' }),
77
+ 'comment list': read('full', 'commentList', {
78
+ options: { product: PRODUCT, label: z.string().optional().describe('商品评价按标签筛选(标签名或 id,如 好评、有图)') },
79
+ }),
80
+ 'comment replies': read('full', 'commentReplies'),
81
+ 'comment add': write('full', 'commentAdd'),
82
+ 'comment delete': 'none',
83
+ 'comment like': 'none',
84
+ 'comment unlike': 'none',
85
+ 'feed list': read('partial', 'feedList', {
86
+ note: 'hot、following 规划中',
87
+ options: { kind: filter.kind('recommend', 'hot', 'following') },
88
+ }),
89
+ 'live get': read('full', 'liveGet'),
90
+ 'live list': 'none',
91
+ 'live search': read('full', 'liveSearch'),
92
+ 'live categories': 'none',
93
+ 'live listen': read('full', 'liveListen'),
94
+ 'live history': read('partial', 'liveHistory', { note: '只有进房时 im/fetch 带回的最近 15 条' }),
95
+ 'live send': write('partial', 'liveSend', { note: '--gift 规划中' }),
96
+ 'live like': write('full', 'liveLike', { options: { count: z.number().int().positive().optional().describe('一次点赞的次数,默认 1') } }),
97
+ 'live rank': read('full', 'liveRank', {
98
+ options: { ranking: z.enum(['contribution', 'thousand']).default('contribution').describe('榜单:贡献榜 / 千票榜') },
99
+ }),
100
+ 'live gifts': 'none',
101
+ 'live products': read('full', 'liveProducts'),
102
+ 'live media': read('partial', 'liveMedia', { note: '拉流地址取自房间资料(room/web/enter)' }),
103
+ 'live replays': 'none',
104
+ 'live start': 'none',
105
+ 'live stop': 'none',
106
+ 'keyword suggest': 'none',
107
+ 'keyword hot': 'none',
108
+ 'notice list': read('full', 'noticeList', {
109
+ options: { group: z.enum(['all', 'fans', 'mention', 'comment', 'like', 'danmaku']).optional().describe('通知分组,不带时为默认分组') },
110
+ }),
111
+ 'notice count': 'none',
112
+ 'msg list': 'none',
113
+ 'msg history': 'none',
114
+ 'msg send': write('full', 'msgSend', {
115
+ options: {
116
+ file: z.string().optional().describe('发文件(路径或 URL,≤10MB)'),
117
+ share: z.string().optional().describe('分享作品 / 用户名片 / 网页卡片(ID 或 URL)'),
118
+ },
119
+ check: msgSendCheck,
120
+ }),
121
+ 'msg listen': read('full', 'msgListen'),
122
+ 'msg read': 'none',
123
+ 'msg revoke': 'none',
124
+ 'msg delete': 'none',
125
+ 'media upload': write('full', 'mediaUpload'),
126
+ 'folder list': read('full', 'folderList'),
127
+ // 上游没有收藏夹内容接口
128
+ 'folder items': 'none',
129
+ 'folder create': 'none',
130
+ 'folder update': 'none',
131
+ 'folder delete': 'none',
132
+ 'series list': 'none',
133
+ 'series items': 'none',
134
+ 'history list': 'none',
135
+ 'topic search': 'none',
136
+ 'poi search': 'none',
137
+ },
138
+ },
139
+ app: 'planned',
140
+ pc: 'planned',
141
+ },
142
+ });