fix(plugins/community/splatoon): upgrade s3si version

This commit is contained in:
Simon Lecoq committed 2023-07-24 21:37:34 -04:00
1 parent 1a95c68e69
commit cea9e4307e
2 files changed
+280 -90

No files matched your search

+1 -1
View File
@@ -216,7 +216,7 @@ export default async function({login, q, imports, data, account}, {enabled = fal
icon: await imports.imgb64(assets.stages[data.detail.coopStage.name]),
},
date: data.detail.playedTime,
grade: data.detail.afterGrade.name,
grade: data.detail.afterGrade?.name ?? "",
})),
),
}
+279 -89
View File
@@ -12,9 +12,26 @@ class APIError extends Error {
}
}
const AGENT_NAME = "s3si.ts";
const S3SI_VERSION = "0.3.1";
const NSOAPP_VERSION = "2.5.0";
const WEB_VIEW_VERSION = "3.0.0-2857bc50";
const S3SI_VERSION = "0.4.5";
const NSOAPP_VERSION = "2.6.0";
const WEB_VIEW_VERSION = "4.0.0-d5178440";
var Queries;
(function(Queries) {
Queries["HomeQuery"] = "7dcc64ea27a08e70919893a0d3f70871";
Queries["LatestBattleHistoriesQuery"] = "0d90c7576f1916469b2ae69f64292c02";
Queries["RegularBattleHistoriesQuery"] = "3baef04b095ad8975ea679d722bc17de";
Queries["BankaraBattleHistoriesQuery"] = "0438ea6978ae8bd77c5d1250f4f84803";
Queries["XBattleHistoriesQuery"] = "6796e3cd5dc3ebd51864dc709d899fc5";
Queries["EventBattleHistoriesQuery"] = "9744fcf676441873c7c8a51285b6aa4d";
Queries["PrivateBattleHistoriesQuery"] = "8e5ae78b194264a6c230e262d069bd28";
Queries["VsHistoryDetailQuery"] = "9ee0099fbe3d8db2a838a75cf42856dd";
Queries["CoopHistoryQuery"] = "91b917becd2fa415890f5b47e15ffb15";
Queries["CoopHistoryDetailQuery"] = "379f0d9b78b531be53044bcac031b34b";
Queries["myOutfitCommonDataFilteringConditionQuery"] = "d02ab22c9dccc440076055c8baa0fa7a";
Queries["myOutfitCommonDataEquipmentsQuery"] = "d29cd0c2b5e6bac90dd5b817914832f8";
Queries["HistoryRecordQuery"] = "d9246baf077b2a29b5f7aac321810a77";
Queries["ConfigureAnalyticsQuery"] = "f8ae00773cc412a50dd41a6d9a159ddd";
})(Queries || (Queries = {}));
const S3SI_LINK = "https://github.com/spacemeowx2/s3si.ts";
const USERAGENT = `${AGENT_NAME}/${S3SI_VERSION} (${S3SI_LINK})`;
const DEFAULT_APP_USER_AGENT = "Mozilla/5.0 (Linux; Android 11; Pixel 5) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/94.0.4606.61 Mobile Safari/537.36";
@@ -1243,13 +1260,13 @@ const MIN_BUF_SIZE = 16;
const CR = "\r".charCodeAt(0);
const LF = "\n".charCodeAt(0);
class BufferFullError extends Error {
partial;
name;
constructor(partial){
super("Buffer full");
this.partial = partial;
this.name = "BufferFullError";
}
partial;
}
class PartialReadError extends Error {
name = "PartialReadError";
@@ -1753,6 +1770,8 @@ class MultiReader {
}
}
class LimitedReader {
reader;
limit;
constructor(reader, limit){
this.reader = reader;
this.limit = limit;
@@ -1771,8 +1790,6 @@ class LimitedReader {
this.limit -= n;
return n;
}
reader;
limit;
}
const DEFAULT_BUFFER_SIZE = 32 * 1024;
async function copyN(r, dest, size) {
@@ -1836,6 +1853,7 @@ function sliceLongToBytes(d, dest = Array.from({
}
const decoder = new TextDecoder();
class StringWriter {
base;
#chunks;
#byteLength;
#cache;
@@ -1869,7 +1887,6 @@ class StringWriter {
this.#cache = decoder.decode(buf);
return this.#cache;
}
base;
}
const mod2 = {
copyN: copyN,
@@ -2144,12 +2161,12 @@ function utf8DecodeTD(bytes, inputOffset, byteLength) {
return sharedTextDecoder.decode(stringBytes);
}
class ExtData {
type;
data;
constructor(type, data){
this.type = type;
this.data = data;
}
type;
data;
}
function setUint64(view, offset, value) {
const high = value / 0x1_0000_0000;
@@ -2336,6 +2353,13 @@ function createDataView(buffer) {
return new DataView(bufferView.buffer, bufferView.byteOffset, bufferView.byteLength);
}
class Encoder {
extensionCodec;
context;
maxDepth;
initialBufferSize;
sortKeys;
forceFloat32;
ignoreUndefined;
pos;
view;
bytes;
@@ -2645,13 +2669,6 @@ class Encoder {
setInt64(this.view, this.pos, value);
this.pos += 8;
}
extensionCodec;
context;
maxDepth;
initialBufferSize;
sortKeys;
forceFloat32;
ignoreUndefined;
}
const defaultEncodeOptions = {};
function encode1(value, options = defaultEncodeOptions) {
@@ -2662,6 +2679,8 @@ function prettyByte(__byte) {
return `${__byte < 0 ? "-" : ""}0x${Math.abs(__byte).toString(16).padStart(2, "0")}`;
}
class CachedKeyDecoder {
maxKeyLength;
maxLengthPerKey;
hit;
miss;
caches;
@@ -2717,8 +2736,6 @@ class CachedKeyDecoder {
this.store(slicedCopyOfBytes, value);
return value;
}
maxKeyLength;
maxLengthPerKey;
}
var State;
(function(State) {
@@ -2744,6 +2761,14 @@ const DataViewIndexOutOfBoundsError = (()=>{
const MORE_DATA = new DataViewIndexOutOfBoundsError("Insufficient data");
const sharedCachedKeyDecoder = new CachedKeyDecoder();
class Decoder {
extensionCodec;
context;
maxStrLength;
maxBinLength;
maxArrayLength;
maxMapLength;
maxExtLength;
keyDecoder;
totalPos;
pos;
view;
@@ -3191,14 +3216,6 @@ class Decoder {
this.pos += 8;
return value;
}
extensionCodec;
context;
maxStrLength;
maxBinLength;
maxArrayLength;
maxMapLength;
maxExtLength;
keyDecoder;
}
const defaultDecodeOptions = {};
function decode1(buffer, options = defaultDecodeOptions) {
@@ -4449,7 +4466,7 @@ function globToRegExp(glob, { extended =true , globstar: globstarOption = true ,
else if (value == "lower") segment += "a-z";
else if (value == "print") segment += "\x20-\x7E";
else if (value == "punct") {
segment += "!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_ÔÇÿ{|}~";
segment += "!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_‘{|}~";
} else if (value == "space") segment += "\\s\v";
else if (value == "upper") segment += "A-Z";
else if (value == "word") segment += "\\w";
@@ -5072,6 +5089,11 @@ function urlSimplify(url) {
return url;
}
}
const battleTime = (id)=>{
const { timestamp } = parseHistoryDetailId(id);
const dateStr = timestamp.replace(/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})/, "$1-$2-$3T$4:$5:$6Z");
return new Date(dateStr);
};
async function loginSteps({ newFetcher }, step2) {
const fetch = newFetcher();
if (!step2) {
@@ -5182,12 +5204,13 @@ async function getGToken({ fApi , sessionToken , env }) {
}
});
const uiRespJson = await uiResp.json();
const { nickname , birthday , language , country } = uiRespJson;
const { nickname , birthday , language , country , id: userId } = uiRespJson;
const getIdToken2 = async (idToken)=>{
const { f , request_id: requestId , timestamp } = await callImink({
fApi,
step: 1,
idToken,
userId,
env
});
const resp = await fetch.post({
@@ -5214,20 +5237,26 @@ async function getGToken({ fApi , sessionToken , env }) {
});
const respJson = await resp.json();
const idToken2 = respJson?.result?.webApiServerCredential?.accessToken;
if (!idToken2) {
const coralUserId = respJson?.result?.user?.id?.toString();
if (!idToken2 || !coralUserId) {
throw new APIError({
response: resp,
json: respJson,
message: "No idToken2 found"
message: `No idToken2 or coralUserId found. Please try again later. (${idToken2.length}, ${coralUserId.length})`
});
}
return idToken2;
return [
idToken2,
coralUserId
];
};
const getGToken = async (idToken)=>{
const getGToken = async (idToken, coralUserId)=>{
const { f , request_id: requestId , timestamp } = await callImink({
step: 2,
idToken,
fApi,
userId,
coralUserId,
env
});
const resp = await fetch.post({
@@ -5261,8 +5290,8 @@ async function getGToken({ fApi , sessionToken , env }) {
}
return webServiceToken;
};
const idToken2 = await retry(()=>getIdToken2(idToken));
const webServiceToken = await retry(()=>getGToken(idToken2));
const [idToken2, coralUserId] = await retry(()=>getIdToken2(idToken));
const webServiceToken = await retry(()=>getGToken(idToken2, coralUserId));
return {
webServiceToken,
nickname,
@@ -5359,7 +5388,8 @@ async function getSessionToken({ fetch , sessionTokenCode , authCodeVerifier })
}
return json["session_token"];
}
async function callImink({ fApi , step , idToken , env }) {
async function callImink(params) {
const { fApi , step , idToken , userId , coralUserId , env } = params;
const { post } = env.newFetcher();
const resp = await post({
url: fApi,
@@ -5369,7 +5399,9 @@ async function callImink({ fApi , step , idToken , env }) {
},
body: JSON.stringify({
"token": idToken,
"hash_method": step
"hash_method": step,
"na_id": userId,
"coral_user_id": coralUserId
})
});
return await resp.json();
@@ -5422,6 +5454,7 @@ const DEFAULT_STATE = {
monitorInterval: 500
};
class FileStateBackend {
path;
constructor(path){
this.path = path;
}
@@ -5436,7 +5469,6 @@ class FileStateBackend {
await Deno.writeTextFile(swapPath, data);
await Deno.rename(swapPath, this.path);
}
path;
}
class Profile {
_state;
@@ -5469,29 +5501,15 @@ class Profile {
}
}
}
var Queries;
(function(Queries) {
Queries["HomeQuery"] = "22e2fa8294168003c21b00c333c35384";
Queries["LatestBattleHistoriesQuery"] = "4f5f26e64bca394b45345a65a2f383bd";
Queries["RegularBattleHistoriesQuery"] = "d5b795d09e67ce153e622a184b7e7dfa";
Queries["BankaraBattleHistoriesQuery"] = "de4754588109b77dbcb90fbe44b612ee";
Queries["XBattleHistoriesQuery"] = "45c74fefb45a49073207229ca65f0a62";
Queries["PrivateBattleHistoriesQuery"] = "1d6ed57dc8b801863126ad4f351dfb9a";
Queries["VsHistoryDetailQuery"] = "291295ad311b99a6288fc95a5c4cb2d2";
Queries["CoopHistoryQuery"] = "6ed02537e4a65bbb5e7f4f23092f6154";
Queries["CoopHistoryDetailQuery"] = "379f0d9b78b531be53044bcac031b34b";
Queries["myOutfitCommonDataFilteringConditionQuery"] = "d02ab22c9dccc440076055c8baa0fa7a";
Queries["myOutfitCommonDataEquipmentsQuery"] = "d29cd0c2b5e6bac90dd5b817914832f8";
Queries["HistoryRecordQuery"] = "32b6771f94083d8f04848109b7300af5";
Queries["ConfigureAnalyticsQuery"] = "f8ae00773cc412a50dd41a6d9a159ddd";
})(Queries || (Queries = {}));
var BattleListType;
(function(BattleListType) {
BattleListType[BattleListType["Latest"] = 0] = "Latest";
BattleListType[BattleListType["Regular"] = 1] = "Regular";
BattleListType[BattleListType["Bankara"] = 2] = "Bankara";
BattleListType[BattleListType["Private"] = 3] = "Private";
BattleListType[BattleListType["Coop"] = 4] = "Coop";
BattleListType[BattleListType["Event"] = 3] = "Event";
BattleListType[BattleListType["XBattle"] = 4] = "XBattle";
BattleListType[BattleListType["Private"] = 5] = "Private";
BattleListType[BattleListType["Coop"] = 6] = "Coop";
})(BattleListType || (BattleListType = {}));
var SummaryEnum;
(function(SummaryEnum) {
@@ -5596,6 +5614,8 @@ class Splatnet3 {
[BattleListType.Latest]: ()=>this.request(Queries.LatestBattleHistoriesQuery).then((r)=>getIdsFromGroups(r.latestBattleHistories)),
[BattleListType.Regular]: ()=>this.request(Queries.RegularBattleHistoriesQuery).then((r)=>getIdsFromGroups(r.regularBattleHistories)),
[BattleListType.Bankara]: ()=>this.request(Queries.BankaraBattleHistoriesQuery).then((r)=>getIdsFromGroups(r.bankaraBattleHistories)),
[BattleListType.XBattle]: ()=>this.request(Queries.XBattleHistoriesQuery).then((r)=>getIdsFromGroups(r.xBattleHistories)),
[BattleListType.Event]: ()=>this.request(Queries.EventBattleHistoriesQuery).then((r)=>getIdsFromGroups(r.eventBattleHistories)),
[BattleListType.Private]: ()=>this.request(Queries.PrivateBattleHistoriesQuery).then((r)=>getIdsFromGroups(r.privateBattleHistories)),
[BattleListType.Coop]: ()=>this.request(Queries.CoopHistoryQuery).then((r)=>getIdsFromGroups(r.coopResult))
};
@@ -5614,6 +5634,24 @@ class Splatnet3 {
async getBattleList(battleListType = BattleListType.Latest) {
return await this.BATTLE_LIST_TYPE_MAP[battleListType]();
}
async getAllBattleList() {
const ALL_TYPE = [
BattleListType.Regular,
BattleListType.Bankara,
BattleListType.XBattle,
BattleListType.Event,
BattleListType.Private
];
const ids = [];
for (const type of ALL_TYPE){
ids.push(...await this.getBattleList(type));
}
const timeMap = new Map(ids.map((id)=>[
id,
battleTime(id)
]));
return ids.sort((a, b)=>timeMap.get(b).getTime() - timeMap.get(a).getTime());
}
getBattleDetail(id) {
return this.request(Queries.VsHistoryDetailQuery, {
vsResultId: id
@@ -5689,6 +5727,7 @@ class MemoryCache {
}
}
class FileCache {
path;
constructor(path){
this.path = path;
}
@@ -5720,8 +5759,8 @@ class FileCache {
await Deno.writeFile(swapPath, data);
await Deno.rename(swapPath, path);
}
path;
}
const __default = JSON.parse("[\n {\n \"key\": \"ink_saver_main\",\n \"name\": {\n \"de-DE\": \"Hauptverbrauch\",\n \"en-GB\": \"Ink Saver (Main)\",\n \"en-US\": \"Ink Saver (Main)\",\n \"es-ES\": \"Tintahorro (ppal.)\",\n \"es-MX\": \"Ahorro tinta (ppal.)\",\n \"fr-CA\": \"Encrémenteur (pr.)\",\n \"fr-FR\": \"Encrémenteur (pr.)\",\n \"it-IT\": \"Eco-colore princ.\",\n \"ja-JP\": \"インク効率アップ(メイン)\",\n \"ko-KR\": \"잉크 효율 업(메인)\",\n \"nl-NL\": \"Hoofdspaarder\",\n \"ru-RU\": \"Основной баллон X\",\n \"zh-CN\": \"提升墨汁效率(主要武器)\",\n \"zh-TW\": \"提升墨汁效率(主要武器)\"\n },\n \"primary_only\": false\n },\n {\n \"key\": \"ink_saver_sub\",\n \"name\": {\n \"de-DE\": \"Sekundärverbrauch\",\n \"en-GB\": \"Ink Saver (Sub)\",\n \"en-US\": \"Ink Saver (Sub)\",\n \"es-ES\": \"Tintahorro (sec.)\",\n \"es-MX\": \"Ahorro tinta (sec.)\",\n \"fr-CA\": \"Encrémenteur (sec.)\",\n \"fr-FR\": \"Encrémenteur (sec.)\",\n \"it-IT\": \"Eco-colore second.\",\n \"ja-JP\": \"インク効率アップ(サブ)\",\n \"ko-KR\": \"잉크 효율 업(서브)\",\n \"nl-NL\": \"Subspaarder\",\n \"ru-RU\": \"Запасной баллон X\",\n \"zh-CN\": \"提升墨汁效率(次要武器)\",\n \"zh-TW\": \"提升墨汁效率(次要武器)\"\n },\n \"primary_only\": false\n },\n {\n \"key\": \"ink_recovery_up\",\n \"name\": {\n \"de-DE\": \"Regeneration +\",\n \"en-GB\": \"Ink Recovery Up\",\n \"en-US\": \"Ink Recovery Up\",\n \"es-ES\": \"Recarga rápida\",\n \"es-MX\": \"Mejor recarga tinta\",\n \"fr-CA\": \"Levée d'encre\",\n \"fr-FR\": \"Levée d'encre\",\n \"it-IT\": \"Recupero colore +\",\n \"ja-JP\": \"インク回復力アップ\",\n \"ko-KR\": \"잉크 회복력 업\",\n \"nl-NL\": \"Inktvulling\",\n \"ru-RU\": \"Быстрый баллон\",\n \"zh-CN\": \"提升墨汁回复力\",\n \"zh-TW\": \"提升墨汁回復力\"\n },\n \"primary_only\": false\n },\n {\n \"key\": \"run_speed_up\",\n \"name\": {\n \"de-DE\": \"Lauftempo +\",\n \"en-GB\": \"Run Speed Up\",\n \"en-US\": \"Run Speed Up\",\n \"es-ES\": \"Supercarrera\",\n \"es-MX\": \"Carrera acelerada\",\n \"fr-CA\": \"Course à pied\",\n \"fr-FR\": \"Course à pied\",\n \"it-IT\": \"Velocità +\",\n \"ja-JP\": \"ヒト移動速度アップ\",\n \"ko-KR\": \"인간 이동 속도 업\",\n \"nl-NL\": \"Hardloper\",\n \"ru-RU\": \"Спринтер\",\n \"zh-CN\": \"提升人类移动速度\",\n \"zh-TW\": \"提升人類移動速度\"\n },\n \"primary_only\": false\n },\n {\n \"key\": \"swim_speed_up\",\n \"name\": {\n \"de-DE\": \"Schwimmtempo +\",\n \"en-GB\": \"Swim Speed Up\",\n \"en-US\": \"Swim Speed Up\",\n \"es-ES\": \"Superbuceo\",\n \"es-MX\": \"Nado acelerado\",\n \"fr-CA\": \"Turbo-calmar\",\n \"fr-FR\": \"Turbo-calamar\",\n \"it-IT\": \"Velocità nuoto +\",\n \"ja-JP\": \"イカダッシュ速度アップ\",\n \"ko-KR\": \"징어대시 속도 업\",\n \"nl-NL\": \"Zwemdiploma\",\n \"ru-RU\": \"Плавунец\",\n \"zh-CN\": \"提升鱿鱼冲刺速度\",\n \"zh-TW\": \"提升魷魚衝刺速度\"\n },\n \"primary_only\": false\n },\n {\n \"key\": \"special_charge_up\",\n \"name\": {\n \"de-DE\": \"Spezialladezeit +\",\n \"en-GB\": \"Special Charge Up\",\n \"en-US\": \"Special Charge Up\",\n \"es-ES\": \"Recarga especial\",\n \"es-MX\": \"Recarga especial\",\n \"fr-CA\": \"Jauge spéciale +\",\n \"fr-FR\": \"Jauge spéciale +\",\n \"it-IT\": \"Ricarica speciale +\",\n \"ja-JP\": \"スペシャル増加量アップ\",\n \"ko-KR\": \"스페셜 증가량 업\",\n \"nl-NL\": \"Speciaallader\",\n \"ru-RU\": \"Особый насос\",\n \"zh-CN\": \"提升特殊武器增加量\",\n \"zh-TW\": \"提升特殊武器增加量\"\n },\n \"primary_only\": false\n },\n {\n \"key\": \"special_saver\",\n \"name\": {\n \"de-DE\": \"Spezialabzug -\",\n \"en-GB\": \"Special Saver\",\n \"en-US\": \"Special Saver\",\n \"es-ES\": \"Reducción especial\",\n \"es-MX\": \"Ahorro especial\",\n \"fr-CA\": \"Baisse spéciale -\",\n \"fr-FR\": \"Baisse spéciale -\",\n \"it-IT\": \"Riduzione speciale -\",\n \"ja-JP\": \"スペシャル減少量ダウン\",\n \"ko-KR\": \"스페셜 감소량 다운\",\n \"nl-NL\": \"Speciaalspaarder\",\n \"ru-RU\": \"Особый резерв\",\n \"zh-CN\": \"降低特殊武器减少量\",\n \"zh-TW\": \"降低特殊武器減少量\"\n },\n \"primary_only\": false\n },\n {\n \"key\": \"sLine truncated
const COOP_POINT_MAP = {
0: -20,
1: -10,
@@ -5739,6 +5778,8 @@ async function checkResponse(resp) {
}
}
class StatInkAPI {
statInkApiKey;
env;
statInk;
FETCH_LOCK;
cache;
@@ -5751,7 +5792,7 @@ class StatInkAPI {
this._salmonWeaponMap = new Map();
this.getSalmonWeapon = ()=>this._getCached(`${this.statInk}/api/v3/salmon/weapon?full=1`);
this.getWeapon = ()=>this._getCached(`${this.statInk}/api/v3/weapon?full=1`);
this.getAbility = ()=>this._getCached(`${this.statInk}/api/v3/ability?full=1`);
this.getAbility = ()=>__default;
this.getStage = ()=>this._getCached(`${this.statInk}/api/v3/stage`);
if (statInkApiKey.length !== 43) {
throw new Error("Invalid stat.ink API key");
@@ -5854,8 +5895,8 @@ class StatInkAPI {
}
}
_getAliasName(name) {
const STAT_INK_DOT = "┬À";
const SPLATNET_DOT = "";
const STAT_INK_DOT = "·";
const SPLATNET_DOT = "‧";
if (name.includes(STAT_INK_DOT)) {
return [
name,
@@ -5890,8 +5931,6 @@ class StatInkAPI {
getWeapon;
getAbility;
getStage;
statInkApiKey;
env;
}
class StatInkExporter {
name = "stat.ink";
@@ -5964,6 +6003,8 @@ class StatInkExporter {
}
} else if (vsMode === "X_MATCH") {
return "xmatch";
} else if (vsMode === "LEAGUE") {
return "event";
}
throw new TypeError(`Unknown vsMode ${vsMode}`);
}
@@ -6028,7 +6069,7 @@ class StatInkExporter {
return result;
};
async mapBattle({ groupInfo , challengeProgress , bankaraMatchChallenge , listNode , detail: vsDetail , rankBeforeState , rankState }) {
const { knockout , vsRule: { rule } , myTeam , otherTeams , bankaraMatch , festMatch , playedTime } = vsDetail;
const { knockout , vsRule: { rule } , myTeam , otherTeams , bankaraMatch , leagueMatch , festMatch , playedTime } = vsDetail;
const self = vsDetail.myTeam.players.find((i)=>i.isMyself);
if (!self) {
throw new Error("Self not found");
@@ -6124,6 +6165,10 @@ class StatInkExporter {
result.rank_after_s_plus = result.rank_before_s_plus;
}
}
if (leagueMatch) {
result.event = leagueMatch.leagueMatchEvent?.id;
result.event_power = leagueMatch.myLeaguePower;
}
if (challengeProgress) {
result.challenge_win = challengeProgress.winCount;
result.challenge_lose = challengeProgress.loseCount;
@@ -6134,6 +6179,7 @@ class StatInkExporter {
result.x_power_after = groupInfo.xMatchMeasurement.xPowerAfter;
}
}
result.bankara_power_after = vsDetail.bankaraMatch?.bankaraPower?.power;
if (rankBeforeState && rankState) {
result.rank_before_exp = rankBeforeState.rankPoint;
result.rank_after_exp = rankState.rankPoint;
@@ -6237,7 +6283,8 @@ class StatInkExporter {
golden_quota: wave.deliverNorm,
golden_appearances: wave.goldenPopCount,
golden_delivered: wave.teamDeliverCount,
special_uses
special_uses,
danger_rate: null
};
}
async mapCoop({ gradeBefore , groupInfo , detail }) {
@@ -6255,9 +6302,10 @@ class StatInkExporter {
]));
const title_after = detail.afterGrade ? b64Number(detail.afterGrade.id).toString() : undefined;
const title_exp_after = detail.afterGradePoint;
const maxWaves = detail.rule === "TEAM_CONTEST" ? 5 : 3;
let clear_waves;
if (waveResults.length > 0) {
clear_waves = waveResults.filter((i)=>i.waveNumber < 4).length - 1 + (resultWave === 0 ? 1 : 0);
clear_waves = waveResults.filter((i)=>i.waveNumber < maxWaves + 1).length - 1 + (resultWave === 0 ? 1 : 0);
} else {
clear_waves = 0;
}
@@ -6282,7 +6330,7 @@ class StatInkExporter {
}
}
let fail_reason = null;
if (clear_waves !== 3 && waveResults.length > 0) {
if (clear_waves !== maxWaves && waveResults.length > 0) {
const lastWave = waveResults[waveResults.length - 1];
if (lastWave.teamDeliverCount >= lastWave.deliverNorm) {
fail_reason = "wipe_out";
@@ -6292,8 +6340,9 @@ class StatInkExporter {
uuid: await gameId(detail.id),
private: groupInfo?.mode === "PRIVATE_CUSTOM" ? "yes" : "no",
big_run: detail.rule === "BIG_RUN" ? "yes" : "no",
eggstra_work: detail.rule === "TEAM_CONTEST" ? "yes" : "no",
stage: b64Number(detail.coopStage.id).toString(),
danger_rate: dangerRate * 100,
danger_rate: detail.rule === "TEAM_CONTEST" ? null : dangerRate * 100,
clear_waves,
fail_reason,
king_smell: smellMeter,
@@ -6326,6 +6375,53 @@ class StatInkExporter {
automated: "yes",
start_at: startedAt
};
if (detail.rule === "TEAM_CONTEST") {
let lastWave;
for (const [wave] of result.waves.map((p, i)=>[
p,
i
])){
let haz_level;
if (!lastWave) {
haz_level = 60;
} else {
const num_players = result.players.length;
const quota = lastWave.golden_quota;
const delivered = lastWave.golden_delivered;
let added_percent = 0;
if (num_players == 4) {
if (delivered >= quota * 2) {
added_percent = 60;
} else if (delivered >= quota * 1.5) {
added_percent = 30;
}
} else if (num_players == 3) {
if (delivered >= quota * 2) {
added_percent = 40;
} else if (delivered >= quota * 1.5) {
added_percent = 20;
}
} else if (num_players == 2) {
if (delivered >= quota * 2) {
added_percent = 20;
} else if (delivered >= quota * 1.5) {
added_percent = 10;
added_percent = 5;
}
} else if (num_players == 1) {
if (delivered >= quota * 2) {
added_percent = 10;
} else if (delivered >= quota * 1.5) {
added_percent = 5;
}
}
const prev_percent = lastWave.danger_rate;
haz_level = prev_percent + added_percent;
}
wave.danger_rate = haz_level;
lastWave = wave;
}
}
return result;
}
}
@@ -6347,6 +6443,7 @@ function replacer(key, value) {
return typeof value === "string" ? urlSimplify(value) : undefined;
}
class FileExporter {
exportPath;
name;
constructor(exportPath){
this.exportPath = exportPath;
@@ -6455,7 +6552,6 @@ class FileExporter {
}
return out;
}
exportPath;
}
const SEASONS = [
{
@@ -6475,6 +6571,12 @@ const SEASONS = [
name: "Fresh Season 2023",
start: new Date("2023-03-01T00:00:00+00:00"),
end: new Date("2023-06-01T00:00:00+00:00")
},
{
id: "season202306",
name: "Sizzle Season 2023",
start: new Date("2023-06-01T00:00:00+00:00"),
end: new Date("2023-09-01T00:00:00+00:00")
}
];
const getSeason = (date)=>{
@@ -6665,11 +6767,6 @@ function addRank(state, delta) {
}
};
}
const battleTime = (id)=>{
const { timestamp } = parseHistoryDetailId(id);
const dateStr = timestamp.replace(/(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})/, "$1-$2-$3T$4:$5:$6Z");
return new Date(dateStr);
};
function beginPoint(state, flatten) {
if (state) {
const index = flatten.findIndex((i)=>i.gameId === state.gameId);
@@ -6767,6 +6864,7 @@ function getRankStateByDelta(i) {
};
}
class RankTracker {
state;
deltaMap;
stateMap;
constructor(state){
@@ -6782,6 +6880,9 @@ class RankTracker {
this.state = state;
}
async updateState(history) {
if (history.length === 0) {
return;
}
const flatten = await Promise.all(history.flatMap(({ historyDetails , bankaraMatchChallenge })=>{
return historyDetails.nodes.map((j, index)=>({
id: j.id,
@@ -6811,7 +6912,6 @@ class RankTracker {
}
return curState;
}
state;
}
class GameFetcher {
_splatnet;
@@ -7018,6 +7118,7 @@ const DEFAULT_OPTS = {
noProgress: false,
monitor: false,
withSummary: false,
listMethod: "auto",
env: DEFAULT_ENV
};
class StepProgress {
@@ -7033,6 +7134,89 @@ class StepProgress {
this.skipped = {};
}
}
class BattleListFetcher {
splatnet;
listMethod;
allBattleList;
latestBattleList;
allLock;
latestLock;
constructor(listMethod, splatnet){
this.splatnet = splatnet;
this.allLock = new Mutex();
this.latestLock = new Mutex();
if (listMethod === "all") {
this.listMethod = "all";
} else if (listMethod === "latest") {
this.listMethod = "latest";
} else {
this.listMethod = "auto";
}
}
getAllBattleList() {
return this.allLock.use(async ()=>{
if (!this.allBattleList) {
this.allBattleList = await this.splatnet.getAllBattleList();
}
return this.allBattleList;
});
}
getLatestBattleList() {
return this.latestLock.use(async ()=>{
if (!this.latestBattleList) {
this.latestBattleList = await this.splatnet.getBattleList();
}
return this.latestBattleList;
});
}
async innerFetch(exporter) {
if (this.listMethod === "latest") {
return await exporter.notExported({
type: "VsInfo",
list: await this.getLatestBattleList()
});
}
if (this.listMethod === "all") {
return await exporter.notExported({
type: "VsInfo",
list: await this.getAllBattleList()
});
}
if (this.listMethod === "auto") {
const latestList = await exporter.notExported({
type: "VsInfo",
list: await this.getLatestBattleList()
});
if (latestList.length === 50) {
return await exporter.notExported({
type: "VsInfo",
list: await this.getAllBattleList()
});
}
return latestList;
}
throw new TypeError(`Unknown listMethod: ${this.listMethod}`);
}
async fetch(exporter) {
return [
...await this.innerFetch(exporter)
].reverse();
}
}
class CoopListFetcher {
splatnet;
constructor(splatnet){
this.splatnet = splatnet;
}
async fetch(exporter) {
return [
...await exporter.notExported({
type: "CoopInfo",
list: await this.splatnet.getBattleList(BattleListType.Coop)
})
].reverse();
}
}
function progress({ total , currentUrl , done }) {
return {
total,
@@ -7041,6 +7225,7 @@ function progress({ total , currentUrl , done }) {
};
}
class App {
opts;
profile;
env;
constructor(opts){
@@ -7051,6 +7236,13 @@ class App {
env: opts.env
});
this.env = opts.env;
if (opts.listMethod && ![
"all",
"auto",
"latest"
].includes(opts.listMethod)) {
throw new TypeError(`Unknown listMethod: ${opts.listMethod}`);
}
}
getSkipMode() {
const mode = this.opts.skipMode;
@@ -7134,8 +7326,7 @@ class App {
if (skipMode.includes("vs") || exporters.length === 0) {
this.env.logger.log("Skip exporting VS games.");
} else {
this.env.logger.log("Fetching battle list...");
const gameList = await splatnet.getBattleList();
const gameListFetcher = new BattleListFetcher(this.opts.listMethod ?? "auto", splatnet);
const { redraw , endBar } = this.exporterProgress("Export vs games");
const fetcher = new GameFetcher({
cache: this.opts.cache ?? new FileCache(this.profile.state.cacheDir),
@@ -7147,7 +7338,7 @@ class App {
type: "VsInfo",
fetcher,
exporter: e,
gameList,
gameListFetcher,
stepProgress: stats[e.name],
onStep: ()=>{
redraw(e.name, progress(stats[e.name]));
@@ -7171,8 +7362,7 @@ class App {
if (skipMode.includes("coop") || exporters.length === 0) {
this.env.logger.log("Skip exporting coop games.");
} else {
this.env.logger.log("Fetching coop battle list...");
const coopBattleList = await splatnet.getBattleList(BattleListType.Coop);
const gameListFetcher = new CoopListFetcher(splatnet);
const { redraw , endBar } = this.exporterProgress("Export coop games");
const fetcher = new GameFetcher({
cache: this.opts.cache ?? new FileCache(this.profile.state.cacheDir),
@@ -7183,7 +7373,7 @@ class App {
type: "CoopInfo",
fetcher,
exporter: e,
gameList: coopBattleList,
gameListFetcher,
stepProgress: stats[e.name],
onStep: ()=>{
redraw(e.name, progress(stats[e.name]));
@@ -7259,14 +7449,9 @@ class App {
await this.exportOnce();
}
}
async exportGameList({ type , fetcher , exporter , gameList , stepProgress , onStep }) {
async exportGameList({ type , fetcher , exporter , gameListFetcher , stepProgress , onStep }) {
onStep?.();
const workQueue = [
...await exporter.notExported({
type,
list: gameList
})
].reverse();
const workQueue = await gameListFetcher.fetch(exporter);
const step = async (id)=>{
const detail = await fetcher.fetch(type, id);
const result = await exporter.exportGame(detail);
@@ -7299,14 +7484,14 @@ class App {
this.env.logger.log(`Skipped ${Object.entries(stats).map(([name, { skipped }])=>Object.entries(skipped).map(([reason, count])=>`${name}: ${reason} (${count})`).join(", "))}`);
}
}
opts;
}
const parseArgs = (args)=>{
const parsed = mod1.parse(args, {
string: [
"profilePath",
"exporter",
"skipMode"
"skipMode",
"listMethod"
],
boolean: [
"help",
@@ -7334,7 +7519,8 @@ const parseArgs = (args)=>{
"s",
"skip-mode"
],
"withSummary": "with-summary"
"withSummary": "with-summary",
"listMethod": "list-method"
}
});
return parsed;
@@ -7348,6 +7534,10 @@ Options:
--exporter <exporter>, -e Exporter list to use (default: stat.ink)
Multiple exporters can be separated by commas
(e.g. "stat.ink,file")
--list-method When set to "latest", the latest 50 matches will be obtained.
When set to "all", matches of all modes will be obtained with a maximum of 250 matches (5 modes x 50 matches).
When set to "auto", the latest 50 matches will be obtained. If 50 matches have not been uploaded yet, matches will be obtained from the list of all modes.
"auto" is the default setting.
--no-progress, -n Disable progress bar
--monitor, -m Monitor mode
--skip-mode <mode>, -s Skip mode (default: null)