specific-ai-cli 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.en.md +177 -0
- package/README.ja.md +177 -0
- package/README.ko.md +177 -0
- package/README.md +174 -2
- package/README.zh-TW.md +177 -0
- package/assets/banner.txt +3 -0
- package/dist/cli/argv.js +98 -0
- package/dist/cli/commands/auth.js +11 -0
- package/dist/cli/commands/config.js +23 -0
- package/dist/cli/commands/doctor.js +134 -0
- package/dist/cli/commands/platforms.js +24 -0
- package/dist/cli/commands/version.js +4 -0
- package/dist/cli/confirm.js +22 -0
- package/dist/cli/dispatch.js +440 -0
- package/dist/cli/help.js +223 -0
- package/dist/cli/hints.js +60 -0
- package/dist/cli/main.js +18 -0
- package/dist/cli/output.js +66 -0
- package/dist/core/auth-handlers.js +41 -0
- package/dist/core/auth-store.js +119 -0
- package/dist/core/capabilities.js +163 -0
- package/dist/core/config.js +121 -0
- package/dist/core/cookies.js +164 -0
- package/dist/core/errors.js +37 -0
- package/dist/core/files.js +89 -0
- package/dist/core/fsutil.js +52 -0
- package/dist/core/hash.js +4 -0
- package/dist/core/http.js +331 -0
- package/dist/core/image.js +280 -0
- package/dist/core/log.js +40 -0
- package/dist/core/login.js +191 -0
- package/dist/core/mp4.js +130 -0
- package/dist/core/normalize.js +207 -0
- package/dist/core/options.js +110 -0
- package/dist/core/paths.js +38 -0
- package/dist/core/pb.js +59 -0
- package/dist/core/py.js +181 -0
- package/dist/core/rand.js +131 -0
- package/dist/core/registry.js +129 -0
- package/dist/core/schemas.js +41 -0
- package/dist/core/stream.js +104 -0
- package/dist/core/toolkit.js +35 -0
- package/dist/core/vm.js +73 -0
- package/dist/core/vocab.js +157 -0
- package/dist/platforms/_shared/impaas.js +410 -0
- package/dist/platforms/bilibili/index.js +216 -0
- package/dist/platforms/bilibili/web/api.js +806 -0
- package/dist/platforms/bilibili/web/client.js +311 -0
- package/dist/platforms/bilibili/web/commands.js +783 -0
- package/dist/platforms/bilibili/web/ddddocr.js +342 -0
- package/dist/platforms/bilibili/web/gaia.js +130 -0
- package/dist/platforms/bilibili/web/geetest-manual.js +154 -0
- package/dist/platforms/bilibili/web/geetest-vision.js +232 -0
- package/dist/platforms/bilibili/web/geetest.js +450 -0
- package/dist/platforms/bilibili/web/normalize.js +234 -0
- package/dist/platforms/bilibili/web/profile.js +135 -0
- package/dist/platforms/bilibili/web/resolve.js +112 -0
- package/dist/platforms/bilibili/web/sign.js +223 -0
- package/dist/platforms/bilibili/web/upos.js +102 -0
- package/dist/platforms/douyin/index.js +142 -0
- package/dist/platforms/douyin/web/api.js +863 -0
- package/dist/platforms/douyin/web/client.js +413 -0
- package/dist/platforms/douyin/web/commands.js +712 -0
- package/dist/platforms/douyin/web/creator.js +601 -0
- package/dist/platforms/douyin/web/crypto.js +247 -0
- package/dist/platforms/douyin/web/dtrait.js +190 -0
- package/dist/platforms/douyin/web/im.js +502 -0
- package/dist/platforms/douyin/web/image.js +5 -0
- package/dist/platforms/douyin/web/live.js +225 -0
- package/dist/platforms/douyin/web/mssdk.js +148 -0
- package/dist/platforms/douyin/web/normalize.js +326 -0
- package/dist/platforms/douyin/web/passport.js +1247 -0
- package/dist/platforms/douyin/web/profile.js +221 -0
- package/dist/platforms/douyin/web/proto.js +26 -0
- package/dist/platforms/douyin/web/resolve.js +88 -0
- package/dist/platforms/douyin/web/sign.js +436 -0
- package/dist/platforms/douyin/web/tos.js +78 -0
- package/dist/platforms/index.js +17 -0
- package/dist/platforms/jd/index.js +67 -0
- package/dist/platforms/jd/web/aks.js +115 -0
- package/dist/platforms/jd/web/api.js +549 -0
- package/dist/platforms/jd/web/chat.js +132 -0
- package/dist/platforms/jd/web/client.js +470 -0
- package/dist/platforms/jd/web/commands.js +557 -0
- package/dist/platforms/jd/web/jcap/helper.js +90 -0
- package/dist/platforms/jd/web/jcap/host.js +107 -0
- package/dist/platforms/jd/web/jcap/lsd.js +300 -0
- package/dist/platforms/jd/web/jcap/solver.js +995 -0
- package/dist/platforms/jd/web/jcap.js +150 -0
- package/dist/platforms/jd/web/js.js +420 -0
- package/dist/platforms/jd/web/login.js +775 -0
- package/dist/platforms/jd/web/normalize.js +297 -0
- package/dist/platforms/jd/web/profile.js +256 -0
- package/dist/platforms/jd/web/util.js +175 -0
- package/dist/platforms/jd/web/webm.js +133 -0
- package/dist/platforms/kuaishou/index.js +98 -0
- package/dist/platforms/kuaishou/web/api.js +694 -0
- package/dist/platforms/kuaishou/web/captcha-crypto.js +134 -0
- package/dist/platforms/kuaishou/web/captcha-fp.js +180 -0
- package/dist/platforms/kuaishou/web/captcha.js +273 -0
- package/dist/platforms/kuaishou/web/client.js +487 -0
- package/dist/platforms/kuaishou/web/commands.js +863 -0
- package/dist/platforms/kuaishou/web/gap.js +144 -0
- package/dist/platforms/kuaishou/web/gdfp.js +376 -0
- package/dist/platforms/kuaishou/web/gql.js +11 -0
- package/dist/platforms/kuaishou/web/live.js +125 -0
- package/dist/platforms/kuaishou/web/normalize.js +241 -0
- package/dist/platforms/kuaishou/web/oracle.js +329 -0
- package/dist/platforms/kuaishou/web/profile.js +259 -0
- package/dist/platforms/kuaishou/web/resolve.js +84 -0
- package/dist/platforms/kuaishou/web/session.js +592 -0
- package/dist/platforms/kuaishou/web/sign.js +568 -0
- package/dist/platforms/taobao/index.js +56 -0
- package/dist/platforms/taobao/web/api.js +96 -0
- package/dist/platforms/taobao/web/client.js +102 -0
- package/dist/platforms/taobao/web/commands.js +172 -0
- package/dist/platforms/taobao/web/im.js +68 -0
- package/dist/platforms/taobao/web/normalize.js +103 -0
- package/dist/platforms/taobao/web/profile.js +85 -0
- package/dist/platforms/taobao/web/resolve.js +51 -0
- package/dist/platforms/taobao/web/sign.js +35 -0
- package/dist/platforms/tiktok/index.js +120 -0
- package/dist/platforms/tiktok/web/api.js +1234 -0
- package/dist/platforms/tiktok/web/client.js +362 -0
- package/dist/platforms/tiktok/web/commands.js +821 -0
- package/dist/platforms/tiktok/web/im.js +133 -0
- package/dist/platforms/tiktok/web/jsrun.js +126 -0
- package/dist/platforms/tiktok/web/normalize.js +286 -0
- package/dist/platforms/tiktok/web/profile.js +169 -0
- package/dist/platforms/tiktok/web/resolve.js +156 -0
- package/dist/platforms/tiktok/web/sign.js +585 -0
- package/dist/platforms/tiktok/web/upload.js +805 -0
- package/dist/platforms/tiktok/web/wire.js +490 -0
- package/dist/platforms/weibo/index.js +91 -0
- package/dist/platforms/weibo/web/api.js +206 -0
- package/dist/platforms/weibo/web/client.js +155 -0
- package/dist/platforms/weibo/web/commands.js +202 -0
- package/dist/platforms/weibo/web/normalize.js +137 -0
- package/dist/platforms/weibo/web/profile.js +246 -0
- package/dist/platforms/weibo/web/resolve.js +60 -0
- package/dist/platforms/weibo/web/sign.js +85 -0
- package/dist/platforms/x/index.js +107 -0
- package/dist/platforms/x/web/api.js +443 -0
- package/dist/platforms/x/web/article.js +212 -0
- package/dist/platforms/x/web/client.js +202 -0
- package/dist/platforms/x/web/commands.js +363 -0
- package/dist/platforms/x/web/graphql.js +108 -0
- package/dist/platforms/x/web/normalize.js +235 -0
- package/dist/platforms/x/web/profile.js +99 -0
- package/dist/platforms/x/web/resolve.js +44 -0
- package/dist/platforms/x/web/transaction.js +171 -0
- package/dist/platforms/xhs/index.js +101 -0
- package/dist/platforms/xhs/web/api.js +278 -0
- package/dist/platforms/xhs/web/client.js +540 -0
- package/dist/platforms/xhs/web/commands.js +1046 -0
- package/dist/platforms/xhs/web/creator-api.js +609 -0
- package/dist/platforms/xhs/web/creator.js +203 -0
- package/dist/platforms/xhs/web/js.js +194 -0
- package/dist/platforms/xhs/web/login.js +230 -0
- package/dist/platforms/xhs/web/normalize.js +166 -0
- package/dist/platforms/xhs/web/profile.js +127 -0
- package/dist/platforms/xhs/web/push.js +361 -0
- package/dist/platforms/xhs/web/resolve.js +69 -0
- package/dist/platforms/xhs/web/state.js +368 -0
- package/dist/platforms/xianyu/index.js +59 -0
- package/dist/platforms/xianyu/web/api.js +312 -0
- package/dist/platforms/xianyu/web/client.js +134 -0
- package/dist/platforms/xianyu/web/commands.js +305 -0
- package/dist/platforms/xianyu/web/im.js +73 -0
- package/dist/platforms/xianyu/web/normalize.js +123 -0
- package/dist/platforms/xianyu/web/profile.js +146 -0
- package/dist/platforms/xianyu/web/resolve.js +48 -0
- package/dist/platforms/xianyu/web/sign.js +79 -0
- package/package.json +77 -4
- package/static/douyin/Live.proto +194 -0
- package/static/douyin/Request.proto +89 -0
- package/static/douyin/Response.proto +46 -0
- package/static/douyin/acrawler_runtime/ac_vm.js +2 -0
- package/static/douyin/acrawler_runtime/browser_window_shape.json +1 -0
- package/static/douyin/acrawler_runtime/canvas_actual_exact.json +1 -0
- package/static/douyin/acrawler_runtime/run_ac_node.js +1005 -0
- package/static/douyin/acrawler_runtime/runner_window_keys_baseline.txt +240 -0
- package/static/douyin/challenge_profile.json +801 -0
- package/static/douyin/challenge_template_runner.js +217 -0
- package/static/douyin/dtrait_profile.json +66 -0
- package/static/douyin/mstoken_common_profile.json +1 -0
- package/static/douyin/mstoken_profile.json +214 -0
- package/static/jd/h5st5_env.js +750 -0
- package/static/jd/h5st5_lib.js +5 -0
- package/static/jd/h5st5_server.js +49 -0
- package/static/jd/jcap/env/env_core.js +512 -0
- package/static/jd/jcap/env/run.js +1307 -0
- package/static/jd/jcap/run/jcap_ujb96b.js +39 -0
- package/static/jd/package.json +4 -0
- package/static/jd/pc_tk_lib.js +1 -0
- package/static/jd/pc_tk_server.js +63 -0
- package/static/jd/summer_cryptico_runner.js +50 -0
- package/static/jd/webm/env/run.js +453 -0
- package/static/jd/webm/run/jdwebm-riskhandle.js +49 -0
- package/static/kuaishou/bundles/weapon/kwf-0.0.2.2cee19b4b7dec496.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-0-0.0.1-obfuscated.4b7f3de1ab0ddcc2.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-1-0.0.1-obfuscated.346ebdb1c78df316.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-10-0.0.1-obfuscated.a33db41897b983af.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-11-0.0.1-obfuscated.5e0a90af726d8a7e.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-12-0.0.1-obfuscated.20ea29345706ec8c.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-13-0.0.1-obfuscated.6b74e9640ff18648.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-14-0.0.1-obfuscated.551e4429d156bf66.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-15-0.0.1-obfuscated.8625b087aaba4a60.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-16-0.0.1-obfuscated.d5ffa4020f3be99a.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-17-0.0.1-obfuscated.6d13e19b22f50a80.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-18-0.0.1-obfuscated.6d31405044166289.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-19-0.0.1-obfuscated.a9a8d925942f19e4.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-2-0.0.1-obfuscated.b849a136ae4a6b3e.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-3-0.0.1-obfuscated.f9e77ebf6186e2ac.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-4-0.0.1-obfuscated.2c3c155aa52ceb0e.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-5-0.0.1-obfuscated.4e553d17691b3c5a.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-6-0.0.1-obfuscated.f73ddd3ff13e1217.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-7-0.0.1-obfuscated.1628b8729beaab7e.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-8-0.0.1-obfuscated.6da6baef7aa81fff.js +1 -0
- package/static/kuaishou/bundles/weapon/kws-9-0.0.1-obfuscated.8645d55e5323b743.js +1 -0
- package/static/kuaishou/fixtures/current_follow_script_813.network-response +2 -0
- package/static/kuaishou/fixtures/live_ws_maps.json +61 -0
- package/static/kuaishou/fixtures/live_ws_proto.json +1342 -0
- package/static/kuaishou/js/cp-kwf.js +1 -0
- package/static/kuaishou/tools/kwf_oracle.js +799 -0
- package/static/kuaishou/tools/kws_oracle.js +885 -0
- package/static/kuaishou/tools/like_token_oracle.js +70 -0
- package/static/taobao/taobao_js_20260407.js +570 -0
- package/static/tiktok/Tiktok_Request.json +597 -0
- package/static/tiktok/package.json +3 -0
- package/static/tiktok/shop_bsid/core.js +1 -0
- package/static/tiktok/shop_bsid/env_core.js +512 -0
- package/static/tiktok/shop_bsid/loader.js +1 -0
- package/static/tiktok/shop_bsid/run.js +753 -0
- package/static/tiktok/shop_bsid/sign.js +100 -0
- package/static/tiktok/shop_bsid/webmssdk.js +1 -0
- package/static/tiktok/shop_bsid/webmssdk_ex.js +1 -0
- package/static/tiktok/signing/env/env_core.js +512 -0
- package/static/tiktok/signing/env/sign.js +455 -0
- package/static/tiktok/signing/sdk_config.json +230 -0
- package/static/tiktok/signing/source/webmssdk.js +1 -0
- package/static/tiktok/signing/source/webmssdk_ex.js +1 -0
- package/static/tiktok/signing/source/webmssdk_legacy.js +1 -0
- package/static/x/graphql.json +7777 -0
- package/static/x/transaction_l1.json +12 -0
- package/static/xhs/package.json +3 -0
- package/static/xhs/xhs_core/js/b1.js +210 -0
- package/static/xhs/xhs_core/js/mns.js +340 -0
- package/static/xhs/xhs_core/js/mns_0101_keystream.json +5 -0
- package/static/xhs/xhs_core/js/mns_keystreams.json +1 -0
- package/static/xhs/xhs_core/js/sign.js +225 -0
- package/static/xhs/xhs_core/js/websectiga_cli.js +220 -0
- package/static/xhs/xhs_core/js/websectiga_env.js +512 -0
- package/static/xhs/xhs_core/js/xs_common.js +79 -0
- package/static/xhs/xhs_creator/js/profile.js +115 -0
- package/static/xhs/xhs_creator/js/rap_fingerprint_creator.json +6 -0
- package/static/xhs/xhs_creator/js/reference_profile.json +282 -0
- package/static/xhs/xhs_creator/js/xhs_creator_sign.js +29 -0
- package/static/xhs/xhs_creator/js/xhs_creator_signature.js +23 -0
- package/static/xhs/xhs_pc/js/aes_decrypt.js +22 -0
- package/static/xhs/xhs_pc/js/aes_encrypt.js +30 -0
- package/static/xhs/xhs_pc/js/b1.js +9 -0
- package/static/xhs/xhs_pc/js/deflate.js +320 -0
- package/static/xhs/xhs_pc/js/mns.js +4 -0
- package/static/xhs/xhs_pc/js/profile.js +141 -0
- package/static/xhs/xhs_pc/js/rap.js +173 -0
- package/static/xhs/xhs_pc/js/rap_cli.js +30 -0
- package/static/xhs/xhs_pc/js/rap_crypto.js +73 -0
- package/static/xhs/xhs_pc/js/rap_fingerprint_template.json +8 -0
- package/static/xhs/xhs_pc/js/reference_profile.json +262 -0
- package/static/xhs/xhs_pc/js/sign.js +6 -0
- package/static/xhs/xhs_pc/js/web_ssk.js +82 -0
- package/static/xhs/xhs_pc/js/xs_common.js +4 -0
- package/static/xianyu/et_f.js +2 -0
- package/static/xianyu/gen_tfstk.js +410 -0
- package/static/xianyu/goofish_js_version_2.js +568 -0
|
@@ -0,0 +1,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
|
+
}
|