s3si.ts/splatnet3.ts

124 lines
3.3 KiB
TypeScript

import { getWebViewVer } from "./iksm.ts";
import { State } from "./state.ts";
import { DEFAULT_APP_USER_AGENT, SPLATNET3_ENDPOINT } from "./constant.ts";
import { APIError } from "./APIError.ts";
import {
BattleListType,
GraphQLResponse,
HistoryGroups,
Queries,
RespMap,
VarsMap,
} from "./types.ts";
async function request<Q extends Queries>(
state: State,
query: Q,
...rest: VarsMap[Q]
): Promise<RespMap[Q]> {
const variables = rest?.[0] ?? {};
const body = {
extensions: {
persistedQuery: {
sha256Hash: query,
version: 1,
},
},
variables,
};
const resp = await fetch(SPLATNET3_ENDPOINT, {
method: "POST",
headers: {
"Authorization": `Bearer ${state.loginState?.bulletToken}`,
"Accept-Language": state.userLang ?? "en-US",
"User-Agent": state.appUserAgent ?? DEFAULT_APP_USER_AGENT,
"X-Web-View-Ver": await getWebViewVer(),
"Content-Type": "application/json",
"Accept": "*/*",
"Origin": "https://api.lp1.av5ja.srv.nintendo.net",
"X-Requested-With": "com.nintendo.znca",
"Referer":
`https://api.lp1.av5ja.srv.nintendo.net/?lang=${state.userLang}&na_country=${state.userCountry}&na_lang=${state.userLang}`,
"Accept-Encoding": "gzip, deflate",
"Cookie": `_gtoken: ${state.loginState?.gToken}`,
},
body: JSON.stringify(body),
});
if (resp.status !== 200) {
throw new APIError({
response: resp,
message: "Splatnet3 request failed",
});
}
const json: GraphQLResponse<RespMap[Q]> = await resp.json();
if ("errors" in json) {
throw new APIError({
response: resp,
json,
message: `Splatnet3 request failed(${json.errors?.[0].message})`,
});
}
return json.data;
}
export async function checkToken(state: State) {
if (
!state.loginState?.sessionToken || !state.loginState?.bulletToken ||
!state.loginState?.gToken
) {
return false;
}
try {
await request(state, Queries.HomeQuery);
return true;
} catch (_e) {
return false;
}
}
function getIdsFromGroups({ historyGroups }: { historyGroups: HistoryGroups }) {
return historyGroups.nodes.flatMap((i) => i.historyDetails.nodes).map((i) =>
i.id
);
}
const BATTLE_LIST_TYPE_MAP: Record<
BattleListType,
(state: State) => Promise<string[]>
> = {
[BattleListType.Latest]: (state: State) =>
request(state, Queries.LatestBattleHistoriesQuery)
.then((r) => getIdsFromGroups(r.latestBattleHistories)),
[BattleListType.Regular]: (state: State) =>
request(state, Queries.RegularBattleHistoriesQuery)
.then((r) => getIdsFromGroups(r.regularBattleHistories)),
[BattleListType.Bankara]: (state: State) =>
request(state, Queries.BankaraBattleHistoriesQuery)
.then((r) => getIdsFromGroups(r.bankaraBattleHistories)),
[BattleListType.Private]: (state: State) =>
request(state, Queries.PrivateBattleHistoriesQuery)
.then((r) => getIdsFromGroups(r.privateBattleHistories)),
};
export async function getBattleList(
state: State,
battleListType: BattleListType = BattleListType.Latest,
) {
return await BATTLE_LIST_TYPE_MAP[battleListType](state);
}
export function getBattleDetail(
state: State,
id: string,
) {
return request(
state,
Queries.VsHistoryDetailQuery,
{
vsResultId: id,
},
);
}