♻️ 请求重构

This commit is contained in:
目棃
2025-03-07 17:53:35 +08:00
parent fee1872b46
commit ce5a88954a
22 changed files with 582 additions and 876 deletions

View File

@@ -1,12 +1,32 @@
/**
* @file web/request/bbsReq.ts
* @description BBS 请求模块
* @since Beta v0.6.3
* @since Beta v0.7.2
*/
import TGHttp from "@/utils/TGHttp.js";
import { getRequestHeader } from "@/web/utils/getRequestHeader.js";
/**
* @description 获取表情包列表
* @since Beta v0.7.2
* @return {Promise<Record<string,string>|TGApp.BBS.Response.Base>}
*/
async function getEmoticonSet(): Promise<Record<string, string> | TGApp.BBS.Response.Base> {
const resp = await TGHttp<TGApp.BBS.Emoji.Resp>(
"https://bbs-api-static.miyoushe.com/misc/api/emoticon_set",
{ method: "GET" },
);
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
const emojis: Record<string, string> = {};
for (const series of resp.data.list) {
for (const emoji of series.list) {
emojis[emoji.name] = emoji.icon;
}
}
return emojis;
}
/**
* @description 根据 cookie 获取用户信息
* @since Beta v0.5.0
@@ -46,6 +66,10 @@ async function getOtherUserInfo(
return resp.data.user_info;
}
const BBSApi = { userInfo: getUserFullInfo, otherUserInfo: getOtherUserInfo };
const BBSApi = {
emojis: getEmoticonSet,
userInfo: getUserFullInfo,
otherUserInfo: getOtherUserInfo,
};
export default BBSApi;

View File

@@ -1,7 +1,7 @@
/**
* @file web/request/otherReq.ts
* @description Other API
* @since Beta v0.6.8
* @since Beta v0.7.2
*/
import TGBbs from "@/utils/TGBbs.js";
@@ -11,7 +11,7 @@ import { getInitDeviceInfo } from "@/utils/toolFunc.js";
/**
* @description 获取设备指纹
* @since Beta v0.5.5
* @since Beta v0.7.2
* @param {TGApp.App.Device.DeviceInfo} Info - 设备信息
* @returns {Promise<TGApp.App.Device.DeviceInfo>} 设备指纹
*/
@@ -91,8 +91,9 @@ async function getDeviceFp(
"x-requested-with": "com.mihoyo.hyperion",
Referer: "https://webstatic.mihoyo.com/",
};
type ResType = { device_fp: string; code: number; msg: string };
try {
const resp = await TGHttp<DeviceFpResp>(
const resp = await TGHttp<TGApp.BBS.Response.BaseWithData<ResType>>(
"https://public-data-api.mihoyo.com/device-fp/api/getFp",
{ method: "POST", body: JSON.stringify(data), headers: header },
);
@@ -125,8 +126,3 @@ async function refreshCode(
const OtherApi = { code: refreshCode, fp: getDeviceFp };
export default OtherApi;
/// 一些类型 ///
type DeviceFpResp = TGApp.BBS.Response.BaseWithData & {
data: { device_fp: string; code: number; msg: string };
};

View File

@@ -1,8 +1,11 @@
/**
* @file web/request/passportReq.ts
* @description Passport 相关请求
* @since Beta v0.6.8
* @since Beta v0.7.2
*/
import { JSEncrypt } from "jsencrypt";
import TGBbs from "@/utils/TGBbs.js";
import TGHttp from "@/utils/TGHttp.js";
import { getDeviceInfo } from "@/utils/toolFunc.js";
import { getRequestHeader } from "@/web/utils/getRequestHeader.js";
@@ -13,6 +16,26 @@ const pAbu: Readonly<string> = "https://passport-api.mihoyo.com/";
const p4Abu: Readonly<string> = "https://passport-api-v4.mihoyo.com/";
// HoyoLauncherVersion
const hlv: Readonly<string> = "1.3.3.182";
// 加密密钥
const PUB_KEY_STR: Readonly<string> = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDvekdPMHN3AYhm/vktJT+YJr7cI5DcsNKqdsx5DZX0gDuWFuIjzdwButrIYPNmRJ1G8ybDIF7oDW2eEpm5sMbL9zs
9ExXCdvqrn51qELbqj0XxtMTIpaCHFSI50PfPpTFV9Xt/hmyVwokoOXFlAEgCn+Q
CgGs52bFoYMtyi+xEQIDAQAB
-----END PUBLIC KEY-----`;
const encrypt = new JSEncrypt();
encrypt.setPublicKey(PUB_KEY_STR);
/**
* @description rsa 加密
* @since Beta v0.5.1
* @param {string} data - 待加密数据
* @returns {string} 加密后数据
*/
function rsaEncrypt(data: string): string {
const res = encrypt.encrypt(data.toString());
if (res === false) return "";
return res;
}
/**
* @description 获取登录ticket
@@ -35,7 +58,8 @@ async function createAuthTicketByGameBiz(
"x-rpc-client_type": "3",
"x-rpc-app_id": "ddxf5dufpuyo",
};
const resp = await TGHttp<AuthTicketByGameBizResp>(
type ResType = { ticket: string };
const resp = await TGHttp<TGApp.BBS.Response.BaseWithData<ResType>>(
`${pAbu}account/ma-cn-verifier/app/createAuthTicketByGameBiz`,
{ method: "POST", headers: headers, query: params },
);
@@ -43,6 +67,48 @@ async function createAuthTicketByGameBiz(
return resp.data.ticket;
}
/**
* @description 获取短信验证码
* @since Beta v0.7.2
* @param {string} phone - 手机号
* @param {string} [aigis] - 验证数据
* @returns {Promise<TGApp.BBS.CaptchaLogin.CaptchaRes | TGApp.BBS.Response.BaseWithData<string>>}
*/
async function createLoginCaptcha(
phone: string,
aigis?: string,
): Promise<TGApp.BBS.CaptchaLogin.CaptchaRes | TGApp.BBS.Response.BaseWithData<string>> {
const body = { area_code: rsaEncrypt("+86"), mobile: rsaEncrypt(phone) };
const header: Record<string, string> = {
"x-rpc-aigis": aigis || "",
"x-rpc-app_version": TGBbs.version,
"x-rpc-client_type": "2",
"x-rpc-app_id": "bll8iq97cem8",
"x-rpc-device_fp": getDeviceInfo("device_fp"),
"x-rpc-device_name": getDeviceInfo("device_name"),
"x-rpc-device_id": getDeviceInfo("device_id"),
"x-rpc-device_model": getDeviceInfo("product"),
"user-agent": TGBbs.ua,
"content-type": "application/json",
referer: "https://user.miyoushe.com/",
"x-rpc-game_biz": "hk4e_cn",
};
const resp = await TGHttp<TGApp.BBS.CaptchaLogin.CaptchaResp>(
`${pAbu}account/ma-cn-verifier/verifier/createLoginCaptcha`,
{ method: "POST", headers: header, body: JSON.stringify(body) },
true,
);
const data = await resp.data;
if (data.retcode !== 0) {
return <TGApp.BBS.Response.BaseWithData<string>>{
retcode: data.retcode,
message: data.message,
data: resp.resp.headers.get("x-rpc-aigis"),
};
}
return <TGApp.BBS.CaptchaLogin.CaptchaRes>data.data;
}
/**
* @description 创建登录二维码
* @since Beta v0.6.8
@@ -78,7 +144,8 @@ async function getCookieAccountInfoBySToken(
): Promise<string | TGApp.BBS.Response.Base> {
const ck = { stoken: cookie.stoken, mid: cookie.mid };
const params = { stoken: cookie.stoken };
const resp = await TGHttp<GetCookieTokenBySTokenResp | TGApp.BBS.Response.Base>(
type ResType = { uid: string; cookie_token: string };
const resp = await TGHttp<TGApp.BBS.Response.BaseWithData<ResType>>(
`${pAbu}account/auth/api/getCookieAccountInfoBySToken`,
{ method: "GET", headers: getRequestHeader(ck, "GET", params), query: params },
);
@@ -97,7 +164,8 @@ async function getLTokenBySToken(
): Promise<string | TGApp.BBS.Response.Base> {
const ck = { mid: cookie.mid, stoken: cookie.stoken };
const params = { stoken: cookie.stoken };
const resp = await TGHttp<GetLTokenBySTokenResp | TGApp.BBS.Response.Base>(
type ResType = { ltoken: string };
const resp = await TGHttp<TGApp.BBS.Response.BaseWithData<ResType>>(
`${pAbu}account/auth/api/getLTokenBySToken`,
{ method: "GET", headers: getRequestHeader(ck, "GET", params), query: params },
);
@@ -105,6 +173,46 @@ async function getLTokenBySToken(
return resp.data.ltoken;
}
/**
* @description 通过短信验证码登录
* @since Beta v0.5.1
* @param {string} phone - 手机号
* @param {string} captcha - 验证码
* @param {string} action_type - 操作类型
* @param {string} [aigis] - 验证数据
* @returns {Promise<TGApp.BBS.CaptchaLogin.LoginRes | TGApp.BBS.Response.Base>}
*/
async function loginByMobileCaptcha(
phone: string,
captcha: string,
action_type: string,
aigis?: string,
): Promise<TGApp.BBS.CaptchaLogin.LoginRes | TGApp.BBS.Response.Base> {
const body = {
area_code: rsaEncrypt("+86"),
mobile: rsaEncrypt(phone),
action_type,
captcha,
};
const header = {
"x-rpc-aigis": aigis || "",
"x-rpc-app_version": TGBbs.version,
"x-rpc-client_type": "2",
"x-rpc-app_id": "bll8iq97cem8",
"x-rpc-device_fp": getDeviceInfo("device_fp"),
"x-rpc-device_name": getDeviceInfo("device_name"),
"x-rpc-device_id": getDeviceInfo("device_id"),
"x-rpc-device_model": getDeviceInfo("product"),
"user-agent": TGBbs.ua,
};
const resp = await TGHttp<TGApp.BBS.CaptchaLogin.LoginResp>(
`${pAbu}account/ma-cn-passport/app/loginByMobileCaptcha`,
{ method: "POST", headers: header, body: JSON.stringify(body) },
);
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
return resp.data;
}
/**
* @description 获取登录状态
* @since Beta v0.6.8
@@ -142,35 +250,7 @@ async function verifyLToken(
): Promise<string | TGApp.BBS.Response.Base> {
const ck = { ltoken: cookie.ltoken, ltuid: cookie.ltuid };
const data = { ltoken: cookie.ltoken };
const resp = await TGHttp<VerifyLtokenResp>(`${p4Abu}account/ma-cn-session/web/verifyLtoken`, {
method: "POST",
headers: getRequestHeader(ck, "POST", data),
body: JSON.stringify(data),
});
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
return resp.data.user_info.mid;
}
const PassportApi = {
authTicket: createAuthTicketByGameBiz,
cookieToken: getCookieAccountInfoBySToken,
lToken: { get: getLTokenBySToken, verify: verifyLToken },
qrLogin: { create: createQrLogin, query: queryLoginStatus },
};
export default PassportApi;
/// 一些类型 ///
type AuthTicketByGameBizResp = TGApp.BBS.Response.BaseWithData & { data: { ticket: string } };
type GetCookieTokenBySTokenResp = TGApp.BBS.Response.BaseWithData & {
data: { uid: string; cookie_token: string };
};
type GetLTokenBySTokenResp = TGApp.BBS.Response.BaseWithData & { data: { ltoken: string } };
type VerifyLtokenResp = TGApp.BBS.Response.BaseWithData & {
data: {
type ResType = {
realname_info: unknown;
need_realperson: boolean;
user_info: {
@@ -189,4 +269,24 @@ type VerifyLtokenResp = TGApp.BBS.Response.BaseWithData & {
links: Array<unknown>;
};
};
const resp = await TGHttp<TGApp.BBS.Response.BaseWithData<ResType>>(
`${p4Abu}account/ma-cn-session/web/verifyLtoken`,
{
method: "POST",
headers: getRequestHeader(ck, "POST", data),
body: JSON.stringify(data),
},
);
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
return resp.data.user_info.mid;
}
const PassportApi = {
authTicket: createAuthTicketByGameBiz,
cookieToken: getCookieAccountInfoBySToken,
lToken: { get: getLTokenBySToken, verify: verifyLToken },
qrLogin: { create: createQrLogin, query: queryLoginStatus },
captcha: { create: createLoginCaptcha, login: loginByMobileCaptcha },
};
export default PassportApi;

View File

@@ -1,7 +1,7 @@
/**
* @file web/request/takumiReq.ts
* @description Takumi 相关请求函数
* @since Beta v0.7.0
* @since Beta v0.7.2
*/
import TGBbs from "@/utils/TGBbs.js";
import TGHttp from "@/utils/TGHttp.js";
@@ -57,20 +57,20 @@ async function getSTokenByGameToken(
/**
* @description 根据stoken获取action_ticket
* @since Beta v0.6.3
* @since Beta v0.7.2
* @param {TGApp.App.Account.Cookie} cookie Cookie
* @param {TGApp.Sqlite.Account.Game} user 用户
* @param {string} actionType 动作类型
* @returns {Promise<ActionTicketByStokenResp>}
* @returns {Promise<TGApp.BBS.Response.Base>}
*/
async function getActionTicketBySToken(
cookie: TGApp.App.Account.Cookie,
user: TGApp.Sqlite.Account.Game,
actionType: string,
): Promise<ActionTicketByStokenResp> {
): Promise<TGApp.BBS.Response.Base> {
const ck = { stoken: cookie.stoken, mid: cookie.mid };
const params = { action_type: actionType, stoken: cookie.stoken, uid: user.gameUid };
return await TGHttp<ActionTicketByStokenResp>(`${taBu}auth/api/getActionTicketBySToken`, {
return await TGHttp<TGApp.BBS.Response.Base>(`${taBu}auth/api/getActionTicketBySToken`, {
method: "GET",
headers: getRequestHeader(ck, "GET", params, "K2"),
query: params,
@@ -127,7 +127,7 @@ async function genAuthKey2(
/**
* @description 通过cookie获取游戏账号
* @since Beta v0.6.5
* @since Beta v0.7.2
* @param {TGApp.App.Account.Cookie} cookie cookie
* @returns {Promise<TGApp.BBS.Account.GameAccount[]|TGApp.BBS.Response.Base>}
*/
@@ -136,58 +136,76 @@ async function getUserGameRolesByCookie(
): Promise<TGApp.BBS.Account.GameAccount[] | TGApp.BBS.Response.Base> {
const ck = { account_id: cookie.account_id, cookie_token: cookie.cookie_token };
const params = { game_biz: "hk4e_cn" };
const resp = await TGHttp<GameAccountsResp>(`${taBu}binding/api/getUserGameRolesByCookie`, {
method: "GET",
headers: getRequestHeader(ck, "GET", params),
query: params,
});
type ResType = { list: Array<TGApp.BBS.Account.GameAccount> };
const resp = await TGHttp<TGApp.BBS.Response.BaseWithData<ResType>>(
`${taBu}binding/api/getUserGameRolesByCookie`,
{
method: "GET",
headers: getRequestHeader(ck, "GET", params),
query: params,
},
);
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
return resp.data.list;
}
/**
* @description 获取卡池信息
* @since Beta v0.7.2
* @return {Promise<Array<TGApp.BBS.Obc.GachaItem>>}
*/
async function getObcGachaPool(): Promise<Array<TGApp.BBS.Obc.GachaItem>> {
const resp = await TGHttp<TGApp.BBS.Obc.GachaResp>(
`${taBu}common/blackboard/ys_obc/v1/gacha_pool`,
{
method: "GET",
query: { app_sn: "ys_obc" },
headers: { "Content-Type": "application/json" },
},
);
return resp.data.list;
}
/**
* @description 深度优先遍历
* @since Beta v0.7.2
* @param {Array<TGApp.BBS.Obc.ObcItem<TGApp.BBS.Obc.PositionItem>>} list 列表
* @returns {Array<TGApp.BBS.Obc.PositionItem>} 返回列表
*/
function DfsObc(
list: Array<TGApp.BBS.Obc.ObcItem<TGApp.BBS.Obc.PositionItem>>,
): Array<TGApp.BBS.Obc.PositionItem> {
const res: Array<TGApp.BBS.Obc.PositionItem> = [];
for (const item of list) {
if (item.name === "近期活动") res.push(...item.list);
if (item.children) res.push(...DfsObc(item.children));
}
return res;
}
/**
* @description 获取热点追踪信息
* @since Beta v0.7.2
* @return {Promise<Array<TGApp.BBS.Obc.PositionItem>>}
*/
async function getObcHomePosition(): Promise<Array<TGApp.BBS.Obc.PositionItem>> {
const resp = await TGHttp<TGApp.BBS.Obc.PositionResp>(
`${taBu}common/blackboard/ys_obc/v1/home/position`,
{
method: "GET",
query: { app_sn: "ys_obc" },
headers: { "Content-Type": "application/json" },
},
);
const data = resp.data.list;
return DfsObc(data);
}
const TakumiApi = {
auth: { actionTicket: getActionTicketBySToken },
bind: { authKey: genAuthKey, authKey2: genAuthKey2, gameRoles: getUserGameRolesByCookie },
game: { stoken: getSTokenByGameToken },
obc: { gacha: getObcGachaPool, position: getObcHomePosition },
};
export default TakumiApi;
/// 一些类型 ///
type ActionTicketByStokenResp = TGApp.BBS.Response.BaseWithData & {
data: {
ticket: string;
is_verified: boolean;
account_info: {
is_realname: boolean;
mobile: string;
safe_mobile: string;
account_id: string;
account_name: string;
email: string;
is_email_verify: boolean;
area_code: string;
safe_area_code: string;
real_name: string;
identity_code: string;
create_time: string;
create_ip: string;
change_pwd_time: string;
nickname: string;
user_icon_id: number;
safe_level: number;
black_endtime: string;
black_note: string;
gender: number;
real_stat: number;
apple_name: string;
sony_name: string;
tap_name: string;
reactivate_ticket: string;
};
};
};
type GameAccountsResp = TGApp.BBS.Response.BaseWithData & {
data: { list: Array<TGApp.BBS.Account.GameAccount> };
};