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,225 @@
1
+ import protobuf from 'protobufjs';
2
+ import { SpecificAIError } from '../../../core/errors.js';
3
+ import * as n from '../../../core/normalize.js';
4
+ import { jsonLoads, urlencode } from '../../../core/py.js';
5
+ import { openSocket, reconnecting } from '../../../core/stream.js';
6
+ import * as api from './api.js';
7
+ import { frontierUrl } from './im.js';
8
+ import * as norm from './normalize.js';
9
+ import * as proto from './proto.js';
10
+ import { APP_VERSION, LIVE, Params, PROFILE, WWW } from './profile.js';
11
+ import { liveSignature } from './sign.js';
12
+ /** im/fetch 的响应(LiveResponse);不是 protobuf 时多为登录态失效或被风控,报 UPSTREAM。 */
13
+ export function decodeFetch(raw) {
14
+ try {
15
+ return proto.decode('Live', 'LiveResponse', proto.inflate(raw));
16
+ }
17
+ catch {
18
+ throw new SpecificAIError('UPSTREAM', `im/fetch 返回的不是 protobuf(${raw.length} 字节),多为登录态失效或被风控`, { detail: { bytes: raw.length } });
19
+ }
20
+ }
21
+ /** 先 im/fetch 取 cursor / internalExt,再拼 wss 地址(上游 DouyinLive.start_ws)。 */
22
+ export async function liveSocket(d, info, webRid) {
23
+ const fetched = decodeFetch(await api.webcastFetch(d, info.user_id, info.room_id, `${LIVE}/${webRid}`));
24
+ const p = new Params();
25
+ p.add('app_name', 'douyin_web').add('version_code', '180800').add('webcast_sdk_version', '1.0.15').add('update_version_code', '1.0.15');
26
+ p.add('compress', 'gzip').add('device_platform', 'web').add('cookie_enabled', 'true').add('screen_width', '1707').add('screen_height', '960');
27
+ p.add('browser_language', 'zh-CN').add('browser_platform', 'Win32').add('browser_name', 'Mozilla').add('browser_version', APP_VERSION);
28
+ p.add('browser_online', 'true').add('tz_name', 'Etc/GMT-8').add('cursor', fetched.cursor ?? '').add('internal_ext', fetched.internalExt ?? '');
29
+ p.add('host', LIVE).add('aid', '6383').add('live_id', '1').add('did_rule', '3').add('endpoint', 'live_pc').add('support_wrds', '1');
30
+ p.add('user_unique_id', info.user_id).add('im_path', '/webcast/im/fetch/').add('identity', 'audience').add('need_persist_msg_count', '15');
31
+ p.add('insert_task_id', '').add('live_reason', '').add('room_id', info.room_id).add('heartbeatDuration', '0');
32
+ p.add('signature', liveSignature(d.xb, info.room_id, info.user_id));
33
+ return {
34
+ url: `wss://webcast100-ws-web-hl.douyin.com/webcast/im/push/v2/?${urlencode(p.pairs())}`,
35
+ headers: {
36
+ Pragma: 'no-cache',
37
+ 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
38
+ 'User-Agent': PROFILE.ua,
39
+ 'Cache-Control': 'no-cache',
40
+ Cookie: d.cookieStr,
41
+ Origin: LIVE,
42
+ },
43
+ };
44
+ }
45
+ const LIVE_TYPES = {
46
+ WebcastChatMessage: 'ChatMessage',
47
+ WebcastGiftMessage: 'GiftMessage',
48
+ WebcastMemberMessage: 'MemberMessage',
49
+ WebcastLikeMessage: 'LikeMessage',
50
+ WebcastSocialMessage: 'SocialMessage',
51
+ WebcastRoomStatsMessage: 'RoomStatsMessage',
52
+ };
53
+ /**
54
+ * 消息的发送时间:各 Webcast*Message 的字段 1 是公共头 Common,其中字段 4 是 createTime(毫秒)。
55
+ * 随包的 Live.proto(上游原样)没有声明 Common,这里用 protobufjs 的 Reader 直接读,取不到时为 null。
56
+ */
57
+ export function messageTime(payload) {
58
+ try {
59
+ const r = protobuf.Reader.create(payload);
60
+ while (r.pos < r.len) {
61
+ const tag = r.uint32();
62
+ if (tag >>> 3 !== 1 || (tag & 7) !== 2) {
63
+ r.skipType(tag & 7);
64
+ continue;
65
+ }
66
+ const common = protobuf.Reader.create(r.bytes());
67
+ while (common.pos < common.len) {
68
+ const t = common.uint32();
69
+ if (t >>> 3 === 4 && (t & 7) === 0)
70
+ return n.time(common.uint64().toString());
71
+ common.skipType(t & 7);
72
+ }
73
+ return null;
74
+ }
75
+ }
76
+ catch { }
77
+ return null;
78
+ }
79
+ /** LiveResponse 的 messagesList → Event[],不关心的消息跳过。时间取消息自带的 createTime。 */
80
+ function responseEvents(res) {
81
+ const events = [];
82
+ for (const item of res.messagesList ?? []) {
83
+ const name = LIVE_TYPES[item.method];
84
+ if (!name)
85
+ continue;
86
+ const event = norm.liveEvent(item.method, proto.decode('Live', name, item.payload));
87
+ if (!event)
88
+ continue;
89
+ const time = messageTime(item.payload);
90
+ if (time)
91
+ event.time = time;
92
+ events.push(event);
93
+ }
94
+ return events;
95
+ }
96
+ /** 一帧推送 → 事件;需要 ack 时返回 ack 帧(上游 on_message)。 */
97
+ export function liveFrame(raw) {
98
+ const t = proto.type('Live', 'PushFrame');
99
+ const frame = t.decode(raw);
100
+ if (frame.payloadType === 'hb' || frame.payloadType === 'ack' || !frame.payload?.length)
101
+ return { events: [], ack: null };
102
+ const res = proto.decode('Live', 'LiveResponse', proto.inflate(frame.payload));
103
+ const ack = res.needAck ? t.encode(t.fromObject({ payloadType: 'ack', payload: Buffer.from(String(res.internalExt ?? ''), 'utf8'), logId: frame.logId })).finish() : null;
104
+ return { events: responseEvents(res), ack };
105
+ }
106
+ /** 进房时 im/fetch 的响应(LiveResponse,need_persist_msg_count=15)里带回的最近消息。 */
107
+ export function fetchEvents(raw) {
108
+ return responseEvents(decodeFetch(raw));
109
+ }
110
+ export const heartbeatFrame = () => proto.encode('Live', 'PushFrame', { payloadType: 'hb' });
111
+ /** 直播间页面解析不出房间信息:房间号不对,或者直播已经结束。 */
112
+ export function roomNotFound(webRid) {
113
+ return new SpecificAIError('UPSTREAM', `未能解析直播间信息:${webRid}`, { hint: '确认直播间号,或者直播已经结束' });
114
+ }
115
+ /**
116
+ * 弹幕长连。进入重连循环之前先解析一次房间:房间号无效时直接报 UPSTREAM,不当成断线一直重连。
117
+ * 重连时重新取房间信息(重新开播后 room_id 会变),取不到同样报 UPSTREAM,由 core 连续 5 次后放弃。
118
+ */
119
+ export async function* listenLive(ctx, d, webRid) {
120
+ const first = await api.liveInfo(d, webRid);
121
+ if (!first)
122
+ throw roomNotFound(webRid);
123
+ yield* reconnecting(ctx, async function* (attempt) {
124
+ const info = attempt === 0 ? first : await api.liveInfo(d, webRid);
125
+ if (!info)
126
+ throw roomNotFound(webRid);
127
+ const s = await liveSocket(d, info, webRid);
128
+ const socket = await openSocket(s.url, { headers: s.headers, proxy: ctx.config.proxy ?? undefined, signal: ctx.signal });
129
+ const beat = setInterval(() => void socket.send(heartbeatFrame()).catch(() => { }), 5000).unref();
130
+ try {
131
+ for await (const raw of socket.messages) {
132
+ if (typeof raw === 'string')
133
+ continue;
134
+ try {
135
+ const { events, ack } = liveFrame(raw);
136
+ if (ack)
137
+ await socket.send(ack).catch(() => { });
138
+ yield* events;
139
+ }
140
+ catch (err) {
141
+ ctx.log.debug(`解析直播消息失败:${err.message}`);
142
+ }
143
+ }
144
+ }
145
+ finally {
146
+ clearInterval(beat);
147
+ socket.close();
148
+ }
149
+ });
150
+ }
151
+ // ================================================================ 私信
152
+ /** frontier-im 的 wss 地址与握手头(上游 DouyinRecvMsg)。 */
153
+ export async function frontierSocket(d) {
154
+ const deviceId = await api.deviceId(d);
155
+ return {
156
+ url: frontierUrl(String(deviceId), d.cookie('sessionid') ?? ''),
157
+ headers: {
158
+ Pragma: 'no-cache',
159
+ 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
160
+ 'User-Agent': PROFILE.ua,
161
+ 'Cache-Control': 'no-cache',
162
+ 'Sec-WebSocket-Protocol': 'binary, base64, pbbp2',
163
+ Cookie: d.cookieStr,
164
+ Origin: WWW,
165
+ },
166
+ };
167
+ }
168
+ const MSG_TYPES = { 7: 'text', 27: 'image', 30: 'video', 8: 'card', 77: 'card', 25: 'card', 26: 'card' };
169
+ /** 新消息推送 → Message;已读回执等返回 null(上游 on_message)。 */
170
+ export function imFrame(raw) {
171
+ const frame = proto.decode('Live', 'PushFrame', raw);
172
+ if (frame.payloadType !== 'pb' || !frame.payload)
173
+ return null;
174
+ const res = proto.decode('Response', 'Response', frame.payload);
175
+ const m = res.body?.new_message_notify?.message;
176
+ if (!m)
177
+ return null;
178
+ const type = Number(m.message_type);
179
+ if (type === 50001)
180
+ return null;
181
+ let content = {};
182
+ try {
183
+ content = jsonLoads(m.content ?? '{}');
184
+ }
185
+ catch { }
186
+ const media = [];
187
+ const url = (x) => n.url(x?.origin_url_list?.[0] ?? x?.url_list?.[0]);
188
+ if (type === 27 && url(content.resource_url))
189
+ media.push(n.media({ type: 'image', url: url(content.resource_url) }));
190
+ if (type === 5 && url(content.url))
191
+ media.push(n.media({ type: 'image', url: url(content.url) }));
192
+ if (type === 17 && url(content.resource_url))
193
+ media.push(n.media({ type: 'audio', url: url(content.resource_url) }));
194
+ return n.message({
195
+ id: n.id(m.server_message_id),
196
+ conversation_id: String(m.conversation_id ?? ''),
197
+ from: n.userRef({ id: m.sender }),
198
+ type: MSG_TYPES[type] ?? 'other',
199
+ text: type === 7 ? n.str(content.text) : type === 8 ? n.str(content.itemId) : null,
200
+ media,
201
+ }, m);
202
+ }
203
+ export function listenMessages(ctx, d) {
204
+ return reconnecting(ctx, async function* () {
205
+ const s = await frontierSocket(d);
206
+ const socket = await openSocket(s.url, { headers: s.headers, proxy: ctx.config.proxy ?? undefined, signal: ctx.signal });
207
+ try {
208
+ for await (const raw of socket.messages) {
209
+ if (typeof raw === 'string')
210
+ continue;
211
+ try {
212
+ const msg = imFrame(raw);
213
+ if (msg)
214
+ yield msg;
215
+ }
216
+ catch (err) {
217
+ ctx.log.debug(`解析私信推送失败:${err.message}`);
218
+ }
219
+ }
220
+ }
221
+ finally {
222
+ socket.close();
223
+ }
224
+ });
225
+ }
@@ -0,0 +1,148 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { staticFile } from '../../../core/paths.js';
3
+ import { compactJson, jsonLoads } from '../../../core/py.js';
4
+ import * as rand from '../../../core/rand.js';
5
+ import { APP_VERSION, PROFILE } from './profile.js';
6
+ import { encodeStrData } from './sign.js';
7
+ /**
8
+ * mssdk 上报体(上游 utils/strdata_pure.py、utils/mstoken.py)。设备常量来自上游抓的档案,
9
+ * 原样复制在 static/douyin/ 下;每次只重算 uuid、采集耗时、时间戳与 nonce。
10
+ */
11
+ const cache = new Map();
12
+ function profile(name) {
13
+ if (!cache.has(name))
14
+ cache.set(name, JSON.parse(readFileSync(staticFile('douyin', name), 'utf8')));
15
+ return structuredClone(cache.get(name));
16
+ }
17
+ /** Python `json.dumps(float)`:整数值的浮点数带 `.0`。 */
18
+ function pyFloat(x) {
19
+ return Number.isInteger(x) ? `${x}.0` : x;
20
+ }
21
+ /** 把 `"collectTime":"<n>.0"` 这类占位还原成数字字面量。 */
22
+ function withRawFloats(json, raw) {
23
+ let out = json;
24
+ for (const [key, v] of Object.entries(raw))
25
+ if (typeof v === 'string')
26
+ out = out.replace(`"${key}":"${v}"`, `"${key}":${v}`);
27
+ return out;
28
+ }
29
+ /** /web/r/token 的上报体(上游 build_report_body)。 */
30
+ export function strDataReport(aid = 6383, pageId = 6241) {
31
+ const data = profile('mstoken_profile.json');
32
+ const n = data.nWID;
33
+ const [, , , , availW, availH] = PROFILE.geo;
34
+ Object.assign(n.screen, { height: Number(PROFILE.screenHeight), width: Number(PROFILE.screenWidth), availHeight: availH, availWidth: availW, availTop: 0, availLeft: 0 });
35
+ n.navigator.hardwareConcurrency = Number(PROFILE.cpuCoreNum);
36
+ n.navigator.userAgent = PROFILE.ua;
37
+ n.webgl.renderer = PROFILE.webglRenderer;
38
+ n.webgl.vendor = PROFILE.webglVendor;
39
+ n.audio = n.audio ?? {};
40
+ n.audio.audioContext = n.audio.audioContext ?? {};
41
+ n.audio.audioContext.state = 'running';
42
+ const msVersion = n.ms_version ?? '0.0.0.1';
43
+ delete n.ms_version;
44
+ const uuid = rand.uuid4();
45
+ const collectTime = pyFloat(Number(rand.uniform(40, 160).toFixed(10)));
46
+ n.custom = withRawFloats(compactJson({ version: msVersion, fxgDid: '', uuid, collectTime, aid, pageId }), { collectTime });
47
+ n.ms_version = msVersion;
48
+ const plaintext = compactJson({ nWID: n, wID: data.wID });
49
+ const nonce = rand.randint(0, 255);
50
+ const ts = rand.now();
51
+ return compactJson({ magic: 538969122, version: 1, dataType: 8, strData: encodeStrData(plaintext, nonce), tspFromClient: ts, ulr: 0 });
52
+ }
53
+ /** /web/common 的完整 msgType=1 上报(上游 build_common_report_body)。 */
54
+ export function commonReport(aid = 6383, pageId = 6241, sms = false) {
55
+ const p = profile('mstoken_common_profile.json');
56
+ const now = rand.now();
57
+ if (sms)
58
+ p.ubCode = 12;
59
+ const n = p.nWID ?? {};
60
+ if (sms)
61
+ n.ubCode = 12;
62
+ n.canvas = n.canvas ?? {};
63
+ n.canvas.crc32 = 'A7996F6A';
64
+ n.audio = n.audio ?? {};
65
+ n.audio.audioContext = n.audio.audioContext ?? {};
66
+ n.audio.audioContext.state = 'running';
67
+ let custom;
68
+ try {
69
+ custom = typeof n.custom === 'string' ? jsonLoads(n.custom) : { ...(n.custom ?? {}) };
70
+ }
71
+ catch {
72
+ custom = {};
73
+ }
74
+ custom.version ??= n.ms_version ?? '0.0.0.1';
75
+ custom.fxgDid ??= '';
76
+ custom.uuid = rand.uuid4();
77
+ custom.collectTime = sms ? 23.799999952316284 : rand.randint(10, 99);
78
+ const msVersion = n.ms_version ?? '0.0.0.1';
79
+ delete n.ms_version;
80
+ n.custom = compactJson(custom);
81
+ n.ms_version = msVersion;
82
+ const wid = p.wID ?? {};
83
+ Object.assign(wid, { msgType: 1, timestamp: String(now), aid, pageId, nap: '11311144242322244122' });
84
+ const g = PROFILE.geo;
85
+ Object.assign((p.navigator ??= {}), { appVersion: APP_VERSION, deviceMemory: PROFILE.deviceMemory, hardwareConcurrency: Number(PROFILE.cpuCoreNum) });
86
+ Object.assign((p.webgl ??= {}), { renderer: PROFILE.webglRenderer, vendor: PROFILE.webglVendor });
87
+ Object.assign((n.navigator ??= {}), { userAgent: PROFILE.ua, hardwareConcurrency: Number(PROFILE.cpuCoreNum) });
88
+ Object.assign((n.screen ??= {}), { height: Number(PROFILE.screenHeight), width: Number(PROFILE.screenWidth), availHeight: g[5], availWidth: g[4], availTop: 0, availLeft: 0 });
89
+ Object.assign((n.webgl ??= {}), { renderer: PROFILE.webglRenderer, vendor: PROFILE.webglVendor });
90
+ Object.assign((p.screen ??= {}), {
91
+ innerWidth: g[0],
92
+ innerHeight: g[1],
93
+ outerWidth: g[2],
94
+ outerHeight: g[3],
95
+ screenX: PROFILE.screenX,
96
+ screenY: PROFILE.screenY,
97
+ availWidth: g[4],
98
+ availHeight: g[5],
99
+ sizeWidth: g[6],
100
+ sizeHeight: g[7],
101
+ clientWidth: g[0],
102
+ clientHeight: g[1],
103
+ });
104
+ p.nWID = n;
105
+ p.wID = wid;
106
+ const nonce = rand.randint(0, 255);
107
+ const strData = encodeStrData(compactJson(p), nonce);
108
+ return compactJson({ magic: 538969122, version: 1, dataType: 8, strData, tspFromClient: sms ? now + 11 : now + 10, ulr: 0 });
109
+ }
110
+ /** 5 分钟一次的 msgType=2 行为心跳(上游 build_common_behavior_body)。 */
111
+ export function commonBehavior() {
112
+ const now = rand.now();
113
+ const g = PROFILE.geo;
114
+ const plaintext = compactJson({
115
+ wID: { msgType: 2, privacyMode: 0, timestamp: String(now) },
116
+ behavior: {
117
+ beMove: [],
118
+ beClick: [],
119
+ beClickEnd: [],
120
+ beKeyboard: [],
121
+ windowState: [],
122
+ gyro: [],
123
+ focus: [],
124
+ screen: {
125
+ innerWidth: g[0],
126
+ innerHeight: g[1],
127
+ outerWidth: g[2],
128
+ outerHeight: g[3],
129
+ screenX: PROFILE.screenX,
130
+ screenY: PROFILE.screenY,
131
+ pageXOffset: 0,
132
+ pageYOffset: 0,
133
+ availWidth: g[4],
134
+ availHeight: g[5],
135
+ sizeWidth: g[6],
136
+ sizeHeight: g[7],
137
+ clientWidth: g[0],
138
+ clientHeight: g[1],
139
+ colorDepth: 24,
140
+ pixelDepth: 24,
141
+ orientaionType: 'landscape-primary',
142
+ orientaionAngle: 0,
143
+ },
144
+ },
145
+ });
146
+ const nonce = rand.randint(0, 255);
147
+ return compactJson({ magic: 538969122, version: 1, dataType: 8, strData: encodeStrData(plaintext, nonce), tspFromClient: now + 3, ulr: 0 });
148
+ }