From 1c9c03e3273855edfe263f3996e740c2b96653bc Mon Sep 17 00:00:00 2001 From: spacemeowx2 Date: Fri, 4 Nov 2022 23:34:07 +0800 Subject: [PATCH] feat(stat.ink): add gears --- CHANGELOG | 4 +++ src/app.ts | 8 ++--- src/constant.ts | 2 +- src/exporters/stat.ink.ts | 66 +++++++++++++++++++++++++++++++++++---- src/types.ts | 35 +++++++++++++++++++++ 5 files changed, 104 insertions(+), 11 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8b6213e..6584b34 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +## 0.1.17 + +feat: add gears to stat.ink + ## 0.1.16 fix: RankTracker broken when token expires diff --git a/src/app.ts b/src/app.ts index 0d48095..719ee5b 100644 --- a/src/app.ts +++ b/src/app.ts @@ -210,6 +210,8 @@ export class App { ), ); + endBar(); + printStats(stats); if (errors.length > 0) { throw errors[0]; @@ -221,8 +223,6 @@ export class App { ...this.state, rankState: finalRankState, }); - - endBar(); } stats = initStats(); @@ -268,12 +268,12 @@ export class App { ), ); + endBar(); + printStats(stats); if (errors.length > 0) { throw errors[0]; } - - endBar(); } } async monitor() { diff --git a/src/constant.ts b/src/constant.ts index 5bb9401..83ef628 100644 --- a/src/constant.ts +++ b/src/constant.ts @@ -1,7 +1,7 @@ import type { StatInkPostBody, VsHistoryDetail } from "./types.ts"; export const AGENT_NAME = "s3si.ts"; -export const S3SI_VERSION = "0.1.16"; +export const S3SI_VERSION = "0.1.17"; export const NSOAPP_VERSION = "2.3.1"; export const WEB_VIEW_VERSION = "1.0.0-5644e7a2"; export const S3SI_LINK = "https://github.com/spacemeowx2/s3si.ts"; diff --git a/src/exporters/stat.ink.ts b/src/exporters/stat.ink.ts index 86b3d00..69a6441 100644 --- a/src/exporters/stat.ink.ts +++ b/src/exporters/stat.ink.ts @@ -9,6 +9,10 @@ import { import { CoopInfo, GameExporter, + PlayerGear, + StatInkAbility, + StatInkGear, + StatInkGears, StatInkPlayer, StatInkPostBody, StatInkPostResponse, @@ -17,7 +21,7 @@ import { VsInfo, VsPlayer, } from "../types.ts"; -import { base64, msgpack } from "../../deps.ts"; +import { base64, msgpack, Mutex } from "../../deps.ts"; import { APIError } from "../APIError.ts"; import { cache, gameId } from "../utils.ts"; @@ -30,11 +34,23 @@ function b64Number(id: string): number { return parseInt(num); } +const FETCH_LOCK = new Mutex(); +async function _getAbility(): Promise { + const release = await FETCH_LOCK.acquire(); + try { + const resp = await fetch("https://stat.ink/api/v3/ability?full=1"); + const json = await resp.json(); + return json; + } finally { + release(); + } +} async function _getStage(): Promise { const resp = await fetch("https://stat.ink/api/v3/stage"); const json = await resp.json(); return json; } +const getAbility = cache(_getAbility); const getStage = cache(_getStage); /** @@ -157,7 +173,42 @@ export class StatInkExporter implements GameExporter { return result.key; } - mapPlayer(player: VsPlayer, index: number): StatInkPlayer { + async mapGears( + { headGear, clothingGear, shoesGear }: VsPlayer, + ): Promise { + const amap = (await getAbility()).map((i) => ({ + ...i, + names: Object.values(i.name), + })); + const mapAbility = ({ name }: { name: string }): string | null => { + const result = amap.find((a) => a.names.includes(name)); + if (!result) { + return null; + } + return result.key; + }; + const mapGear = ( + { primaryGearPower, additionalGearPowers }: PlayerGear, + ): StatInkGear => { + const primary = mapAbility(primaryGearPower); + if (!primary) { + throw new Error("Unknown ability: " + primaryGearPower.name); + } + return { + primary_ability: primary, + secondary_abilities: additionalGearPowers.map(mapAbility), + }; + }; + return { + headgear: mapGear(headGear), + clothing: mapGear(clothingGear), + shoes: mapGear(shoesGear), + }; + } + mapPlayer = async ( + player: VsPlayer, + index: number, + ): Promise => { const result: StatInkPlayer = { me: player.isMyself ? "yes" : "no", rank_in_team: index + 1, @@ -166,6 +217,7 @@ export class StatInkExporter implements GameExporter { splashtag_title: player.byname, weapon: b64Number(player.weapon.id).toString(), inked: player.paint, + gears: await this.mapGears(player), disconnected: player.result ? "no" : "yes", }; if (player.result) { @@ -176,7 +228,7 @@ export class StatInkExporter implements GameExporter { result.special = player.result.special; } return result; - } + }; async mapBattle( { challengeProgress, @@ -216,9 +268,11 @@ export class StatInkExporter implements GameExporter { medals: vsDetail.awards.map((i) => i.name), - our_team_players: myTeam.players.map(this.mapPlayer), - their_team_players: otherTeams.flatMap((i) => i.players).map( - this.mapPlayer, + our_team_players: await Promise.all(myTeam.players.map(this.mapPlayer)), + their_team_players: await Promise.all( + otherTeams.flatMap((i) => i.players).map( + this.mapPlayer, + ), ), agent: AGENT_NAME, diff --git a/src/types.ts b/src/types.ts index d1a72d4..f5f21e1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -61,6 +61,19 @@ export type HistoryGroups = { }; }[]; }; +export type PlayerGear = { + name: string; + primaryGearPower: { + name: string; + }; + additionalGearPowers: { + name: string; + }[]; + brand: { + name: string; + id: string; + }; +}; export type VsPlayer = { id: string; nameId: string | null; @@ -81,6 +94,10 @@ export type VsPlayer = { special: number; } | null; paint: number; + + headGear: PlayerGear; + clothingGear: PlayerGear; + shoesGear: PlayerGear; }; export type VsTeam = { players: VsPlayer[]; @@ -240,6 +257,23 @@ export enum BattleListType { Coop, } +export type StatInkAbility = { + key: string; + name: Record; + primary_only: boolean; +}[]; + +export type StatInkGear = { + primary_ability: string; + secondary_abilities: (string | null)[]; +}; + +export type StatInkGears = { + headgear: StatInkGear; + clothing: StatInkGear; + shoes: StatInkGear; +}; + export type StatInkPlayer = { me: "yes" | "no"; rank_in_team: number; @@ -253,6 +287,7 @@ export type StatInkPlayer = { kill_or_assist?: number; death?: number; special?: number; + gears?: StatInkGears; disconnected: "yes" | "no"; };