mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-13 09:28:14 +08:00
🎨 一些代码调整
This commit is contained in:
@@ -4,26 +4,14 @@
|
||||
* @since Beta v0.6.3
|
||||
*/
|
||||
|
||||
import {
|
||||
BBS_APP_ID,
|
||||
BBS_SALT,
|
||||
BBS_UA_MOBILE,
|
||||
BBS_UA_PC,
|
||||
BBS_VERSION,
|
||||
CHANNEL_LIST,
|
||||
} from "./bbs.js";
|
||||
import { GAME_BIZ } from "./utils.js";
|
||||
import { BBS_UA_MOBILE, BBS_VERSION, CHANNEL_LIST } from "./bbs.js";
|
||||
|
||||
const TGConstant = {
|
||||
BBS: {
|
||||
VERSION: BBS_VERSION,
|
||||
UA_PC: BBS_UA_PC,
|
||||
UA_MOBILE: BBS_UA_MOBILE,
|
||||
APP_ID: BBS_APP_ID,
|
||||
CHANNELS: CHANNEL_LIST,
|
||||
},
|
||||
Salt: BBS_SALT,
|
||||
GAME_BIZ,
|
||||
};
|
||||
|
||||
export default TGConstant;
|
||||
|
||||
@@ -5,22 +5,7 @@
|
||||
*/
|
||||
|
||||
export const BBS_VERSION = "2.78.1";
|
||||
export const BBS_UA_PC = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) miHoYoBBS/${BBS_VERSION}`;
|
||||
export const BBS_UA_MOBILE = `Mozilla/5.0 (Linux; Android 12) Mobile miHoYoBBS/${BBS_VERSION}`;
|
||||
export const BBS_APP_ID = "bll8iq97cem8";
|
||||
|
||||
/**
|
||||
* @description salt 值
|
||||
* @version 2.78.1
|
||||
* @since Beta v0.6.5
|
||||
*/
|
||||
export const BBS_SALT = {
|
||||
K2: "GuODIETRPuJxpiUQoZairQxHtmzZKYFl",
|
||||
LK2: "ACDpsiiEFSqqLiEpzXMuXNsLNqGkrIQc",
|
||||
X4: "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs",
|
||||
X6: "t0qEgfub6cvueAPgR5m9aQWWVciEer7v",
|
||||
PROD: "t0qEgfub6cvueAPgR5m9aQWWVciEer7v",
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 频道列表
|
||||
@@ -44,7 +29,7 @@ export interface ToChannelItem {
|
||||
* @since Beta v0.6.5
|
||||
* @type {Array<ToChannelItem>}
|
||||
*/
|
||||
export const CHANNEL_LIST: ToChannelItem[] = [
|
||||
export const CHANNEL_LIST: Readonly<Array<ToChannelItem>> = [
|
||||
{
|
||||
title: "原神",
|
||||
icon: "/platforms/mhy/ys.webp",
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* @file web constant utils.ts
|
||||
* @description 一些杂项的常量
|
||||
* @author BTMuli<bt-muli@outlook.com>
|
||||
* @since Alpha v0.2.0
|
||||
*/
|
||||
|
||||
// game_biz
|
||||
export const GAME_BIZ = "hk4e_cn";
|
||||
@@ -16,15 +16,12 @@ import { getRequestHeader } from "../utils/getRequestHeader.js";
|
||||
async function getUserFullInfo(
|
||||
cookie: TGApp.App.Account.Cookie,
|
||||
): Promise<TGApp.BBS.Response.Base | TGApp.Plugins.Mys.User.Info> {
|
||||
const url = "https://bbs-api.miyoushe.com/user/wapi/getUserFullInfo";
|
||||
const ck = { cookie_token: cookie.cookie_token, account_id: cookie.account_id };
|
||||
const params = { gids: "2" };
|
||||
const header = getRequestHeader(ck, "GET", params, "common", true);
|
||||
const resp = await TGHttp<TGApp.Plugins.Mys.User.HomeResponse | TGApp.BBS.Response.Base>(url, {
|
||||
method: "GET",
|
||||
headers: header,
|
||||
query: params,
|
||||
});
|
||||
const resp = await TGHttp<TGApp.Plugins.Mys.User.HomeResponse | TGApp.BBS.Response.Base>(
|
||||
"https://bbs-api.miyoushe.com/user/wapi/getUserFullInfo",
|
||||
{ method: "GET", headers: getRequestHeader(ck, "GET", params, "X4", true), query: params },
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data.user_info;
|
||||
}
|
||||
@@ -42,22 +39,16 @@ async function userFavouritePost(
|
||||
uid: string,
|
||||
offset: string = "",
|
||||
): Promise<TGApp.BBS.Collection.PostRespData | TGApp.BBS.Response.Base> {
|
||||
const url = "https://bbs-api.miyoushe.com/post/wapi/userFavouritePost";
|
||||
const ck = { cookie_token: cookie.cookie_token, account_id: cookie.account_id };
|
||||
const params = { size: "20", uid, offset };
|
||||
const header = getRequestHeader(ck, "GET", params, "common");
|
||||
const resp = await TGHttp<TGApp.BBS.Collection.PostResponse | TGApp.BBS.Response.Base>(url, {
|
||||
method: "GET",
|
||||
headers: header,
|
||||
query: params,
|
||||
});
|
||||
const resp = await TGHttp<TGApp.BBS.Collection.PostResponse | TGApp.BBS.Response.Base>(
|
||||
"https://bbs-api.miyoushe.com/post/wapi/userFavouritePost",
|
||||
{ method: "GET", headers: getRequestHeader(ck, "GET", params), query: params },
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
const BBSApi = {
|
||||
userInfo: getUserFullInfo,
|
||||
lovePost: userFavouritePost,
|
||||
};
|
||||
const BBSApi = { userInfo: getUserFullInfo, lovePost: userFavouritePost };
|
||||
|
||||
export default BBSApi;
|
||||
|
||||
@@ -17,8 +17,29 @@ export enum AnnoServer {
|
||||
|
||||
export type AnnoLang = "zh-cn" | "zh-tw" | "en" | "ja";
|
||||
|
||||
const AnnoApi = "https://hk4e-ann-api.mihoyo.com/common/hk4e_cn/announcement/api";
|
||||
const AnnoApiGlobal = "https://sg-hk4e-api.hoyoverse.com/common/hk4e_global/announcement/api";
|
||||
const AnnoApi: Readonly<string> = "https://hk4e-ann-api.mihoyo.com/common/hk4e_cn/announcement/api";
|
||||
const AnnoApiGlobal: Readonly<string> =
|
||||
"https://sg-hk4e-api.hoyoverse.com/common/hk4e_global/announcement/api";
|
||||
|
||||
/**
|
||||
* @description 判断是否为国内服务器
|
||||
* @since Beta v0.6.5
|
||||
* @param {AnnoServer} region 服务器
|
||||
* @returns {boolean} 是否为国内服务器
|
||||
*/
|
||||
function isCN(region: AnnoServer): boolean {
|
||||
return region === AnnoServer.CN_ISLAND || region === AnnoServer.CN_TREE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 根据服务器获取公告地址
|
||||
* @since Beta v0.6.5
|
||||
* @param {AnnoServer} region 服务器
|
||||
* @returns {string} 公告地址
|
||||
*/
|
||||
function getAnnoApi(region: AnnoServer): string {
|
||||
return isCN(region) ? AnnoApi : AnnoApiGlobal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取游戏内公告参数
|
||||
@@ -31,22 +52,16 @@ function getAnnoParams(
|
||||
region: AnnoServer = AnnoServer.CN_ISLAND,
|
||||
lang: AnnoLang = "zh-cn",
|
||||
): TGApp.BBS.Announcement.Params {
|
||||
const params: TGApp.BBS.Announcement.Params = {
|
||||
return {
|
||||
game: "hk4e",
|
||||
game_biz: "hk4e_cn",
|
||||
game_biz: isCN(region) ? "hk4e_cn" : "hk4e_global",
|
||||
lang,
|
||||
bundle_id: "hk4e_cn",
|
||||
bundle_id: isCN(region) ? "hk4e_cn" : "hk4e_global",
|
||||
platform: "pc",
|
||||
region,
|
||||
level: "55",
|
||||
uid: "100000000",
|
||||
};
|
||||
if (region === AnnoServer.CN_ISLAND || region === AnnoServer.CN_TREE) {
|
||||
return params;
|
||||
}
|
||||
params.game_biz = "hk4e_global";
|
||||
params.bundle_id = "hk4e_global";
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,15 +75,10 @@ async function getAnnoList(
|
||||
region: AnnoServer = AnnoServer.CN_ISLAND,
|
||||
lang: AnnoLang = "zh-cn",
|
||||
): Promise<TGApp.BBS.Announcement.ListData> {
|
||||
const params: TGApp.BBS.Announcement.Params = getAnnoParams(region, lang);
|
||||
let url = `${AnnoApi}/getAnnList`;
|
||||
if (region !== AnnoServer.CN_ISLAND && region !== AnnoServer.CN_TREE) {
|
||||
url = `${AnnoApiGlobal}/getAnnList`;
|
||||
}
|
||||
const resp = await TGHttp<TGApp.BBS.Announcement.ListResponse>(url, {
|
||||
method: "GET",
|
||||
query: params,
|
||||
});
|
||||
const resp = await TGHttp<TGApp.BBS.Announcement.ListResponse>(
|
||||
`${getAnnoApi(region)}/getAnnList`,
|
||||
{ method: "GET", query: getAnnoParams(region, lang) },
|
||||
);
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
@@ -85,21 +95,15 @@ async function getAnnoContent(
|
||||
region: AnnoServer = AnnoServer.CN_ISLAND,
|
||||
lang: AnnoLang = "zh-cn",
|
||||
): Promise<TGApp.BBS.Announcement.ContentItem> {
|
||||
const params: TGApp.BBS.Announcement.Params = getAnnoParams(region, lang);
|
||||
let url = `${AnnoApi}/getAnnContent`;
|
||||
if (region !== AnnoServer.CN_ISLAND && region !== AnnoServer.CN_TREE) {
|
||||
url = `${AnnoApiGlobal}/getAnnContent`;
|
||||
}
|
||||
const annoResp = await TGHttp<TGApp.BBS.Announcement.ContentResponse>(url, {
|
||||
method: "GET",
|
||||
query: params,
|
||||
});
|
||||
const annoResp = await TGHttp<TGApp.BBS.Announcement.ContentResponse>(
|
||||
`${getAnnoApi(region)}/getAnnContent`,
|
||||
{ method: "GET", query: getAnnoParams(region, lang) },
|
||||
);
|
||||
const annoContent = annoResp.data.list.find((item) => item.ann_id === annId);
|
||||
if (annoContent != null) {
|
||||
return annoContent;
|
||||
} else {
|
||||
if (annoContent === undefined) {
|
||||
throw new Error("公告内容不存在");
|
||||
}
|
||||
return annoContent;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,7 +119,6 @@ async function getGachaLog(
|
||||
gachaType: string,
|
||||
endId: string = "0",
|
||||
): Promise<TGApp.Game.Gacha.GachaItem[] | TGApp.BBS.Response.Base> {
|
||||
const url = "https://public-operation-hk4e.mihoyo.com/gacha_info/api/getGachaLog";
|
||||
const params = {
|
||||
lang: "zh-cn",
|
||||
auth_appid: "webview_gacha",
|
||||
@@ -126,19 +129,16 @@ async function getGachaLog(
|
||||
size: "20",
|
||||
end_id: endId,
|
||||
};
|
||||
const resp = await TGHttp<TGApp.Game.Gacha.GachaLogResponse | TGApp.BBS.Response.Base>(url, {
|
||||
method: "GET",
|
||||
query: params,
|
||||
});
|
||||
const resp = await TGHttp<TGApp.Game.Gacha.GachaLogResponse | TGApp.BBS.Response.Base>(
|
||||
"https://public-operation-hk4e.mihoyo.com/gacha_info/api/getGachaLog",
|
||||
{ method: "GET", query: params },
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data.list;
|
||||
}
|
||||
|
||||
const Hk4eApi = {
|
||||
anno: {
|
||||
list: getAnnoList,
|
||||
content: getAnnoContent,
|
||||
},
|
||||
anno: { list: getAnnoList, content: getAnnoContent },
|
||||
gacha: getGachaLog,
|
||||
};
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ async function getDeviceFp(
|
||||
Info?: TGApp.App.Device.DeviceInfo,
|
||||
): Promise<TGApp.App.Device.DeviceInfo> {
|
||||
const info = Info ?? getInitDeviceInfo();
|
||||
const deviceFPHeader = {
|
||||
const deviceFPHeader: Record<string, string | number> = {
|
||||
proxyStatus: 0,
|
||||
isRoot: 0,
|
||||
romCapacity: "512",
|
||||
@@ -74,8 +74,7 @@ async function getDeviceFp(
|
||||
hasKeyboard: 0,
|
||||
board: "taro",
|
||||
};
|
||||
const url = "https://public-data-api.mihoyo.com/device-fp/api/getFp";
|
||||
const data = {
|
||||
const data: Record<string, string> = {
|
||||
device_id: info.device_id,
|
||||
seed_id: info.seed_id,
|
||||
platform: "2",
|
||||
@@ -85,24 +84,20 @@ async function getDeviceFp(
|
||||
bbs_device_id: info.device_id,
|
||||
device_fp: info.device_fp,
|
||||
};
|
||||
const header = {
|
||||
"User-Agent": TGConstant.BBS.UA_MOBILE,
|
||||
const header: Record<string, string> = {
|
||||
"user-agent": TGConstant.BBS.UA_MOBILE,
|
||||
"x-rpc-app_version": TGConstant.BBS.VERSION,
|
||||
"x-rpc-client_type": "5",
|
||||
"x-requested-with": "com.mihoyo.hyperion",
|
||||
Referer: "https://webstatic.mihoyo.com/",
|
||||
};
|
||||
try {
|
||||
const resp = await TGHttp<TGApp.BBS.Response.getDeviceFp>(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
headers: header,
|
||||
});
|
||||
if (resp.retcode !== 0) {
|
||||
info.device_fp = "0000000000000";
|
||||
} else {
|
||||
info.device_fp = resp.data.device_fp;
|
||||
}
|
||||
const resp = await TGHttp<TGApp.BBS.Response.getDeviceFp>(
|
||||
"https://public-data-api.mihoyo.com/device-fp/api/getFp",
|
||||
{ method: "POST", body: JSON.stringify(data), headers: header },
|
||||
);
|
||||
if (resp.retcode !== 0) info.device_fp = "0000000000000";
|
||||
else info.device_fp = resp.data.device_fp;
|
||||
} catch (error) {
|
||||
info.device_fp = "0000000000000";
|
||||
await TGLogger.Error(`获取设备指纹失败: ${error}`);
|
||||
@@ -119,19 +114,14 @@ async function getDeviceFp(
|
||||
async function refreshCode(
|
||||
actId: string,
|
||||
): Promise<TGApp.BBS.Navigator.CodeData[] | TGApp.BBS.Response.Base> {
|
||||
const url = "https://api-takumi-static.mihoyo.com/event/miyolive/refreshCode";
|
||||
const header = { "x-rpc-act_id": actId };
|
||||
const res = await TGHttp<TGApp.BBS.Navigator.CodeResponse | TGApp.BBS.Response.Base>(url, {
|
||||
method: "GET",
|
||||
headers: header,
|
||||
});
|
||||
const res = await TGHttp<TGApp.BBS.Navigator.CodeResponse | TGApp.BBS.Response.Base>(
|
||||
"https://api-takumi-static.mihoyo.com/event/miyolive/refreshCode",
|
||||
{ method: "GET", headers: { "x-rpc-act_id": actId } },
|
||||
);
|
||||
if (res.retcode !== 0) return <TGApp.BBS.Response.Base>res;
|
||||
return res.data.code_list;
|
||||
}
|
||||
|
||||
const OtherApi = {
|
||||
code: refreshCode,
|
||||
fp: getDeviceFp,
|
||||
};
|
||||
const OtherApi = { code: refreshCode, fp: getDeviceFp };
|
||||
|
||||
export default OtherApi;
|
||||
|
||||
@@ -7,9 +7,9 @@ import TGHttp from "../../utils/TGHttp.js";
|
||||
import { getRequestHeader } from "../utils/getRequestHeader.js";
|
||||
|
||||
// PassportApiBaseUrl => pAbu
|
||||
const pAbu = "https://passport-api.mihoyo.com/";
|
||||
const pAbu: Readonly<string> = "https://passport-api.mihoyo.com/";
|
||||
// PassportV4ApiBaseUrl => p4Abu
|
||||
const p4Abu = "https://passport-api-v4.mihoyo.com/";
|
||||
const p4Abu: Readonly<string> = "https://passport-api-v4.mihoyo.com/";
|
||||
|
||||
/**
|
||||
* @description 获取登录ticket
|
||||
@@ -22,22 +22,20 @@ async function createAuthTicketByGameBiz(
|
||||
account: TGApp.Sqlite.Account.Game,
|
||||
cookie: TGApp.App.Account.Cookie,
|
||||
): Promise<TGApp.BBS.Response.Base | string> {
|
||||
const url = `${pAbu}account/ma-cn-verifier/app/createAuthTicketByGameBiz`;
|
||||
const params = {
|
||||
const params: Record<string, string> = {
|
||||
game_biz: account.gameBiz,
|
||||
stoken: cookie.stoken,
|
||||
uid: account.gameUid,
|
||||
mid: cookie.mid,
|
||||
};
|
||||
const header = {
|
||||
const headers: Record<string, string> = {
|
||||
"x-rpc-client_type": "3",
|
||||
"x-rpc-app_id": "ddxf5dufpuyo",
|
||||
};
|
||||
const resp = await TGHttp<TGApp.BBS.Response.getAuthTicketByGameBiz>(url, {
|
||||
method: "POST",
|
||||
headers: header,
|
||||
query: params,
|
||||
});
|
||||
const resp = await TGHttp<TGApp.BBS.Response.getAuthTicketByGameBiz>(
|
||||
`${pAbu}account/ma-cn-verifier/app/createAuthTicketByGameBiz`,
|
||||
{ method: "POST", headers: headers, query: params },
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data.ticket;
|
||||
}
|
||||
@@ -51,17 +49,11 @@ async function createAuthTicketByGameBiz(
|
||||
async function getCookieAccountInfoBySToken(
|
||||
cookie: TGApp.App.Account.Cookie,
|
||||
): Promise<string | TGApp.BBS.Response.Base> {
|
||||
const url = `${pAbu}account/auth/api/getCookieAccountInfoBySToken`;
|
||||
const ck = { stoken: cookie.stoken, mid: cookie.mid };
|
||||
const params = { stoken: cookie.stoken };
|
||||
const header = getRequestHeader(ck, "GET", params, "common");
|
||||
const resp = await TGHttp<TGApp.BBS.Response.getCookieTokenBySToken | TGApp.BBS.Response.Base>(
|
||||
url,
|
||||
{
|
||||
method: "GET",
|
||||
headers: header,
|
||||
query: params,
|
||||
},
|
||||
`${pAbu}account/auth/api/getCookieAccountInfoBySToken`,
|
||||
{ method: "GET", headers: getRequestHeader(ck, "GET", params), query: params },
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data.cookie_token;
|
||||
@@ -76,39 +68,30 @@ async function getCookieAccountInfoBySToken(
|
||||
async function getLTokenBySToken(
|
||||
cookie: TGApp.App.Account.Cookie,
|
||||
): Promise<string | TGApp.BBS.Response.Base> {
|
||||
const url = `${pAbu}account/auth/api/getLTokenBySToken`;
|
||||
const ck = { mid: cookie.mid, stoken: cookie.stoken };
|
||||
const params = { stoken: cookie.stoken };
|
||||
const header = getRequestHeader(ck, "GET", params, "common");
|
||||
const resp = await TGHttp<TGApp.BBS.Response.getLTokenBySToken | TGApp.BBS.Response.Base>(url, {
|
||||
method: "GET",
|
||||
headers: header,
|
||||
query: params,
|
||||
});
|
||||
const resp = await TGHttp<TGApp.BBS.Response.getLTokenBySToken | TGApp.BBS.Response.Base>(
|
||||
`${pAbu}account/auth/api/getLTokenBySToken`,
|
||||
{ method: "GET", headers: getRequestHeader(ck, "GET", params), query: params },
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data.ltoken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 验证 ltoken 有效性,返回 mid
|
||||
* @since Beta v0.5.0
|
||||
* @since Beta v0.6.5
|
||||
* @param {TGApp.App.Account.Cookie} cookie - 账户 cookie
|
||||
* @returns {Promise<string | TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
async function verifyLToken(
|
||||
cookie: TGApp.App.Account.Cookie,
|
||||
): Promise<string | TGApp.BBS.Response.Base> {
|
||||
const url = `${p4Abu}account/ma-cn-session/web/verifyLtoken`;
|
||||
const ck = { ltoken: cookie.ltoken, ltuid: cookie.ltuid };
|
||||
const data = { ltoken: cookie.ltoken };
|
||||
const header = getRequestHeader(ck, "POST", data, "common");
|
||||
const resp = await TGHttp<TGApp.BBS.Response.verifyUserInfoBySToken | TGApp.BBS.Response.Base>(
|
||||
url,
|
||||
{
|
||||
method: "POST",
|
||||
headers: header,
|
||||
body: JSON.stringify(data),
|
||||
},
|
||||
`${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;
|
||||
@@ -117,10 +100,7 @@ async function verifyLToken(
|
||||
const PassportApi = {
|
||||
authTicket: createAuthTicketByGameBiz,
|
||||
cookieToken: getCookieAccountInfoBySToken,
|
||||
lToken: {
|
||||
get: getLTokenBySToken,
|
||||
verify: verifyLToken,
|
||||
},
|
||||
lToken: { get: getLTokenBySToken, verify: verifyLToken },
|
||||
};
|
||||
|
||||
export default PassportApi;
|
||||
|
||||
@@ -8,7 +8,8 @@ import TGHttp from "../../utils/TGHttp.js";
|
||||
import { getRequestHeader } from "../utils/getRequestHeader.js";
|
||||
|
||||
// TakumiRecordGenshinApiBaseUrl => trgAbu
|
||||
const trgAbu = "https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/";
|
||||
const trgAbu: Readonly<string> =
|
||||
"https://api-takumi-record.mihoyo.com/game_record/app/genshin/api/";
|
||||
|
||||
/**
|
||||
* @description 获取角色详情
|
||||
@@ -23,15 +24,12 @@ async function characterDetail(
|
||||
user: TGApp.Sqlite.Account.Game,
|
||||
avatarIds: string[],
|
||||
): Promise<TGApp.Game.Avatar.AvatarDetail | TGApp.BBS.Response.Base> {
|
||||
const url = `${trgAbu}character/detail`;
|
||||
const ck = { account_id: cookie.account_id, cookie_token: cookie.cookie_token };
|
||||
const data = { role_id: user.gameUid, server: user.region, character_ids: avatarIds };
|
||||
const header = getRequestHeader(ck, "POST", data, "common");
|
||||
const resp = await TGHttp<TGApp.Game.Avatar.DetailResponse | TGApp.BBS.Response.Base>(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
headers: header,
|
||||
});
|
||||
const resp = await TGHttp<TGApp.Game.Avatar.DetailResponse | TGApp.BBS.Response.Base>(
|
||||
`${trgAbu}character/detail`,
|
||||
{ method: "POST", body: JSON.stringify(data), headers: getRequestHeader(ck, "POST", data) },
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data;
|
||||
}
|
||||
@@ -47,15 +45,12 @@ async function characterList(
|
||||
cookie: TGApp.App.Account.Cookie,
|
||||
user: TGApp.Sqlite.Account.Game,
|
||||
): Promise<TGApp.Game.Avatar.Avatar[] | TGApp.BBS.Response.Base> {
|
||||
const url = `${trgAbu}character/list`;
|
||||
const ck = { account_id: cookie.account_id, cookie_token: cookie.cookie_token };
|
||||
const data = { role_id: user.gameUid, server: user.region };
|
||||
const header = getRequestHeader(ck, "POST", data, "common");
|
||||
const resp = await TGHttp<TGApp.Game.Avatar.ListResponse | TGApp.BBS.Response.Base>(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
headers: header,
|
||||
});
|
||||
const resp = await TGHttp<TGApp.Game.Avatar.ListResponse | TGApp.BBS.Response.Base>(
|
||||
`${trgAbu}character/list`,
|
||||
{ method: "POST", body: JSON.stringify(data), headers: getRequestHeader(ck, "POST", data) },
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data.list;
|
||||
}
|
||||
@@ -73,15 +68,12 @@ async function index(
|
||||
user: TGApp.Sqlite.Account.Game,
|
||||
listType: number = 0,
|
||||
): Promise<TGApp.Game.Record.FullData | TGApp.BBS.Response.Base> {
|
||||
const url = `${trgAbu}index`;
|
||||
const ck = { account_id: cookie.account_id, cookie_token: cookie.cookie_token };
|
||||
const params = { avatar_list_type: listType, role_id: user.gameUid, server: user.region };
|
||||
const header = getRequestHeader(ck, "GET", params, "common");
|
||||
const resp = await TGHttp<TGApp.Game.Record.Response | TGApp.BBS.Response.Base>(url, {
|
||||
method: "GET",
|
||||
headers: header,
|
||||
query: params,
|
||||
});
|
||||
const resp = await TGHttp<TGApp.Game.Record.Response | TGApp.BBS.Response.Base>(
|
||||
`${trgAbu}index`,
|
||||
{ method: "GET", headers: getRequestHeader(ck, "GET", params), query: params },
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data;
|
||||
}
|
||||
@@ -97,7 +89,6 @@ async function roleCombat(
|
||||
cookie: TGApp.App.Account.Cookie,
|
||||
user: TGApp.Sqlite.Account.Game,
|
||||
): Promise<TGApp.Game.Combat.Combat[] | TGApp.BBS.Response.Base | false> {
|
||||
const url = `${trgAbu}role_combat`;
|
||||
const ck = {
|
||||
account_id: cookie.account_id,
|
||||
cookie_token: cookie.cookie_token,
|
||||
@@ -105,12 +96,10 @@ async function roleCombat(
|
||||
ltuid: cookie.ltuid,
|
||||
};
|
||||
const params = { role_id: user.gameUid, server: user.region, active: 1, need_detail: true };
|
||||
const header = getRequestHeader(ck, "GET", params, "common");
|
||||
const resp = await TGHttp<TGApp.Game.Combat.Response | TGApp.BBS.Response.Base>(url, {
|
||||
method: "GET",
|
||||
headers: header,
|
||||
query: params,
|
||||
});
|
||||
const resp = await TGHttp<TGApp.Game.Combat.Response | TGApp.BBS.Response.Base>(
|
||||
`${trgAbu}role_combat`,
|
||||
{ method: "GET", headers: getRequestHeader(ck, "GET", params), query: params },
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
if (!resp.data.is_unlock) return false;
|
||||
return resp.data.data;
|
||||
@@ -129,7 +118,6 @@ async function spiralAbyss(
|
||||
user: TGApp.Sqlite.Account.Game,
|
||||
schedule: string,
|
||||
): Promise<TGApp.Game.Abyss.FullData | TGApp.BBS.Response.Base> {
|
||||
const url = `${trgAbu}spiralAbyss`;
|
||||
const ck = {
|
||||
account_id: cookie.account_id,
|
||||
cookie_token: cookie.cookie_token,
|
||||
@@ -137,22 +125,17 @@ async function spiralAbyss(
|
||||
ltuid: cookie.ltuid,
|
||||
};
|
||||
const params = { role_id: user.gameUid, schedule_type: schedule, server: user.region };
|
||||
const header = getRequestHeader(ck, "GET", params, "common");
|
||||
const resp = await TGHttp<TGApp.Game.Abyss.Response | TGApp.BBS.Response.Base>(url, {
|
||||
method: "GET",
|
||||
headers: header,
|
||||
query: params,
|
||||
});
|
||||
const resp = await TGHttp<TGApp.Game.Abyss.Response | TGApp.BBS.Response.Base>(
|
||||
`${trgAbu}spiralAbyss`,
|
||||
{ method: "GET", headers: getRequestHeader(ck, "GET", params), query: params },
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
const TakumiRecordGenshinApi = {
|
||||
index: index,
|
||||
character: {
|
||||
list: characterList,
|
||||
detail: characterDetail,
|
||||
},
|
||||
character: { list: characterList, detail: characterDetail },
|
||||
roleCombat: roleCombat,
|
||||
spiralAbyss: spiralAbyss,
|
||||
};
|
||||
|
||||
@@ -4,13 +4,12 @@
|
||||
* @since Beta v0.6.3
|
||||
*/
|
||||
import TGHttp from "../../utils/TGHttp.js";
|
||||
import TGConstant from "../constant/TGConstant.js";
|
||||
import { getRequestHeader } from "../utils/getRequestHeader.js";
|
||||
|
||||
// TakumiAuthApiBaseUrl => taAbu
|
||||
const taAbu = "https://api-takumi.mihoyo.com/auth/api/";
|
||||
const taAbu: Readonly<string> = "https://api-takumi.mihoyo.com/auth/api/";
|
||||
// TakumiBingApiBaseUrl => tbAbu
|
||||
const tbAbu = "https://api-takumi.mihoyo.com/binding/api/";
|
||||
const tbAbu: Readonly<string> = "https://api-takumi.mihoyo.com/binding/api/";
|
||||
|
||||
/**
|
||||
* @description 根据stoken获取action_ticket
|
||||
@@ -25,15 +24,12 @@ async function getActionTicketBySToken(
|
||||
user: TGApp.Sqlite.Account.Game,
|
||||
actionType: string,
|
||||
): Promise<TGApp.BBS.Response.getActionTicketBySToken> {
|
||||
const url = `${taAbu}getActionTicketBySToken`;
|
||||
const ck = { stoken: cookie.stoken, mid: cookie.mid };
|
||||
const params = { action_type: actionType, stoken: cookie.stoken, uid: user.gameUid };
|
||||
const header = getRequestHeader(ck, "GET", params, "k2");
|
||||
return await TGHttp<TGApp.BBS.Response.getActionTicketBySToken>(url, {
|
||||
method: "GET",
|
||||
headers: header,
|
||||
query: params,
|
||||
});
|
||||
return await TGHttp<TGApp.BBS.Response.getActionTicketBySToken>(
|
||||
`${taAbu}getActionTicketBySToken`,
|
||||
{ method: "GET", headers: getRequestHeader(ck, "GET", params, "K2"), query: params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +43,6 @@ async function genAuthKey(
|
||||
cookie: TGApp.App.Account.Cookie,
|
||||
account: TGApp.Sqlite.Account.Game,
|
||||
): Promise<string | TGApp.BBS.Response.Base> {
|
||||
const url = `${tbAbu}genAuthKey`;
|
||||
const ck = { stoken: cookie.stoken, mid: cookie.mid };
|
||||
const data = {
|
||||
auth_appid: "webview_gacha",
|
||||
@@ -55,12 +50,14 @@ async function genAuthKey(
|
||||
game_uid: account.gameUid,
|
||||
region: account.region,
|
||||
};
|
||||
const header = getRequestHeader(ck, "POST", JSON.stringify(data), "lk2", true);
|
||||
const resp = await TGHttp<TGApp.Game.Gacha.AuthkeyResponse | TGApp.BBS.Response.Base>(url, {
|
||||
method: "POST",
|
||||
headers: header,
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
const resp = await TGHttp<TGApp.Game.Gacha.AuthkeyResponse | TGApp.BBS.Response.Base>(
|
||||
`${tbAbu}genAuthKey`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: getRequestHeader(ck, "POST", JSON.stringify(data), "LK2", true),
|
||||
body: JSON.stringify(data),
|
||||
},
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data.authkey;
|
||||
}
|
||||
@@ -76,46 +73,35 @@ async function genAuthKey2(
|
||||
cookie: Record<string, string>,
|
||||
payload: Record<string, string>,
|
||||
): Promise<TGApp.BBS.Response.Base> {
|
||||
const url = `${tbAbu}genAuthKey`;
|
||||
const header = getRequestHeader(cookie, "POST", JSON.stringify(payload), "lk2", true);
|
||||
return await TGHttp<TGApp.BBS.Response.Base>(url, {
|
||||
return await TGHttp<TGApp.BBS.Response.Base>(`${tbAbu}genAuthKey`, {
|
||||
method: "POST",
|
||||
headers: header,
|
||||
headers: getRequestHeader(cookie, "POST", JSON.stringify(payload), "LK2", true),
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 通过cookie获取游戏账号
|
||||
* @since Beta v0.6.3
|
||||
* @since Beta v0.6.5
|
||||
* @param {TGApp.App.Account.Cookie} cookie cookie
|
||||
* @returns {Promise<TGApp.BBS.Account.GameAccount[]|TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
async function getUserGameRolesByCookie(
|
||||
cookie: TGApp.App.Account.Cookie,
|
||||
): Promise<TGApp.BBS.Account.GameAccount[] | TGApp.BBS.Response.Base> {
|
||||
const url = `${tbAbu}getUserGameRolesByCookie`;
|
||||
const ck = { account_id: cookie.account_id, cookie_token: cookie.cookie_token };
|
||||
const params = { game_biz: TGConstant.GAME_BIZ };
|
||||
const header = getRequestHeader(ck, "GET", params, "common");
|
||||
const resp = await TGHttp<TGApp.BBS.Response.getGameAccounts | TGApp.BBS.Response.Base>(url, {
|
||||
method: "GET",
|
||||
headers: header,
|
||||
query: params,
|
||||
});
|
||||
const params = { game_biz: "hk4e_cn" };
|
||||
const resp = await TGHttp<TGApp.BBS.Response.getGameAccounts | TGApp.BBS.Response.Base>(
|
||||
`${tbAbu}getUserGameRolesByCookie`,
|
||||
{ method: "GET", headers: getRequestHeader(ck, "GET", params), query: params },
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data.list;
|
||||
}
|
||||
|
||||
const TakumiApi = {
|
||||
auth: {
|
||||
actionTicket: getActionTicketBySToken,
|
||||
},
|
||||
bind: {
|
||||
authKey: genAuthKey,
|
||||
authKey2: genAuthKey2,
|
||||
gameRoles: getUserGameRolesByCookie,
|
||||
},
|
||||
auth: { actionTicket: getActionTicketBySToken },
|
||||
bind: { authKey: genAuthKey, authKey2: genAuthKey2, gameRoles: getUserGameRolesByCookie },
|
||||
};
|
||||
|
||||
export default TakumiApi;
|
||||
|
||||
@@ -7,34 +7,35 @@
|
||||
import Md5 from "js-md5";
|
||||
|
||||
import { getDeviceInfo, getRandomString } from "../../utils/toolFunc.js";
|
||||
import TGConstant from "../constant/TGConstant.js";
|
||||
|
||||
import { transCookie, transParams } from "./tools.js";
|
||||
|
||||
type SaltType = "common" | "prod" | "lk2" | "k2";
|
||||
import { BBS_VERSION } from "../constant/bbs.js";
|
||||
|
||||
/**
|
||||
* @description 获取 salt
|
||||
* @since Beta v0.6.3
|
||||
* @version 2.59.1
|
||||
* @param {SaltType} saltType salt 类型
|
||||
* @returns {string} salt
|
||||
* @description salt 类型
|
||||
* @since Beta v0.6.5
|
||||
* @enum {number}
|
||||
*/
|
||||
function getSalt(saltType: SaltType): string {
|
||||
switch (saltType) {
|
||||
case "common":
|
||||
return TGConstant.Salt.X4;
|
||||
case "prod":
|
||||
return TGConstant.Salt.PROD;
|
||||
case "lk2":
|
||||
return TGConstant.Salt.LK2;
|
||||
case "k2":
|
||||
return TGConstant.Salt.K2;
|
||||
default:
|
||||
return TGConstant.Salt.X4;
|
||||
}
|
||||
const enum SaltType {
|
||||
K2,
|
||||
LK2,
|
||||
X4,
|
||||
X6,
|
||||
PROD,
|
||||
}
|
||||
|
||||
/**
|
||||
* @description salt 值
|
||||
* @version 2.78.1
|
||||
* @since Beta v0.6.5
|
||||
*/
|
||||
const Salt: Readonly<Record<keyof typeof SaltType, string>> = {
|
||||
K2: "GuODIETRPuJxpiUQoZairQxHtmzZKYFl",
|
||||
LK2: "ACDpsiiEFSqqLiEpzXMuXNsLNqGkrIQc",
|
||||
X4: "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs",
|
||||
X6: "t0qEgfub6cvueAPgR5m9aQWWVciEer7v",
|
||||
PROD: "t0qEgfub6cvueAPgR5m9aQWWVciEer7v",
|
||||
};
|
||||
const UserAgent: Readonly<string> = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) miHoYoBBS/${BBS_VERSION}`;
|
||||
|
||||
/**
|
||||
* @description 获取随机数
|
||||
* @since Alpha v0.2.0
|
||||
@@ -56,8 +57,13 @@ function getRandomNumber(min: number, max: number): number {
|
||||
* @param {boolean} isSign 是否为签名
|
||||
* @returns {string} ds
|
||||
*/
|
||||
function getDS(method: string, data: string, saltType: SaltType, isSign: boolean): string {
|
||||
const salt = getSalt(saltType);
|
||||
function getDS(
|
||||
method: string,
|
||||
data: string,
|
||||
saltType: keyof typeof SaltType,
|
||||
isSign: boolean,
|
||||
): string {
|
||||
const salt = Salt[saltType];
|
||||
const time = Math.floor(Date.now() / 1000).toString();
|
||||
let random = getRandomNumber(100000, 200000).toString();
|
||||
if (isSign) random = getRandomString(6);
|
||||
@@ -69,38 +75,64 @@ function getDS(method: string, data: string, saltType: SaltType, isSign: boolean
|
||||
return `${time},${random},${md5Str}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description ds 算法需要数据转换后的字符串是按照字典序排序的
|
||||
* @since Beta v0.6.5
|
||||
* @param { Record<string, string | number | boolean | Array<string>> | string} obj object
|
||||
* @returns {string} query string
|
||||
*/
|
||||
function transParams(
|
||||
obj: Record<string, string | number | boolean | Array<string>> | string,
|
||||
): string {
|
||||
if (typeof obj === "string") return obj;
|
||||
let res = "";
|
||||
const keys = Object.keys(obj).sort();
|
||||
for (const key of keys) {
|
||||
res += `${key}=${obj[key].toString()}&`;
|
||||
}
|
||||
return res.slice(0, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 将 cookie 对象转换为字符串
|
||||
* @since Alpha v0.1.5
|
||||
* @param {Record<string, string>} cookie cookie
|
||||
* @returns {string} 转换后的 cookie
|
||||
*/
|
||||
function transCookie(cookie: Record<string, string>): string {
|
||||
let res = "";
|
||||
for (const key of Object.keys(cookie).sort()) {
|
||||
res += `${key}=${cookie[key]};`;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取请求头
|
||||
* @since Beta v0.6.2
|
||||
* @since Beta v0.6.5
|
||||
* @param {Record<string, string>} cookie cookie
|
||||
* @param {string} method 请求方法
|
||||
* @param {Record<string, string|number|string[]|boolean>|string} data 请求数据
|
||||
* @param {SaltType} saltType salt 类型
|
||||
* @param {Record<string, string | number | boolean | Array<string>> | string} data 请求数据
|
||||
* @param {keyof typeof SaltType} saltType salt 类型
|
||||
* @param {boolean} isSign 是否为签名
|
||||
* @returns {Record<string, string>} 请求头
|
||||
*/
|
||||
export function getRequestHeader(
|
||||
cookie: Record<string, string>,
|
||||
method: string,
|
||||
data: Record<string, string | number | string[] | boolean> | string,
|
||||
saltType: SaltType,
|
||||
data: Record<string, string | number | boolean | Array<string>> | string,
|
||||
saltType: keyof typeof SaltType = "X4",
|
||||
isSign: boolean = false,
|
||||
): Record<string, string> {
|
||||
let ds;
|
||||
if (typeof data === "string") {
|
||||
ds = getDS(method, data, saltType, isSign);
|
||||
} else {
|
||||
ds = getDS(method, transParams(data), saltType, isSign);
|
||||
}
|
||||
return {
|
||||
"user-agent": TGConstant.BBS.UA_PC,
|
||||
"x-rpc-app_version": TGConstant.BBS.VERSION,
|
||||
"user-agent": UserAgent,
|
||||
"x-rpc-app_version": BBS_VERSION,
|
||||
"x-rpc-client_type": "5",
|
||||
"x-requested-with": "com.mihoyo.hyperion",
|
||||
referer: "https://webstatic.mihoyo.com",
|
||||
"x-rpc-device_id": getDeviceInfo("device_id"),
|
||||
"x-rpc-device_fp": getDeviceInfo("device_fp"),
|
||||
ds,
|
||||
ds: getDS(method, transParams(data), saltType, isSign),
|
||||
cookie: transCookie(cookie),
|
||||
};
|
||||
}
|
||||
@@ -108,26 +140,31 @@ export function getRequestHeader(
|
||||
/**
|
||||
* @description 获取 DS
|
||||
* @since Beta v0.3.9
|
||||
* @param {SaltType} saltType salt 类型
|
||||
* @param {keyof typeof SaltType} saltType salt 类型
|
||||
* @param {number} dsType ds 类型
|
||||
* @param {Record<string, string|number>|string} body
|
||||
* @param {Record<string, string|number>|string} query
|
||||
* @returns {string} DS
|
||||
*/
|
||||
export function getDS4JS(saltType: SaltType, dsType: 1, body: undefined, query: undefined): string;
|
||||
export function getDS4JS(
|
||||
saltType: SaltType,
|
||||
saltType: keyof typeof SaltType,
|
||||
dsType: 1,
|
||||
body: undefined,
|
||||
query: undefined,
|
||||
): string;
|
||||
export function getDS4JS(
|
||||
saltType: keyof typeof SaltType,
|
||||
dsType: 2,
|
||||
body: Record<string, string | number> | string,
|
||||
query: Record<string, string | number> | string,
|
||||
): string;
|
||||
export function getDS4JS(
|
||||
saltType: SaltType,
|
||||
saltType: keyof typeof SaltType,
|
||||
dsType: 1 | 2,
|
||||
body?: Record<string, string | number> | string,
|
||||
query?: Record<string, string | number> | string,
|
||||
): string {
|
||||
const salt = getSalt(saltType);
|
||||
const salt = Salt[saltType];
|
||||
const time = Math.floor(Date.now() / 1000).toString();
|
||||
let random = getRandomNumber(100000, 200000).toString();
|
||||
let hashStr: string;
|
||||
|
||||
@@ -26,35 +26,6 @@ export function decodeRegExp(data: string): string {
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 将 cookie 对象转换为字符串
|
||||
* @since Alpha v0.1.5
|
||||
* @param {Record<string, string>} cookie cookie
|
||||
* @returns {string} 转换后的 cookie
|
||||
*/
|
||||
export function transCookie(cookie: Record<string, string>): string {
|
||||
let res = "";
|
||||
for (const key of Object.keys(cookie).sort()) {
|
||||
res += `${key}=${cookie[key]};`;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description ds 算法需要数据转换后的字符串是按照字典序排序的
|
||||
* @since Beta v0.6.3
|
||||
* @param {Record<string, string|number>} obj object
|
||||
* @returns {string} query string
|
||||
*/
|
||||
export function transParams(obj: Record<string, string | number | string[] | boolean>): string {
|
||||
let res = "";
|
||||
const keys = Object.keys(obj).sort();
|
||||
for (const key of keys) {
|
||||
res += `${key}=${obj[key].toString()}&`;
|
||||
}
|
||||
return res.slice(0, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 根据 gid 获取游戏名称
|
||||
* @param {number} gid
|
||||
|
||||
Reference in New Issue
Block a user