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,863 @@
|
|
|
1
|
+
import { compactJson, jsonDumps, jsonLoads, quote, urlencode } from '../../../core/py.js';
|
|
2
|
+
import * as rand from '../../../core/rand.js';
|
|
3
|
+
import { authError } from '../../../core/toolkit.js';
|
|
4
|
+
import { riskJson } from './client.js';
|
|
5
|
+
import { strDataReport } from './mssdk.js';
|
|
6
|
+
import { APP_VERSION, headers, LIVE, livePlatformParams, Params, platformParams, PROFILE, WWW } from './profile.js';
|
|
7
|
+
import { randomMsToken, signedUrl, spliceUrl } from './sign.js';
|
|
8
|
+
/**
|
|
9
|
+
* 上游 dy_apis/douyin_api.py 等的请求构造,一个函数对应一个上游方法,字段与顺序照抄(对拍测试逐字节比较)。
|
|
10
|
+
* webid / msToken / csrf 等附带请求发生的时机也与上游一致。返回平台原始 JSON。
|
|
11
|
+
*/
|
|
12
|
+
const LIVE_HOST = 'live.douyin.com';
|
|
13
|
+
// ---------------------------------------------------------------- 公共片段
|
|
14
|
+
async function withWebId(d, p) {
|
|
15
|
+
p.add('webid', await d.webid());
|
|
16
|
+
}
|
|
17
|
+
function withUifid(d, p) {
|
|
18
|
+
const v = d.cookie('UIFID');
|
|
19
|
+
if (v)
|
|
20
|
+
p.add('uifid', v);
|
|
21
|
+
}
|
|
22
|
+
function headerUifid(d, h) {
|
|
23
|
+
const v = d.cookie('UIFID');
|
|
24
|
+
if (v)
|
|
25
|
+
h.set('uifid', v);
|
|
26
|
+
return h;
|
|
27
|
+
}
|
|
28
|
+
function withVerifyFp(d, p) {
|
|
29
|
+
const fp = d.cookie('s_v_web_id');
|
|
30
|
+
if (fp)
|
|
31
|
+
p.add('verifyFp', fp).add('fp', fp);
|
|
32
|
+
}
|
|
33
|
+
function fp(d, p) {
|
|
34
|
+
const v = d.cookie('s_v_web_id') ?? '';
|
|
35
|
+
p.add('verifyFp', v).add('fp', v);
|
|
36
|
+
}
|
|
37
|
+
async function withMsToken(d, p) {
|
|
38
|
+
p.add('msToken', await d.msToken());
|
|
39
|
+
}
|
|
40
|
+
/** 上游 with_a_bogus(data):签名输入是 splice_url 后的 query 与 body。 */
|
|
41
|
+
function withABogus(d, p, data, host = 'www.douyin.com') {
|
|
42
|
+
p.add('a_bogus', d.ab.sign(spliceUrl(p.pairs()), data ? spliceUrl(data) : '', host));
|
|
43
|
+
}
|
|
44
|
+
/** 上游 with_platform(auth=auth, url=...):公共组 + webid + uifid + verifyFp/fp。 */
|
|
45
|
+
async function withPlatformAuth(d, p, rtt = '0') {
|
|
46
|
+
p.update(platformParams(rtt));
|
|
47
|
+
await withWebId(d, p);
|
|
48
|
+
withUifid(d, p);
|
|
49
|
+
withVerifyFp(d, p);
|
|
50
|
+
}
|
|
51
|
+
async function getJson(d, url, h, p, timeout) {
|
|
52
|
+
const res = await d.request({ url, headers: h.list(), query: p?.pairs(), timeout });
|
|
53
|
+
return riskJson(res);
|
|
54
|
+
}
|
|
55
|
+
async function getSigned(d, base, h, p) {
|
|
56
|
+
return getJson(d, signedUrl(base, p.raw(), d.cookie('UIFID') ?? ''), h);
|
|
57
|
+
}
|
|
58
|
+
/** x-secsdk-csrf-token:HEAD 一次 abtest_config,取响应头 X-Ware-Csrf-Token 的第 2 段(上游 generate_csrf_token)。 */
|
|
59
|
+
export async function csrfToken(d, cookieStr, origin = WWW, path = '/service/2/abtest_config/', referer) {
|
|
60
|
+
try {
|
|
61
|
+
const res = await d.http.request({
|
|
62
|
+
method: 'HEAD',
|
|
63
|
+
url: origin + path,
|
|
64
|
+
headers: [
|
|
65
|
+
['x-secsdk-csrf-request', '1'],
|
|
66
|
+
['referer', referer ?? origin + '/'],
|
|
67
|
+
['user-agent', PROFILE.ua],
|
|
68
|
+
['x-secsdk-csrf-version', '1.2.22'],
|
|
69
|
+
['accept', '*/*'],
|
|
70
|
+
['accept-language', PROFILE.acceptLanguage],
|
|
71
|
+
],
|
|
72
|
+
cookies: cookieObject(cookieStr),
|
|
73
|
+
});
|
|
74
|
+
const parts = (res.headers.get('x-ware-csrf-token') ?? '').split(',');
|
|
75
|
+
return parts.length > 4 ? parts[1] : null;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function withCsrf(d, h) {
|
|
82
|
+
const token = await csrfToken(d, d.cookieStr);
|
|
83
|
+
if (token != null)
|
|
84
|
+
h.set('x-secsdk-csrf-token', token);
|
|
85
|
+
}
|
|
86
|
+
/** trans_cookies:cookie 串 → 有序对象(空片段跳过)。 */
|
|
87
|
+
export function cookieObject(cookieStr) {
|
|
88
|
+
const out = {};
|
|
89
|
+
for (const item of cookieStr.split(';')) {
|
|
90
|
+
const s = item.trim();
|
|
91
|
+
const i = s.indexOf('=');
|
|
92
|
+
if (i <= 0)
|
|
93
|
+
continue;
|
|
94
|
+
out[s.slice(0, i)] = s.slice(i + 1);
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
// ================================================================ 游客态:ttwid、msToken、设备号
|
|
99
|
+
/** ttwid 注册(上游 DYLoginApi.register_ttwid)。 */
|
|
100
|
+
export async function registerTtwid(d) {
|
|
101
|
+
const body = jsonDumps({
|
|
102
|
+
region: 'cn',
|
|
103
|
+
aid: 1768,
|
|
104
|
+
needFid: false,
|
|
105
|
+
service: 'www.douyin.com',
|
|
106
|
+
migrate_info: { ticket: '', source: 'node' },
|
|
107
|
+
cbUrlProtocol: 'https',
|
|
108
|
+
union: true,
|
|
109
|
+
});
|
|
110
|
+
const res = await d.plain({
|
|
111
|
+
method: 'POST',
|
|
112
|
+
url: 'https://ttwid.bytedance.com/ttwid/union/register/',
|
|
113
|
+
headers: [
|
|
114
|
+
['content-type', 'application/json'],
|
|
115
|
+
['user-agent', PROFILE.ua],
|
|
116
|
+
],
|
|
117
|
+
body,
|
|
118
|
+
timeout: 20,
|
|
119
|
+
});
|
|
120
|
+
for (const line of res.headers.getSetCookie()) {
|
|
121
|
+
const m = /^ttwid=([^;]*)/.exec(line);
|
|
122
|
+
if (m)
|
|
123
|
+
return m[1];
|
|
124
|
+
}
|
|
125
|
+
return '';
|
|
126
|
+
}
|
|
127
|
+
function mssdkHeaders(storageAccess = false) {
|
|
128
|
+
const h = [
|
|
129
|
+
['sec-ch-ua-platform', PROFILE.secChUaPlatform],
|
|
130
|
+
['referer', `${WWW}/`],
|
|
131
|
+
['user-agent', PROFILE.ua],
|
|
132
|
+
['sec-ch-ua', PROFILE.secChUa],
|
|
133
|
+
['content-type', 'text/plain;charset=UTF-8'],
|
|
134
|
+
['sec-ch-ua-mobile', '?0'],
|
|
135
|
+
['accept', '*/*'],
|
|
136
|
+
['accept-language', 'zh-CN,zh;q=0.9'],
|
|
137
|
+
['cache-control', 'no-cache'],
|
|
138
|
+
['origin', WWW],
|
|
139
|
+
['pragma', 'no-cache'],
|
|
140
|
+
['priority', 'u=1, i'],
|
|
141
|
+
['sec-fetch-dest', 'empty'],
|
|
142
|
+
['sec-fetch-mode', 'cors'],
|
|
143
|
+
['sec-fetch-site', 'cross-site'],
|
|
144
|
+
];
|
|
145
|
+
if (storageAccess)
|
|
146
|
+
h.push(['sec-fetch-storage-access', 'active']);
|
|
147
|
+
return h;
|
|
148
|
+
}
|
|
149
|
+
function msTokenOf(res) {
|
|
150
|
+
const token = res.headers.get('x-ms-token') ?? '';
|
|
151
|
+
if (token)
|
|
152
|
+
return token;
|
|
153
|
+
return /msToken=([^;]+)/.exec(res.headers.get('set-cookie') ?? '')?.[1] ?? '';
|
|
154
|
+
}
|
|
155
|
+
/** mssdk /web/r/token 换 msToken;续期时把旧 token 挂在 query 上(上游 utils/mstoken.get_mstoken)。 */
|
|
156
|
+
export async function mstoken(d, previous = '') {
|
|
157
|
+
const body = strDataReport();
|
|
158
|
+
let url = 'https://mssdk.bytedance.com/web/r/token?ms_appid=6383';
|
|
159
|
+
if (previous)
|
|
160
|
+
url += '&msToken=' + quote(previous, '');
|
|
161
|
+
const res = await d.plain({ method: 'POST', url, headers: mssdkHeaders(), body, timeout: 25 });
|
|
162
|
+
return msTokenOf(res);
|
|
163
|
+
}
|
|
164
|
+
/** mssdk /web/common 轮换 msToken(上游 refresh_common_mstoken):完整上报或 5 分钟一次的行为心跳。响应的 cookie 并回会话(cookie_sink)。 */
|
|
165
|
+
export async function commonMstoken(d, current, body) {
|
|
166
|
+
let url = 'https://mssdk.bytedance.com/web/common?ms_appid=6383';
|
|
167
|
+
if (current)
|
|
168
|
+
url += '&msToken=' + quote(current, '');
|
|
169
|
+
const res = await d.plain({ method: 'POST', url, headers: mssdkHeaders(true), body, timeout: 25 });
|
|
170
|
+
for (const line of res.headers.getSetCookie()) {
|
|
171
|
+
const pair = line.split(';', 1)[0];
|
|
172
|
+
const i = pair.indexOf('=');
|
|
173
|
+
if (i > 0 && pair.slice(i + 1).trim())
|
|
174
|
+
d.setCookie(pair.slice(0, i).trim(), pair.slice(i + 1).trim());
|
|
175
|
+
}
|
|
176
|
+
return msTokenOf(res);
|
|
177
|
+
}
|
|
178
|
+
/** 设备号(上游 get_device_id):query/user 的 id。 */
|
|
179
|
+
export async function deviceId(d) {
|
|
180
|
+
const refer = `${WWW}/discover`;
|
|
181
|
+
const h = headerUifid(d, headers('GET').set('referer', refer));
|
|
182
|
+
const p = new Params().update(platformParams()).add('publish_video_strategy_type', '2');
|
|
183
|
+
await withWebId(d, p);
|
|
184
|
+
p.add('msToken', randomMsToken());
|
|
185
|
+
fp(d, p);
|
|
186
|
+
withABogus(d, p);
|
|
187
|
+
const body = await getJson(d, `${WWW}/aweme/v1/web/query/user`, h, p);
|
|
188
|
+
return body.id;
|
|
189
|
+
}
|
|
190
|
+
/** 服务端渲染页面里的 user_unique_id(上游 generate_webid);拿不到返回空串。 */
|
|
191
|
+
export async function pageWebid(d, url) {
|
|
192
|
+
try {
|
|
193
|
+
const h = headers('DOC').set('cookie', d.cookieStr).set('upgrade-insecure-requests', '1');
|
|
194
|
+
const text = await (await d.http.request({ url, headers: h.list() })).text();
|
|
195
|
+
return /\\"user_unique_id\\":\\"(.*?)\\"/.exec(text)?.[1] ?? '';
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return '';
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/** 自己的数字 uid(上游 get_my_uid)。没有 user_uid 或为 0 表示没登录,报 AUTH_*(游客 AUTH_REQUIRED,账号 AUTH_EXPIRED)。 */
|
|
202
|
+
export async function myUid(d) {
|
|
203
|
+
const refer = `${WWW}/`;
|
|
204
|
+
const h = headerUifid(d, headers('GET').set('referer', refer));
|
|
205
|
+
const p = new Params().add('publish_video_strategy_type', '2').update(platformParams());
|
|
206
|
+
withUifid(d, p);
|
|
207
|
+
await withWebId(d, p);
|
|
208
|
+
fp(d, p);
|
|
209
|
+
withABogus(d, p);
|
|
210
|
+
const body = await getJson(d, `${WWW}/aweme/v1/web/query/user/`, h, p);
|
|
211
|
+
const uid = String(body.user_uid ?? '').trim();
|
|
212
|
+
if (!/^\d+$/.test(uid) || /^0+$/.test(uid))
|
|
213
|
+
throw authError(d.ctx, 'query/user 没有返回 user_uid,cookie 无效或已过期');
|
|
214
|
+
return uid;
|
|
215
|
+
}
|
|
216
|
+
/** 自己的 sec_uid(上游 get_my_sec_uid):创作者中心 user/info,主站 HTML 兜底。 */
|
|
217
|
+
export async function mySecUid(d) {
|
|
218
|
+
try {
|
|
219
|
+
const res = await d.request({
|
|
220
|
+
url: 'https://creator.douyin.com/web/api/media/user/info/',
|
|
221
|
+
headers: [
|
|
222
|
+
['accept', 'application/json, text/plain, */*'],
|
|
223
|
+
['accept-language', PROFILE.acceptLanguage],
|
|
224
|
+
['referer', 'https://creator.douyin.com/creator-micro/home'],
|
|
225
|
+
['user-agent', PROFILE.ua],
|
|
226
|
+
],
|
|
227
|
+
timeout: 20,
|
|
228
|
+
});
|
|
229
|
+
const user = (jsonLoads(await res.text()) ?? {}).user ?? {};
|
|
230
|
+
if (user.sec_uid)
|
|
231
|
+
return { secUid: user.sec_uid, user };
|
|
232
|
+
}
|
|
233
|
+
catch { }
|
|
234
|
+
const res = await d.request({ url: `${WWW}/user/self`, headers: headers('GET').list(), query: [['from_tab_name', 'main']] });
|
|
235
|
+
const found = /\\"secUid\\":\\"(.*?)\\"/.exec(await res.text())?.[1];
|
|
236
|
+
if (!found)
|
|
237
|
+
throw new Error('未取到 sec_uid:创作者接口与主站 HTML 都没拿到,请检查登录态');
|
|
238
|
+
return { secUid: found, user: null };
|
|
239
|
+
}
|
|
240
|
+
// ================================================================ 作品、用户、评论、搜索
|
|
241
|
+
export async function userWorks(d, secUid, maxCursor = '0', own = false) {
|
|
242
|
+
const userUrl = `${WWW}/user/${secUid}`;
|
|
243
|
+
const h = headerUifid(d, headers('GET').referer(userUrl));
|
|
244
|
+
const p = new Params();
|
|
245
|
+
p.add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web');
|
|
246
|
+
p.add('sec_user_id', secUid).add('max_cursor', maxCursor).add('locate_query', 'false').add('show_live_replay_strategy', '1');
|
|
247
|
+
p.add('need_time_list', maxCursor === '0' ? '1' : '0').add('time_list_query', '0').add('whale_cut_token', '').add('cut_version', '1');
|
|
248
|
+
p.add('count', '18').add('publish_video_strategy_type', '2').add('from_user_page', own ? '0' : '1');
|
|
249
|
+
p.add('update_version_code', '170400').add('pc_client_type', '1').add('pc_libra_divert', 'Windows').add('support_h265', '1').add('support_dash', '1');
|
|
250
|
+
p.add('cpu_core_num', PROFILE.cpuCoreNum).add('version_code', '290100').add('version_name', '29.1.0').add('cookie_enabled', 'true');
|
|
251
|
+
p.add('screen_width', PROFILE.screenWidth).add('screen_height', PROFILE.screenHeight).add('browser_language', 'zh-CN').add('browser_platform', 'Win32');
|
|
252
|
+
p.add('browser_name', PROFILE.browserName).add('browser_version', PROFILE.browserVersion).add('browser_online', 'true').add('engine_name', 'Blink');
|
|
253
|
+
p.add('engine_version', PROFILE.engineVersion).add('os_name', 'Windows').add('os_version', '10').add('device_memory', PROFILE.deviceMemory);
|
|
254
|
+
p.add('platform', 'PC').add('downlink', '10').add('effective_type', '4g').add('round_trip_time', '0');
|
|
255
|
+
await withWebId(d, p);
|
|
256
|
+
withUifid(d, p);
|
|
257
|
+
await withMsToken(d, p);
|
|
258
|
+
withABogus(d, p);
|
|
259
|
+
withVerifyFp(d, p);
|
|
260
|
+
return getSigned(d, `${WWW}/aweme/v1/web/aweme/post/`, h, p);
|
|
261
|
+
}
|
|
262
|
+
export async function workInfo(d, awemeId) {
|
|
263
|
+
const h = headerUifid(d, headers('GET').referer(`${WWW}/video/${awemeId}`));
|
|
264
|
+
const p = new Params();
|
|
265
|
+
p.add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web').add('aweme_id', awemeId);
|
|
266
|
+
p.add('request_source', '600').add('origin_type', 'video_page');
|
|
267
|
+
p.update(platformParams('50', '190500', '19.5.0'));
|
|
268
|
+
await withWebId(d, p);
|
|
269
|
+
withUifid(d, p);
|
|
270
|
+
withVerifyFp(d, p);
|
|
271
|
+
await withMsToken(d, p);
|
|
272
|
+
withABogus(d, p);
|
|
273
|
+
return getSigned(d, `${WWW}/aweme/v1/web/aweme/detail/`, h, p);
|
|
274
|
+
}
|
|
275
|
+
export async function comments(d, awemeId, cursor = '0') {
|
|
276
|
+
const h = headerUifid(d, headers('GET').referer(`${WWW}/video/${awemeId}`));
|
|
277
|
+
d.withBdReadonly(h);
|
|
278
|
+
const p = new Params();
|
|
279
|
+
p.add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web').add('aweme_id', awemeId).add('cursor', cursor);
|
|
280
|
+
p.add('count', '5').add('item_type', '0').add('whale_cut_token', '').add('cut_version', '1').add('rcFT', '');
|
|
281
|
+
p.update(platformParams('0'));
|
|
282
|
+
await withWebId(d, p);
|
|
283
|
+
withUifid(d, p);
|
|
284
|
+
withVerifyFp(d, p);
|
|
285
|
+
await withMsToken(d, p);
|
|
286
|
+
withABogus(d, p);
|
|
287
|
+
return getJson(d, `${WWW}/aweme/v1/web/comment/list/`, h, p);
|
|
288
|
+
}
|
|
289
|
+
export async function replies(d, awemeId, commentId, cursor = '0', count = '3') {
|
|
290
|
+
const h = headers('GET').referer(`${WWW}/video/${awemeId}`);
|
|
291
|
+
const p = new Params();
|
|
292
|
+
p.add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web').add('item_id', awemeId).add('comment_id', commentId);
|
|
293
|
+
p.add('cut_version', '1').add('cursor', cursor).add('count', count).add('item_type', '0');
|
|
294
|
+
p.update(platformParams('0'));
|
|
295
|
+
await withWebId(d, p);
|
|
296
|
+
await withMsToken(d, p);
|
|
297
|
+
withABogus(d, p);
|
|
298
|
+
fp(d, p);
|
|
299
|
+
return getJson(d, `${WWW}/aweme/v1/web/comment/list/reply/`, h, p);
|
|
300
|
+
}
|
|
301
|
+
export async function userInfo(d, secUid) {
|
|
302
|
+
const h = headerUifid(d, headers('GET').referer(`${WWW}/user/${secUid}`));
|
|
303
|
+
const p = new Params();
|
|
304
|
+
p.add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web').add('publish_video_strategy_type', '2');
|
|
305
|
+
p.add('source', 'channel_pc_web').add('sec_user_id', secUid).add('personal_center_strategy', '1').add('profile_other_record_enable', '1').add('land_to', '1');
|
|
306
|
+
p.update(platformParams('0'));
|
|
307
|
+
await withWebId(d, p);
|
|
308
|
+
withUifid(d, p);
|
|
309
|
+
await withMsToken(d, p);
|
|
310
|
+
withABogus(d, p);
|
|
311
|
+
withVerifyFp(d, p);
|
|
312
|
+
return getJson(d, `${WWW}/aweme/v1/web/user/profile/other/`, h, p);
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* 综合搜索(上游 search_general_work)。后端不会从 is_filter_search 反推筛选条件,五个筛选值作为 JSON 一起放进
|
|
316
|
+
* filter_selected(紧凑、不转义非 ASCII,键序照上游);没有筛选时也发,值为默认值。
|
|
317
|
+
*/
|
|
318
|
+
export async function searchGeneral(d, keyword, offset = '0', searchId = '', f = {}) {
|
|
319
|
+
const sortType = f.sortType ?? '0';
|
|
320
|
+
const publishTime = f.publishTime ?? '0';
|
|
321
|
+
const filterDuration = f.filterDuration ?? '';
|
|
322
|
+
const searchRange = f.searchRange ?? '';
|
|
323
|
+
const contentType = f.contentType ?? '';
|
|
324
|
+
const filtered = sortType !== '0' || publishTime !== '0' || Boolean(filterDuration || searchRange || contentType);
|
|
325
|
+
const filterSelected = compactJson({
|
|
326
|
+
sort_type: sortType,
|
|
327
|
+
publish_time: publishTime,
|
|
328
|
+
filter_duration: filterDuration,
|
|
329
|
+
search_range: searchRange,
|
|
330
|
+
content_type: contentType,
|
|
331
|
+
});
|
|
332
|
+
const refer = `${WWW}/search/${quote(keyword)}?aid=${rand.uuid4()}&type=general`;
|
|
333
|
+
const h = headerUifid(d, headers('GET').referer(refer));
|
|
334
|
+
const p = new Params();
|
|
335
|
+
p.add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web').add('search_channel', 'aweme_general');
|
|
336
|
+
p.add('enable_history', '1').add('filter_selected', filterSelected).add('keyword', keyword).add('search_source', 'tab_search').add('query_correct_type', '1');
|
|
337
|
+
p.add('is_filter_search', filtered ? '1' : '0').add('from_group_id', '').add('disable_rs', '0').add('offset', offset).add('count', '15');
|
|
338
|
+
p.add('need_filter_settings', offset === '0' ? '1' : '0').add('list_type', 'single').add('pc_search_top_1_params', '{"enable_ai_search_top_1":1}');
|
|
339
|
+
p.add('search_id', searchId);
|
|
340
|
+
p.update(platformParams('0', '190600', '19.6.0'));
|
|
341
|
+
await withWebId(d, p);
|
|
342
|
+
withUifid(d, p);
|
|
343
|
+
await withMsToken(d, p);
|
|
344
|
+
withABogus(d, p);
|
|
345
|
+
withVerifyFp(d, p);
|
|
346
|
+
return getJson(d, `${WWW}/aweme/v1/web/general/search/single/`, h, p);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* 视频频道搜索(上游 search_video_work):筛选值都进 query。翻页时带上一页响应头的 X-Tt-Logid 作为 search_id。
|
|
350
|
+
* 返回 [下一页的 search_id, 原始 JSON]。
|
|
351
|
+
*/
|
|
352
|
+
export async function searchVideo(d, keyword, offset = '0', count = '16', f = {}, searchId = '') {
|
|
353
|
+
const refer = `${WWW}/search/${quote(keyword)}?aid=${rand.uuid4()}&type=video`;
|
|
354
|
+
const h = headers('GET').referer(refer);
|
|
355
|
+
const p = new Params();
|
|
356
|
+
p.add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web').add('search_channel', 'aweme_video_web');
|
|
357
|
+
p.add('enable_history', '1').add('sort_type', f.sortType ?? '0').add('publish_time', f.publishTime ?? '0');
|
|
358
|
+
p.add('filter_duration', f.filterDuration ?? '').add('search_range', f.searchRange ?? '0');
|
|
359
|
+
p.add('keyword', keyword).add('search_source', 'normal_search').add('query_correct_type', '1').add('is_filter_search', '1');
|
|
360
|
+
p.add('from_group_id', '').add('offset', offset).add('count', count).add('need_filter_settings', offset === '0' ? '1' : '0');
|
|
361
|
+
if (searchId)
|
|
362
|
+
p.add('search_id', searchId);
|
|
363
|
+
p.add('list_type', 'single').add('pc_search_top_1_params', '');
|
|
364
|
+
p.update(platformParams('50'));
|
|
365
|
+
await withWebId(d, p);
|
|
366
|
+
await withMsToken(d, p);
|
|
367
|
+
withABogus(d, p);
|
|
368
|
+
fp(d, p);
|
|
369
|
+
const res = await d.request({ url: `${WWW}/aweme/v1/web/search/item/`, headers: h.list(), query: p.pairs() });
|
|
370
|
+
const next = res.headers.get('x-tt-logid') ?? '';
|
|
371
|
+
return [next, await riskJson(res)];
|
|
372
|
+
}
|
|
373
|
+
/** 用户搜索(上游 search_user)。fans:0_1k / 1k_1w / 1w_10w / 10w_100w / 100w_;userType:common_user / enterprise_user / personal_user。 */
|
|
374
|
+
export async function searchUser(d, keyword, offset = '0', count = '25', fans = '', userType = '') {
|
|
375
|
+
const refer = `${WWW}/search/${quote(keyword)}?type=user`;
|
|
376
|
+
const h = headerUifid(d, headers('GET').referer(refer));
|
|
377
|
+
const hasFilter = Boolean(fans || userType);
|
|
378
|
+
const p = new Params();
|
|
379
|
+
p.add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web').add('search_channel', 'aweme_user_web');
|
|
380
|
+
if (hasFilter)
|
|
381
|
+
p.add('search_filter_value', `{"douyin_user_fans":["${fans}"],"douyin_user_type":["${userType}"]}`);
|
|
382
|
+
p.add('keyword', keyword).add('search_source', 'normal_search').add('query_correct_type', '1').add('is_filter_search', hasFilter ? '1' : '0');
|
|
383
|
+
p.add('from_group_id', '').add('disable_rs', '0').add('offset', offset).add('count', count).add('need_filter_settings', offset === '0' ? '1' : '0');
|
|
384
|
+
p.add('list_type', 'single').add('pc_search_top_1_params', '{"enable_ai_search_top_1":1}');
|
|
385
|
+
p.update(platformParams('50').slice(3));
|
|
386
|
+
await withWebId(d, p);
|
|
387
|
+
withUifid(d, p);
|
|
388
|
+
await withMsToken(d, p);
|
|
389
|
+
withABogus(d, p);
|
|
390
|
+
fp(d, p);
|
|
391
|
+
return getJson(d, `${WWW}/aweme/v1/web/discover/search/`, h, p);
|
|
392
|
+
}
|
|
393
|
+
export async function searchLive(d, keyword, offset = '0', count = '15') {
|
|
394
|
+
const refer = `${WWW}/search/${quote(keyword)}?aid=${rand.uuid4()}&type=live`;
|
|
395
|
+
const h = headerUifid(d, headers('GET').referer(refer));
|
|
396
|
+
const p = new Params();
|
|
397
|
+
p.add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web').add('search_channel', 'aweme_live');
|
|
398
|
+
p.add('keyword', keyword).add('search_source', 'normal_search').add('query_correct_type', '1').add('is_filter_search', '0');
|
|
399
|
+
p.add('from_group_id', '').add('disable_rs', '0').add('offset', offset).add('count', count).add('need_filter_settings', offset === '0' ? '1' : '0');
|
|
400
|
+
p.add('list_type', 'single').add('pc_search_top_1_params', '{"enable_ai_search_top_1":1}');
|
|
401
|
+
p.update(platformParams('0'));
|
|
402
|
+
await withWebId(d, p);
|
|
403
|
+
withUifid(d, p);
|
|
404
|
+
await withMsToken(d, p);
|
|
405
|
+
withABogus(d, p);
|
|
406
|
+
withVerifyFp(d, p);
|
|
407
|
+
return getJson(d, `${WWW}/aweme/v1/web/live/search/`, h, p);
|
|
408
|
+
}
|
|
409
|
+
/** 用户喜欢的作品(上游 get_user_favorite,接口是 aweme/favorite)。 */
|
|
410
|
+
export async function userFavorite(d, secUid, maxCursor = '0', count = '18') {
|
|
411
|
+
const refer = `${WWW}/user/${secUid}?showTab=like`;
|
|
412
|
+
const h = headerUifid(d, headers('GET').referer(refer));
|
|
413
|
+
d.withBdReadonly(h);
|
|
414
|
+
const p = new Params();
|
|
415
|
+
p.add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web').add('sec_user_id', secUid).add('max_cursor', maxCursor);
|
|
416
|
+
p.add('min_cursor', '0').add('whale_cut_token', '').add('cut_version', '1').add('count', count).add('publish_video_strategy_type', '2');
|
|
417
|
+
p.update(platformParams('0'));
|
|
418
|
+
await withWebId(d, p);
|
|
419
|
+
withUifid(d, p);
|
|
420
|
+
withVerifyFp(d, p);
|
|
421
|
+
withABogus(d, p);
|
|
422
|
+
return getSigned(d, `${WWW}/aweme/v1/web/aweme/favorite/`, h, p);
|
|
423
|
+
}
|
|
424
|
+
/** 收藏夹列表(上游 get_collect_list 只取 cursor=0 的第一页;翻页时换成上一页响应的 cursor)。 */
|
|
425
|
+
export async function collectList(d, cursor = '0', count = '20') {
|
|
426
|
+
const h = headerUifid(d, headers('GET')).referer(`${WWW}/?recommend=1`);
|
|
427
|
+
const p = new Params();
|
|
428
|
+
p.add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web').add('cursor', cursor).add('count', count);
|
|
429
|
+
p.update(platformParams('0'));
|
|
430
|
+
await withWebId(d, p);
|
|
431
|
+
withUifid(d, p);
|
|
432
|
+
withVerifyFp(d, p);
|
|
433
|
+
await withMsToken(d, p);
|
|
434
|
+
withABogus(d, p);
|
|
435
|
+
return getSigned(d, `${WWW}/aweme/v1/web/collects/list/`, h, p);
|
|
436
|
+
}
|
|
437
|
+
export async function followers(d, userId, secUid, maxTime = '', count = '20') {
|
|
438
|
+
if (!maxTime || maxTime === '0')
|
|
439
|
+
maxTime = String(rand.nowSeconds());
|
|
440
|
+
const h = headerUifid(d, headers('GET')).referer(`${WWW}/user/${secUid}`);
|
|
441
|
+
const p = new Params();
|
|
442
|
+
p.add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web').add('user_id', userId).add('sec_user_id', secUid);
|
|
443
|
+
p.add('offset', '0').add('min_time', '0').add('max_time', maxTime).add('count', count).add('source_type', maxTime === '0' ? '2' : '1');
|
|
444
|
+
p.add('gps_access', '0').add('address_book_access', '0');
|
|
445
|
+
p.update(platformParams('0'));
|
|
446
|
+
await withWebId(d, p);
|
|
447
|
+
withUifid(d, p);
|
|
448
|
+
withVerifyFp(d, p);
|
|
449
|
+
await withMsToken(d, p);
|
|
450
|
+
withABogus(d, p);
|
|
451
|
+
return getJson(d, `${WWW}/aweme/v1/web/user/follower/list/`, h, p);
|
|
452
|
+
}
|
|
453
|
+
export async function following(d, userId, secUid, maxTime = '0', count = '20') {
|
|
454
|
+
const h = headers('GET').referer(`${WWW}/user/${secUid}`);
|
|
455
|
+
d.withBdReadonly(h);
|
|
456
|
+
const p = new Params();
|
|
457
|
+
p.add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web').add('user_id', userId).add('sec_user_id', secUid);
|
|
458
|
+
p.add('offset', '0').add('min_time', '0').add('max_time', maxTime).add('count', count).add('source_type', maxTime === '0' ? '2' : '1');
|
|
459
|
+
p.add('gps_access', '0').add('address_book_access', '0').add('is_top', '1').add('pc_client_type', '1').add('pc_libra_divert', 'Windows');
|
|
460
|
+
p.add('support_h265', '1').add('support_dash', '1').add('webcast_sdk_version', '170400').add('webcast_version_code', '170400');
|
|
461
|
+
p.add('version_code', '170400').add('version_name', '17.4.0').add('cookie_enabled', 'true').add('screen_width', PROFILE.screenWidth);
|
|
462
|
+
p.add('screen_height', PROFILE.screenHeight).add('browser_language', 'zh-CN').add('browser_platform', 'Win32').add('browser_name', PROFILE.browserName);
|
|
463
|
+
p.add('browser_version', PROFILE.browserVersion).add('browser_online', 'true').add('engine_name', 'Blink').add('engine_version', PROFILE.engineVersion);
|
|
464
|
+
p.add('os_name', 'Windows').add('os_version', '10').add('cpu_core_num', PROFILE.cpuCoreNum).add('device_memory', PROFILE.deviceMemory);
|
|
465
|
+
p.add('platform', 'PC').add('downlink', '10').add('effective_type', '4g').add('round_trip_time', '0');
|
|
466
|
+
await withWebId(d, p);
|
|
467
|
+
withVerifyFp(d, p);
|
|
468
|
+
await withMsToken(d, p);
|
|
469
|
+
withABogus(d, p);
|
|
470
|
+
return getSigned(d, `${WWW}/aweme/v1/web/user/following/list/`, h, p);
|
|
471
|
+
}
|
|
472
|
+
export async function notices(d, minTime = '0', maxTime = '0', count = '10', group = '960') {
|
|
473
|
+
const h = headerUifid(d, headers('GET')).referer(`${WWW}/?recommend=1`);
|
|
474
|
+
const p = new Params();
|
|
475
|
+
p.add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web').add('is_new_notice', '1').add('is_mark_read', '1');
|
|
476
|
+
p.add('notice_group', group).add('count', count).add('min_time', minTime).add('max_time', maxTime);
|
|
477
|
+
p.update(platformParams('0'));
|
|
478
|
+
await withWebId(d, p);
|
|
479
|
+
withUifid(d, p);
|
|
480
|
+
await withMsToken(d, p);
|
|
481
|
+
withABogus(d, p);
|
|
482
|
+
fp(d, p);
|
|
483
|
+
const body = await getJson(d, `${WWW}/aweme/v1/web/notice/`, h, p);
|
|
484
|
+
if (!body.notice_list?.length && body.notice_list_v2)
|
|
485
|
+
body.notice_list = body.notice_list_v2;
|
|
486
|
+
return body;
|
|
487
|
+
}
|
|
488
|
+
export async function feed(d, count = '20', refreshIndex = '2') {
|
|
489
|
+
const h = headers('GET').referer(`${WWW}/`);
|
|
490
|
+
const p = new Params();
|
|
491
|
+
p.add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web').add('module_id', '3003101').add('count', count);
|
|
492
|
+
p.add('filterGids', '').add('presented_ids', '').add('refresh_index', refreshIndex).add('refer_id', '').add('refer_type', '10');
|
|
493
|
+
p.add('awemePcRecRawData', '{"is_client":false}').add('Seo-Flag', '0').add('install_time', '1715480185').add('pc_client_type', '1');
|
|
494
|
+
p.add('update_version_code', '170400').add('version_code', '170400').add('version_name', '17.4.0').add('cookie_enabled', 'true');
|
|
495
|
+
p.add('screen_width', PROFILE.screenWidth).add('screen_height', PROFILE.screenHeight).add('browser_language', 'zh-CN').add('browser_platform', 'Win32');
|
|
496
|
+
p.add('browser_name', PROFILE.browserName).add('browser_version', PROFILE.browserVersion).add('browser_online', 'true').add('engine_name', 'Blink');
|
|
497
|
+
p.add('engine_version', PROFILE.engineVersion).add('os_name', 'Windows').add('os_version', '10').add('cpu_core_num', PROFILE.cpuCoreNum);
|
|
498
|
+
p.add('device_memory', PROFILE.deviceMemory).add('platform', 'PC').add('downlink', '10').add('effective_type', '4g').add('round_trip_time', '100');
|
|
499
|
+
await withWebId(d, p);
|
|
500
|
+
await withMsToken(d, p);
|
|
501
|
+
withABogus(d, p);
|
|
502
|
+
fp(d, p);
|
|
503
|
+
return getJson(d, `${WWW}/aweme/v1/web/module/feed/`, h, p);
|
|
504
|
+
}
|
|
505
|
+
// ================================================================ 写操作:点赞、收藏、评论
|
|
506
|
+
export async function digg(d, awemeId, type) {
|
|
507
|
+
const api = '/aweme/v1/web/commit/item/digg/';
|
|
508
|
+
const refer = `${WWW}/discover?modal_id=${awemeId}`;
|
|
509
|
+
const h = headers('FORM');
|
|
510
|
+
d.withBdReadonly(h);
|
|
511
|
+
// 上游 digg 仍是「算得出就带」(成品头也行),没有像收藏 / 收藏夹移动那样改用 with_session_dtrait
|
|
512
|
+
const dt = d.dtraitHeader(api);
|
|
513
|
+
if (dt)
|
|
514
|
+
h.set('x-tt-session-dtrait', dt);
|
|
515
|
+
await withCsrf(d, h);
|
|
516
|
+
headerUifid(d, h).set('origin', WWW).set('referer', refer);
|
|
517
|
+
const p = new Params().update(platformParams('0'));
|
|
518
|
+
await withWebId(d, p);
|
|
519
|
+
withUifid(d, p);
|
|
520
|
+
withVerifyFp(d, p);
|
|
521
|
+
await withMsToken(d, p);
|
|
522
|
+
withABogus(d, p);
|
|
523
|
+
p.add('uid', await d.commentUid());
|
|
524
|
+
const res = await d.request({
|
|
525
|
+
method: 'POST',
|
|
526
|
+
url: WWW + api,
|
|
527
|
+
headers: h.list(),
|
|
528
|
+
query: p.pairs(),
|
|
529
|
+
form: [
|
|
530
|
+
['aweme_id', awemeId],
|
|
531
|
+
['item_type', '0'],
|
|
532
|
+
['type', type],
|
|
533
|
+
],
|
|
534
|
+
});
|
|
535
|
+
return riskJson(res);
|
|
536
|
+
}
|
|
537
|
+
export async function collect(d, awemeId, action) {
|
|
538
|
+
const api = '/aweme/v1/web/aweme/collect/';
|
|
539
|
+
const h = headers('FORM').referer(`${WWW}/?recommend=1`);
|
|
540
|
+
d.withBdReadonly(h);
|
|
541
|
+
// 收藏是写接口,与收藏夹移动一样要带按 path 重算的 dtrait
|
|
542
|
+
d.withSessionDtrait(h, api);
|
|
543
|
+
await withCsrf(d, h);
|
|
544
|
+
headerUifid(d, h).set('origin', WWW);
|
|
545
|
+
const p = new Params().add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web').add('pc_client_type', '1');
|
|
546
|
+
p.update(platformParams('0'));
|
|
547
|
+
await withWebId(d, p);
|
|
548
|
+
withUifid(d, p);
|
|
549
|
+
withVerifyFp(d, p);
|
|
550
|
+
await withMsToken(d, p);
|
|
551
|
+
const data = [
|
|
552
|
+
['action', action],
|
|
553
|
+
['aweme_id', awemeId],
|
|
554
|
+
['aweme_type', '0'],
|
|
555
|
+
];
|
|
556
|
+
withABogus(d, p, data);
|
|
557
|
+
p.add('uid', await d.commentUid());
|
|
558
|
+
const res = await d.request({ method: 'POST', url: WWW + api, headers: h.list(), query: p.pairs(), form: data });
|
|
559
|
+
return riskJson(res);
|
|
560
|
+
}
|
|
561
|
+
/** 收藏夹移动的 query(上游 move / remove_collect_aweme 手写的那组,键按字母序)。 */
|
|
562
|
+
async function collectsMoveParams(d, awemeId, collectName, collectId, remove) {
|
|
563
|
+
const p = new Params();
|
|
564
|
+
p.add('aid', '6383').add('browser_language', 'zh-CN').add('browser_name', PROFILE.browserName).add('browser_online', 'true');
|
|
565
|
+
p.add('browser_platform', PROFILE.platform).add('browser_version', PROFILE.browserVersion).add('channel', 'channel_pc_web').add('collects_name', collectName);
|
|
566
|
+
p.add('cookie_enabled', 'true').add('cpu_core_num', PROFILE.cpuCoreNum).add('device_memory', PROFILE.deviceMemory).add('device_platform', 'webapp');
|
|
567
|
+
p.add('downlink', '10').add('effective_type', '4g').add('engine_name', 'Blink').add('engine_version', PROFILE.engineVersion);
|
|
568
|
+
if (remove)
|
|
569
|
+
p.add('from_collects_id', collectId);
|
|
570
|
+
p.add('item_ids', awemeId).add('item_type', '2');
|
|
571
|
+
if (!remove)
|
|
572
|
+
p.add('move_collects_list', collectId);
|
|
573
|
+
p.add('os_name', 'Windows').add('os_version', PROFILE.osVersion).add('pc_client_type', '1').add('platform', 'PC').add('round_trip_time', '50');
|
|
574
|
+
p.add('screen_height', PROFILE.screenHeight).add('screen_width', PROFILE.screenWidth);
|
|
575
|
+
if (!remove)
|
|
576
|
+
p.add('to_collects_id', collectId).add('update_collects_sort', 'true');
|
|
577
|
+
p.add('update_version_code', '170400').add('version_code', '170400').add('version_name', '17.4.0');
|
|
578
|
+
await withWebId(d, p);
|
|
579
|
+
fp(d, p);
|
|
580
|
+
await withMsToken(d, p);
|
|
581
|
+
withABogus(d, p);
|
|
582
|
+
return p;
|
|
583
|
+
}
|
|
584
|
+
/** 把已收藏的作品移进某个收藏夹(上游 move_collect_aweme)。 */
|
|
585
|
+
export async function collectMove(d, awemeId, collectName, collectId) {
|
|
586
|
+
const api = '/aweme/v1/web/collects/video/move/';
|
|
587
|
+
const h = headers('FORM').referer(`${WWW}/?recommend=1`);
|
|
588
|
+
d.withBdReadonly(h);
|
|
589
|
+
d.withSessionDtrait(h, api);
|
|
590
|
+
await withCsrf(d, h);
|
|
591
|
+
headerUifid(d, h).set('origin', WWW);
|
|
592
|
+
const p = await collectsMoveParams(d, awemeId, collectName, collectId, false);
|
|
593
|
+
return riskJson(await d.request({ method: 'POST', url: WWW + api, headers: h.list(), query: p.pairs() }));
|
|
594
|
+
}
|
|
595
|
+
/** 把作品从某个收藏夹移出(上游 remove_collect_aweme,同一个接口)。 */
|
|
596
|
+
export async function collectRemove(d, awemeId, collectName, collectId) {
|
|
597
|
+
const api = '/aweme/v1/web/collects/video/move/';
|
|
598
|
+
const h = headers('FORM').referer(`${WWW}/user/self?showTab=favorite_collection`);
|
|
599
|
+
d.withBdReadonly(h);
|
|
600
|
+
d.withSessionDtrait(h, api);
|
|
601
|
+
await withCsrf(d, h);
|
|
602
|
+
headerUifid(d, h).set('origin', WWW);
|
|
603
|
+
const p = await collectsMoveParams(d, awemeId, collectName, collectId, true);
|
|
604
|
+
return riskJson(await d.request({ method: 'POST', url: WWW + api, headers: h.list(), query: p.pairs() }));
|
|
605
|
+
}
|
|
606
|
+
export async function publishComment(d, awemeId, text, replyId = '', replyToReplyId = '') {
|
|
607
|
+
const api = '/aweme/v1/web/comment/publish';
|
|
608
|
+
const refer = `${WWW}/video/${awemeId}`;
|
|
609
|
+
const h = headers('FORM').set('origin', WWW).referer(refer);
|
|
610
|
+
await d.withBd(h, api, { requireDtrait: true });
|
|
611
|
+
await withCsrf(d, h);
|
|
612
|
+
headerUifid(d, h);
|
|
613
|
+
const p = new Params();
|
|
614
|
+
p.add('app_name', 'aweme').add('enter_from', 'video_detail').add('previous_page', 'video_detail').add('device_platform', 'webapp').add('aid', '6383');
|
|
615
|
+
p.add('channel', 'channel_pc_web').add('pc_client_type', '1').add('pc_libra_divert', 'Windows').add('update_version_code', '170400');
|
|
616
|
+
p.add('support_h265', '1').add('support_dash', '1').add('version_code', '170400').add('version_name', '17.4.0').add('cookie_enabled', 'true');
|
|
617
|
+
p.add('screen_width', PROFILE.screenWidth).add('screen_height', PROFILE.screenHeight).add('browser_language', 'zh-CN').add('browser_platform', 'Win32');
|
|
618
|
+
p.add('browser_name', PROFILE.browserName).add('browser_version', PROFILE.browserVersion).add('browser_online', 'true').add('engine_name', 'Blink');
|
|
619
|
+
p.add('engine_version', PROFILE.engineVersion).add('os_name', 'Windows').add('os_version', '10').add('cpu_core_num', PROFILE.cpuCoreNum);
|
|
620
|
+
p.add('device_memory', PROFILE.deviceMemory).add('platform', 'PC').add('downlink', '10').add('effective_type', '4g').add('round_trip_time', '50');
|
|
621
|
+
await withWebId(d, p);
|
|
622
|
+
withUifid(d, p);
|
|
623
|
+
fp(d, p);
|
|
624
|
+
await withMsToken(d, p);
|
|
625
|
+
const data = [['aweme_id', awemeId]];
|
|
626
|
+
if (replyId)
|
|
627
|
+
data.push(['reply_id', replyId]);
|
|
628
|
+
if (replyToReplyId)
|
|
629
|
+
data.push(['reply_to_reply_id', replyToReplyId]);
|
|
630
|
+
data.push(['comment_send_celltime', '0'], ['comment_video_celltime', '0'], ['one_level_comment_rank', '-1'], ['paste_edit_method', 'non_paste'], ['text', text], ['text_extra', '[]']);
|
|
631
|
+
withABogus(d, p, data);
|
|
632
|
+
const uid = await d.commentUid();
|
|
633
|
+
if (uid)
|
|
634
|
+
p.add('uid', uid);
|
|
635
|
+
const res = await d.request({ method: 'POST', url: WWW + api, headers: h.list(), query: p.pairs(), form: data });
|
|
636
|
+
return riskJson(res);
|
|
637
|
+
}
|
|
638
|
+
// ================================================================ 商品
|
|
639
|
+
function liveEcomHeaders(url) {
|
|
640
|
+
return headers('GET').referer(url).remove('sec-ch-ua', 'sec-ch-ua-mobile', 'sec-ch-ua-platform');
|
|
641
|
+
}
|
|
642
|
+
/** 直播间正在讲解的商品(上游 get_live_production,接口 /live/promotions/pop/v3/)。 */
|
|
643
|
+
export async function liveProduction(d, url, roomId, authorId, sceneId = '1001') {
|
|
644
|
+
const h = headerUifid(d, liveEcomHeaders(url));
|
|
645
|
+
const entrance = JSON.stringify({ room_id: roomId, anchor_id: authorId, carrier_type: 'live_popup_card', ecom_scene_id: sceneId });
|
|
646
|
+
const p = new Params();
|
|
647
|
+
p.add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web').add('room_id', roomId).add('author_id', authorId);
|
|
648
|
+
p.add('live_scene_id', '0').add('entrance_info', quote(entrance, ''));
|
|
649
|
+
p.update(livePlatformParams());
|
|
650
|
+
await withWebId(d, p);
|
|
651
|
+
withUifid(d, p);
|
|
652
|
+
await withMsToken(d, p);
|
|
653
|
+
withABogus(d, p, undefined, LIVE_HOST);
|
|
654
|
+
const res = await d.request({ url: `${LIVE}/live/promotions/pop/v3/`, headers: h.list(), query: p.pairs() });
|
|
655
|
+
const buf = await res.clone().arrayBuffer();
|
|
656
|
+
if (!buf.byteLength)
|
|
657
|
+
return { promotions: [] };
|
|
658
|
+
return riskJson(res);
|
|
659
|
+
}
|
|
660
|
+
/** 商品详情(上游 get_live_production_detail,接口 /aweme/v2/shop/promotion/pack/detail/)。 */
|
|
661
|
+
export async function productDetail(d, url, promotionId, originType = '638303') {
|
|
662
|
+
const h = headers('FORM').set('origin', LIVE).referer(url);
|
|
663
|
+
await withCsrf(d, h);
|
|
664
|
+
headerUifid(d, h).remove('sec-ch-ua', 'sec-ch-ua-mobile', 'sec-ch-ua-platform');
|
|
665
|
+
const p = new Params().add('is_h5', '1').add('origin_type', originType).add('device_platform', 'webapp').add('aid', '6383').add('channel', 'channel_pc_web');
|
|
666
|
+
p.update(livePlatformParams());
|
|
667
|
+
await withWebId(d, p);
|
|
668
|
+
withUifid(d, p);
|
|
669
|
+
await withMsToken(d, p);
|
|
670
|
+
const data = [
|
|
671
|
+
['is_h5', '1'],
|
|
672
|
+
['bff_type', '2'],
|
|
673
|
+
['origin_type', originType],
|
|
674
|
+
['promotion_id', promotionId],
|
|
675
|
+
];
|
|
676
|
+
withABogus(d, p, data, LIVE_HOST);
|
|
677
|
+
const res = await d.request({ method: 'POST', url: `${LIVE}/aweme/v2/shop/promotion/pack/detail/`, headers: h.list(), query: p.pairs(), form: data });
|
|
678
|
+
return riskJson(res);
|
|
679
|
+
}
|
|
680
|
+
/** 商品评价(上游 get_product_comments)。 */
|
|
681
|
+
export async function productComments(d, productId, shopId, cursor = '0', count = '10', sortType = '0', tagId = '', statId = '') {
|
|
682
|
+
const h = headerUifid(d, headers('GET').referer(`${WWW}/`));
|
|
683
|
+
const p = new Params().add('product_id', productId).add('shop_id', shopId).add('cursor', cursor).add('count', count);
|
|
684
|
+
p.add('stat_id', statId).add('tag_id', tagId).add('sort_type', sortType);
|
|
685
|
+
await withPlatformAuth(d, p);
|
|
686
|
+
await withMsToken(d, p);
|
|
687
|
+
withABogus(d, p);
|
|
688
|
+
return getJson(d, `${WWW}/aweme/v1/web/ecom/product/comments/`, h, p);
|
|
689
|
+
}
|
|
690
|
+
/** 商品评价的分类计数:好评 / 差评 / 有图等标签(上游 get_product_comment_counter)。 */
|
|
691
|
+
export async function productCommentCounter(d, productId, shopId, statId = '') {
|
|
692
|
+
const h = headerUifid(d, headers('GET').referer(`${WWW}/`));
|
|
693
|
+
const p = new Params().add('product_id', productId).add('shop_id', shopId).add('stat_id', statId);
|
|
694
|
+
await withPlatformAuth(d, p);
|
|
695
|
+
await withMsToken(d, p);
|
|
696
|
+
withABogus(d, p);
|
|
697
|
+
return getJson(d, `${WWW}/aweme/v1/web/ecom/product/comment/counter/`, h, p);
|
|
698
|
+
}
|
|
699
|
+
/** 直播间页面(上游 get_live_info):从服务端渲染的脚本里取 roomId、user_unique_id、主播 id。 */
|
|
700
|
+
export async function liveInfo(d, webRid) {
|
|
701
|
+
const res = await d.request({
|
|
702
|
+
url: `${LIVE}/${webRid}`,
|
|
703
|
+
headers: [
|
|
704
|
+
['accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7'],
|
|
705
|
+
['accept-language', 'zh-CN,zh;q=0.9,zh-TW;q=0.8,en;q=0.7,ja;q=0.6'],
|
|
706
|
+
['cache-control', 'no-cache'],
|
|
707
|
+
['pragma', 'no-cache'],
|
|
708
|
+
['priority', 'u=0, i'],
|
|
709
|
+
['referer', 'https://live.douyin.com/?from_nav=1'],
|
|
710
|
+
['sec-ch-ua', '"Not)A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"'],
|
|
711
|
+
['sec-ch-ua-mobile', '?0'],
|
|
712
|
+
['sec-ch-ua-platform', '"Windows"'],
|
|
713
|
+
['sec-fetch-dest', 'empty'],
|
|
714
|
+
['sec-fetch-mode', 'navigate'],
|
|
715
|
+
['sec-fetch-site', 'same-origin'],
|
|
716
|
+
['upgrade-insecure-requests', '1'],
|
|
717
|
+
['user-agent', PROFILE.ua],
|
|
718
|
+
],
|
|
719
|
+
timeout: 30,
|
|
720
|
+
});
|
|
721
|
+
const text = await res.text();
|
|
722
|
+
const ttwid = /(?:^|;\s*)ttwid=([^;]*)/.exec(res.headers.getSetCookie().find((c) => c.startsWith('ttwid=')) ?? '')?.[1] ?? d.cookie('ttwid') ?? '';
|
|
723
|
+
const scripts = [...text.matchAll(/<script\b[^>]*\bnonce\b[^>]*>([\s\S]*?)<\/script>/gi)].map((m) => m[1]);
|
|
724
|
+
for (const s of scripts) {
|
|
725
|
+
if (!s.includes('roomId'))
|
|
726
|
+
continue;
|
|
727
|
+
const userId = /\\"user_unique_id\\":\\"(\d+)\\"/.exec(s)?.[1];
|
|
728
|
+
const roomId = /\\"roomId\\":\\"(\d+)\\"/.exec(s)?.[1];
|
|
729
|
+
const info = /\\"roomInfo\\":\{\\"room\\":\{\\"id_str\\":\\".*?\\",\\"status\\":(.*?),\\"status_str\\":\\".*?\\",\\"title\\":\\"(.*?)\\"/.exec(s);
|
|
730
|
+
const anchorId = /\\"anchor\\":\{\\"id_str\\":\\"(\d+)\\"/.exec(s)?.[1];
|
|
731
|
+
const secUid = /\\"sec_uid\\":\\"(.*?)\\"/.exec(s)?.[1];
|
|
732
|
+
if (userId && roomId && info && anchorId && secUid) {
|
|
733
|
+
return { room_id: roomId, user_id: userId, user_unique_id: userId, anchor_id: anchorId, sec_uid: secUid, ttwid, room_status: info[1], room_title: info[2] };
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
if (text.includes('roomId')) {
|
|
737
|
+
const first = (re) => re.exec(text)?.[1] ?? '';
|
|
738
|
+
const roomId = first(/\\"roomId\\"\s*:\s*\\"(\d+)\\"/);
|
|
739
|
+
const userId = first(/\\"user_unique_id\\"\s*:\s*\\"(\d+)\\"/);
|
|
740
|
+
const anchorId = first(/\\"anchor\\"\s*:\s*\\\{[^{}]*?\\"id_str\\"\s*:\s*\\"(\d+)\\"/);
|
|
741
|
+
const secUid = first(/\\"sec_uid\\"\s*:\s*\\"([^"]+)\\"/);
|
|
742
|
+
if (roomId && userId)
|
|
743
|
+
return { room_id: roomId, user_id: userId, user_unique_id: userId, anchor_id: anchorId || userId, sec_uid: secUid, ttwid };
|
|
744
|
+
}
|
|
745
|
+
return null;
|
|
746
|
+
}
|
|
747
|
+
/** 直播 GET 公共参数(上游 _get_live_web)。 */
|
|
748
|
+
async function liveWeb(d, api, endpoint, webRid = '', enterFrom = 'link_share') {
|
|
749
|
+
const h = headers('GET').referer(webRid ? `${LIVE}/${webRid}` : LIVE);
|
|
750
|
+
const p = new Params();
|
|
751
|
+
p.add('aid', '6383').add('app_name', 'douyin_web').add('live_id', '1').add('device_platform', 'web').add('language', 'zh-CN');
|
|
752
|
+
p.add('enter_from', enterFrom).add('cookie_enabled', 'true').add('screen_width', PROFILE.screenWidth).add('screen_height', PROFILE.screenHeight);
|
|
753
|
+
p.add('browser_language', 'zh-CN').add('browser_platform', PROFILE.platform).add('browser_name', PROFILE.browserName);
|
|
754
|
+
p.add('browser_version', PROFILE.browserVersion).add('os_name', 'Windows').add('os_version', '10');
|
|
755
|
+
for (const [k, v] of endpoint)
|
|
756
|
+
if (v != null)
|
|
757
|
+
p.add(k, v);
|
|
758
|
+
await withMsToken(d, p);
|
|
759
|
+
withABogus(d, p, undefined, LIVE_HOST);
|
|
760
|
+
return getJson(d, LIVE + api, h, p, 30);
|
|
761
|
+
}
|
|
762
|
+
/** 房间资料(上游 get_live_room_enter)。 */
|
|
763
|
+
export function liveRoomEnter(d, webRid) {
|
|
764
|
+
return liveWeb(d, '/webcast/room/web/enter/', [['web_rid', webRid]], webRid);
|
|
765
|
+
}
|
|
766
|
+
/** 直播间贡献榜(上游 get_live_contribution_rank)。 */
|
|
767
|
+
export function liveRank(d, roomId, anchorId, secAnchorId, webRid = '') {
|
|
768
|
+
return liveWeb(d, '/webcast/ranklist/audience/', [
|
|
769
|
+
['webcast_sdk_version', '2450'],
|
|
770
|
+
['room_id', roomId],
|
|
771
|
+
['anchor_id', anchorId],
|
|
772
|
+
['sec_anchor_id', secAnchorId],
|
|
773
|
+
['ignoreToast', 'true'],
|
|
774
|
+
['rank_type', '30'],
|
|
775
|
+
['update_scene', null],
|
|
776
|
+
], webRid, 'web_live');
|
|
777
|
+
}
|
|
778
|
+
/** 直播间千票榜,页面上的「1000 贡献用户」(上游 get_live_thousand_ticket_rank,接口 ranklist/paygrade_seats)。 */
|
|
779
|
+
export function liveThousandRank(d, roomId, webRid = '', seatsType = '2') {
|
|
780
|
+
return liveWeb(d, '/webcast/ranklist/paygrade_seats/', [
|
|
781
|
+
['webcast_sdk_version', '2450'],
|
|
782
|
+
['room_id', roomId],
|
|
783
|
+
['seats_type', seatsType],
|
|
784
|
+
], webRid, 'web_live');
|
|
785
|
+
}
|
|
786
|
+
/** 弹幕长连前的 im/fetch(上游 get_webcast_detail),返回 protobuf 字节。 */
|
|
787
|
+
export async function webcastFetch(d, userId, roomId, url) {
|
|
788
|
+
const h = headers('FORM').set('origin', LIVE).referer(url);
|
|
789
|
+
await withCsrf(d, h);
|
|
790
|
+
const p = new Params();
|
|
791
|
+
p.add('resp_content_type', 'protobuf').add('did_rule', '3').add('device_id', '').add('app_name', 'douyin_web').add('endpoint', 'live_pc');
|
|
792
|
+
p.add('support_wrds', '1').add('user_unique_id', userId).add('identity', 'audience').add('need_persist_msg_count', '15').add('insert_task_id', '');
|
|
793
|
+
p.add('live_reason', '').add('room_id', roomId).add('version_code', '180800').add('last_rtt', '0').add('live_id', '1').add('aid', '6383');
|
|
794
|
+
p.add('fetch_rule', '1').add('cursor', '').add('internal_ext', '').add('device_platform', 'web').add('cookie_enabled', 'true');
|
|
795
|
+
p.add('screen_width', PROFILE.screenWidth).add('screen_height', PROFILE.screenHeight).add('browser_language', 'zh-CN').add('browser_platform', PROFILE.platform);
|
|
796
|
+
p.add('browser_name', 'Mozilla').add('browser_version', APP_VERSION).add('browser_online', 'true').add('tz_name', 'Asia/Shanghai');
|
|
797
|
+
await withMsToken(d, p);
|
|
798
|
+
withABogus(d, p, undefined, LIVE_HOST);
|
|
799
|
+
const res = await d.request({ url: `${LIVE}/webcast/im/fetch/`, headers: h.list(), query: p.pairs() });
|
|
800
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
801
|
+
}
|
|
802
|
+
function liveBase(p) {
|
|
803
|
+
p.add('aid', '6383').add('app_name', 'douyin_web').add('live_id', '1').add('device_platform', 'web').add('language', 'zh-CN');
|
|
804
|
+
return p;
|
|
805
|
+
}
|
|
806
|
+
/** 直播间点赞(上游 diggLiveRoom)。 */
|
|
807
|
+
export async function liveLike(d, roomId, count = '1') {
|
|
808
|
+
const h = headers('FORM').set('origin', WWW);
|
|
809
|
+
await withCsrf(d, h);
|
|
810
|
+
h.referer(`${LIVE}/${roomId}`);
|
|
811
|
+
const p = liveBase(new Params());
|
|
812
|
+
p.add('enter_from', 'web_live').add('cookie_enabled', 'true').add('screen_width', PROFILE.screenWidth).add('screen_height', PROFILE.screenHeight);
|
|
813
|
+
p.add('browser_language', 'zh-CN').add('browser_platform', 'Win32').add('browser_name', PROFILE.browserName).add('browser_version', PROFILE.browserVersion);
|
|
814
|
+
p.add('room_id', roomId).add('count', count);
|
|
815
|
+
await withMsToken(d, p);
|
|
816
|
+
withABogus(d, p, []);
|
|
817
|
+
const res = await d.request({ method: 'POST', url: `${LIVE}/webcast/room/like/`, headers: h.list(), query: p.pairs(), form: [] });
|
|
818
|
+
return riskJson(res);
|
|
819
|
+
}
|
|
820
|
+
/** 发直播间评论(上游 sendMsgInRoom,GET /webcast/room/chat/)。 */
|
|
821
|
+
export async function liveChat(d, roomId, content, webRid = roomId) {
|
|
822
|
+
const api = '/webcast/room/chat/';
|
|
823
|
+
const h = headers('GET').set('Origin', LIVE);
|
|
824
|
+
await d.withBd(h, api, { origin: LIVE });
|
|
825
|
+
await withCsrf(d, h);
|
|
826
|
+
h.referer(`${LIVE}/${webRid}`);
|
|
827
|
+
const p = liveBase(new Params());
|
|
828
|
+
p.add('enter_from', 'link_share').add('cookie_enabled', 'true').add('screen_width', PROFILE.screenWidth).add('screen_height', PROFILE.screenHeight);
|
|
829
|
+
p.add('browser_language', 'zh-CN').add('browser_platform', 'Win32').add('browser_name', PROFILE.browserName).add('browser_version', PROFILE.browserVersion);
|
|
830
|
+
p.add('room_id', roomId).add('content', content).add('type', '0');
|
|
831
|
+
await withMsToken(d, p);
|
|
832
|
+
withABogus(d, p, undefined, LIVE_HOST);
|
|
833
|
+
return getJson(d, LIVE + api, h, p);
|
|
834
|
+
}
|
|
835
|
+
/** 服务端 ecies 证书(上游 utils/bd_ticket.fetch_server_cert)。 */
|
|
836
|
+
export async function serverCert(d, aid, cookieStr, origin = WWW, options = {}) {
|
|
837
|
+
const query = [
|
|
838
|
+
['aid', String(aid)],
|
|
839
|
+
['is_from_ttaccountsdk', '1'],
|
|
840
|
+
['msToken', options.msToken || randomMsToken()],
|
|
841
|
+
];
|
|
842
|
+
query.push(['a_bogus', d.ab.sign(urlencode(query))]);
|
|
843
|
+
const url = `${origin}/passport/ticket_guard/get_client_cert/?${urlencode(query)}`;
|
|
844
|
+
const h = [
|
|
845
|
+
['x-tt-session-dtrait', options.dtrait ?? ''],
|
|
846
|
+
['referer', options.referer ?? `${origin}/`],
|
|
847
|
+
['user-agent', PROFILE.ua],
|
|
848
|
+
['accept', 'application/json'],
|
|
849
|
+
];
|
|
850
|
+
const csrf = options.csrf ?? (await csrfToken(d, cookieStr));
|
|
851
|
+
if (csrf)
|
|
852
|
+
h.push(['x-secsdk-csrf-token', csrf]);
|
|
853
|
+
h.push(['content-type', 'application/x-www-form-urlencoded'], ['cookie', cookieStr]);
|
|
854
|
+
h.push(['accept-language', 'zh-CN,zh;q=0.9'], ['origin', origin], ['priority', 'u=1, i'], ['sec-fetch-dest', 'empty'], ['sec-fetch-mode', 'cors'], ['sec-fetch-site', 'same-origin']);
|
|
855
|
+
const res = await d.http.request({ method: 'POST', url, headers: h, body: `server_data=1,aid=${aid}`, timeout: 15 });
|
|
856
|
+
const body = jsonLoads(await res.text());
|
|
857
|
+
if (body.message !== 'success')
|
|
858
|
+
throw new Error(`获取服务端证书失败:${body.message ?? ''}`);
|
|
859
|
+
const cert = body.data?.server_cert;
|
|
860
|
+
if (!cert)
|
|
861
|
+
throw new Error('服务端证书为空');
|
|
862
|
+
return cert;
|
|
863
|
+
}
|