feat: add support for china bilibili channel resources

This commit is contained in:
daydreamer-json
2026-02-24 18:23:02 +09:00
parent 537bcad9d3
commit 9ad661c925
23 changed files with 1434 additions and 27 deletions

View File

@@ -0,0 +1,41 @@
import ky from 'ky';
import * as TypesApiAkEndfield from '../../../types/api/akEndfield/Api.js';
import config from '../../config.js';
import defaultSettings from './defaultSettings.js';
const overrideDefSetKy = {
...defaultSettings.ky,
headers: {
'User-Agent': config.network.userAgent.qtHgSdk,
},
};
export default {
giftcode: {
redeem: async (
channelId: number,
serverId: number,
platform: 'Windows' | 'iOS' | 'Android',
code: string,
token: string,
confirm: boolean = false,
) => {
const rsp = await ky
.post(`https://${config.network.api.akEndfield.base.gameHub}/giftcode/api/redeem`, {
...overrideDefSetKy,
headers: {
...overrideDefSetKy.headers,
Origin: 'https://' + config.network.api.akEndfield.base.webview,
Referer:
'https://' +
config.network.api.akEndfield.base.webview +
`/page/giftcode?u8_token=${encodeURIComponent(token)}&platform=${platform}&channel=${channelId}&subChannel=${channelId}&lang=en-us&server=${serverId}`,
'Accept-Language': 'en-us',
},
json: { channelId: String(channelId), serverId: String(serverId), platform, code, token, confirm },
})
.json();
return rsp as TypesApiAkEndfield.GameHubGiftCodeRedeem;
},
},
};

View File

@@ -1,15 +1,19 @@
import accountService from './accountService.js';
import binding from './binding.js';
import gameHub from './gameHub.js';
import launcher from './launcher.js';
import launcherWeb from './launcherWeb.js';
import u8 from './u8.js';
import webview from './webview.js';
import zonai from './zonai.js';
export default {
accountService,
binding,
gameHub,
launcher,
launcherWeb,
u8,
webview,
zonai,
};

View File

@@ -0,0 +1,159 @@
// https://zonai.skport.com/web/v1/user/auth/generate_cred_by_code
import crypto from 'node:crypto';
import ky from 'ky';
import { DateTime } from 'luxon';
import * as TypesApiAkEndfield from '../../../types/api/akEndfield/Api.js';
import config from '../../config.js';
import defaultSettings from './defaultSettings.js';
const overrideDefSetKy = {
...defaultSettings.ky,
headers: {
'User-Agent': config.network.userAgent.chromeWindows,
vname: '1.0.0',
platform: '3',
},
};
function calcSignHeader(path: string, cred: string, salt: string) {
const timestamp = DateTime.now().toUnixInteger().toString();
const useV2Path: string[] = [
'/web/v1/wiki/me',
'/web/v2/user',
'/api/v1/game/player/binding',
'/web/v1/game/endfield/attendance',
'/web/v1/game/endfield/attendance/record',
];
if (useV2Path.includes(path)) {
const v2Payload = JSON.stringify({
platform: String(overrideDefSetKy.headers.platform),
timestamp,
dId: '',
vName: overrideDefSetKy.headers.vname,
});
return {
sign: crypto
.createHash('md5')
.update(
crypto
.createHmac('sha256', salt)
.update(path + timestamp + v2Payload)
.digest('hex'),
)
.digest('hex'),
timestamp,
};
} else {
return { sign: crypto.hash('md5', `timestamp=${timestamp}&cred=${cred}`, 'hex'), timestamp };
}
}
export default {
web: {
v1: {
game: {
endfield: {
attendance: {
get: async (cred: string, token: string, skGameRole: string) => {
const path = '/web/v1/game/endfield/attendance';
const rsp = await ky
.get(`https://${config.network.api.akEndfield.base.zonai}` + path, {
...overrideDefSetKy,
headers: {
...overrideDefSetKy.headers,
cred,
...calcSignHeader(path, cred, token),
'sk-game-role': skGameRole,
},
})
.json();
return rsp as TypesApiAkEndfield.ZonaiWebV1GameEndfieldAttendance;
},
record: async (cred: string, token: string, skGameRole: string) => {
const path = '/web/v1/game/endfield/attendance/record';
const rsp = await ky
.get(`https://${config.network.api.akEndfield.base.zonai}` + path, {
...overrideDefSetKy,
headers: {
...overrideDefSetKy.headers,
cred,
...calcSignHeader(path, cred, token),
'sk-game-role': skGameRole, // 3_4000000000_2
},
})
.json();
return rsp as TypesApiAkEndfield.ZonaiWebV1GameEndfieldAttendanceRecord;
},
},
},
},
user: {
auth: {
generateCredByCode: async (code: string, kind: 1) => {
const rsp = await ky
.post(`https://${config.network.api.akEndfield.base.zonai}/web/v1/user/auth/generate_cred_by_code`, {
...overrideDefSetKy,
headers: { ...overrideDefSetKy.headers },
json: { kind, code },
})
.json();
return rsp as TypesApiAkEndfield.ZonaiWebV1UserAuthGenCredByCode;
},
},
check: async (cred: string, token: string) => {
const path = '/web/v1/user/check';
const rsp = await ky
.get(`https://${config.network.api.akEndfield.base.zonai}` + path, {
...overrideDefSetKy,
headers: { ...overrideDefSetKy.headers, cred, ...calcSignHeader(path, cred, token) },
})
.json();
return rsp as TypesApiAkEndfield.ZonaiWebV1UserCheck;
},
},
wiki: {
me: async (cred: string, token: string) => {
const path = '/web/v1/wiki/me';
const rsp = await ky
.get(`https://${config.network.api.akEndfield.base.zonai}` + path, {
...overrideDefSetKy,
headers: { ...overrideDefSetKy.headers, cred, ...calcSignHeader(path, cred, token) },
})
.json();
return rsp as TypesApiAkEndfield.ZonaiWebV1WikiMe;
},
},
},
v2: {
user: async (cred: string, token: string) => {
const path = '/web/v2/user';
const rsp = await ky
.get(`https://${config.network.api.akEndfield.base.zonai}` + path, {
...overrideDefSetKy,
headers: { ...overrideDefSetKy.headers, cred, ...calcSignHeader(path, cred, token) },
})
.json();
return rsp as TypesApiAkEndfield.ZonaiWebV2User;
},
},
},
api: {
v1: {
game: {
player: {
binding: async (cred: string, token: string) => {
const path = '/api/v1/game/player/binding';
const rsp = await ky
.get(`https://${config.network.api.akEndfield.base.zonai}` + path, {
...overrideDefSetKy,
headers: { ...overrideDefSetKy.headers, cred, ...calcSignHeader(path, cred, token) },
})
.json();
return rsp as TypesApiAkEndfield.ZonaiApiV1GamePlayerBinding;
},
},
},
},
},
};

View File

@@ -21,15 +21,23 @@ type ConfigType = AllRequired<
accountService: { osWinRel: string; skport: string; binding: string };
u8: { osWinRel: string };
};
channel: { osWinRel: number; cnWinRel: number };
subChannel: { osWinRel: number; osWinRelEpic: number; osWinRelGooglePlay: number; cnWinRel: number };
channel: { osWinRel: number; cnWinRel: number; cnWinRelBilibili: number };
subChannel: {
osWinRel: number;
osWinRelEpic: number;
osWinRelGooglePlay: number;
cnWinRel: number;
cnWinRelBilibili: number;
};
base: {
accountService: string;
gameHub: string;
launcher: string;
launcherCN: string;
u8: string;
binding: string;
webview: string;
zonai: string;
};
};
};
@@ -37,6 +45,7 @@ type ConfigType = AllRequired<
// UA to hide the fact that the access is from this tool
minimum: string;
chromeWindows: string;
qtHgSdk: string;
curl: string;
ios: string;
};
@@ -69,15 +78,17 @@ const initialConfig: ConfigType = {
accountService: { osWinRel: 'd9f6dbb6bbd6bb33', skport: '6eb76d4e13aa36e6', binding: '3dacefa138426cfe' },
u8: { osWinRel: '973bd727dd11cbb6ead8' },
},
channel: { osWinRel: 6, cnWinRel: 1 },
subChannel: { osWinRel: 6, osWinRelEpic: 801, osWinRelGooglePlay: 802, cnWinRel: 1 },
channel: { osWinRel: 6, cnWinRel: 1, cnWinRelBilibili: 2 },
subChannel: { osWinRel: 6, osWinRelEpic: 801, osWinRelGooglePlay: 802, cnWinRel: 1, cnWinRelBilibili: 2 },
base: {
accountService: 'YXMuZ3J5cGhsaW5lLmNvbQ==',
gameHub: 'Z2FtZS1odWIuZ3J5cGhsaW5lLmNvbQ==',
launcher: 'bGF1bmNoZXIuZ3J5cGhsaW5lLmNvbS9hcGk=',
launcherCN: 'bGF1bmNoZXIuaHlwZXJncnlwaC5jb20vYXBp',
u8: 'dTguZ3J5cGhsaW5lLmNvbQ==',
binding: 'YmluZGluZy1hcGktYWNjb3VudC1wcm9kLmdyeXBobGluZS5jb20=',
webview: 'ZWYtd2Vidmlldy5ncnlwaGxpbmUuY29t',
zonai: 'em9uYWkuc2twb3J0LmNvbQ==',
},
},
},
@@ -85,6 +96,8 @@ const initialConfig: ConfigType = {
minimum: 'Mozilla/5.0',
chromeWindows:
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36',
qtHgSdk:
'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/5.15.8 Chrome/87.0.4280.144 Safari/537.36 PC/WIN/HGSDK HGWebPC/1.30.1',
curl: 'curl/8.4.0',
ios: 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1',
},