|
| 1 | +//Imports |
| 2 | +import assets from "./assets.mjs" |
| 3 | + |
| 4 | +//Setup |
| 5 | +export default async function({login, q, imports, data, account}, {enabled = false, extras = false, token} = {}) { |
| 6 | + //Plugin execution |
| 7 | + try { |
| 8 | + //Check if plugin is enabled and requirements are met |
| 9 | + if ((!q.splatoon) || (!imports.metadata.plugins.splatoon.enabled(enabled, {extras}))) |
| 10 | + return null |
| 11 | + |
| 12 | + //Load inputs |
| 13 | + const {modes, "versus.limit":_versus_limit, "salmon.limit":_salmon_limit} = imports.metadata.plugins.splatoon.inputs({data, account, q}) |
| 14 | + |
| 15 | + //Save profile |
| 16 | + { |
| 17 | + const profile = `${imports.__module(import.meta.url)}/s3si/profile.json` |
| 18 | + console.debug(`metrics/compute/${login}/plugins > splatoon > saving ${profile}`) |
| 19 | + const parsed = JSON.parse(token) |
| 20 | + if (!parsed?.loginState?.sessionToken) |
| 21 | + throw new Error("Configuration is missing sessionToken") |
| 22 | + await imports.fs.writeFile(profile, token) |
| 23 | + } |
| 24 | + |
| 25 | + //Fetch data |
| 26 | + const allowed = { |
| 27 | + files:["profile.json", "profile.json.swap", "export", "cache"], |
| 28 | + net:["api.imink.app", "accounts.nintendo.com", "api.accounts.nintendo.com", "api-lp1.znc.srv.nintendo.net", "api.lp1.av5ja.srv.nintendo.net"] |
| 29 | + } |
| 30 | + await imports.run(`deno run --no-prompt --cached-only --no-remote --allow-read="${allowed.files}" --allow-write="${allowed.files}" --allow-net="${allowed.net}" index.ts --exporter file --no-progress`, {cwd: `${imports.__module(import.meta.url)}/s3si`}, {prefixed:false}) |
| 31 | + |
| 32 | + //Read fetched data |
| 33 | + const fetched = (await Promise.all( |
| 34 | + (await imports.fs.readdir(`${imports.__module(import.meta.url)}/s3si/export`)) |
| 35 | + .map(async file => JSON.parse(await imports.fs.readFile(`${imports.__module(import.meta.url)}/s3si/export/${file}`))))) |
| 36 | + .sort((a, b) => new Date(b.data.detail.playedTime) - new Date(a.data.detail.playedTime)) |
| 37 | + console.debug(`metrics/compute/${login}/plugins > splatoon > fetched ${fetched.length} matches`) |
| 38 | + |
| 39 | + //Versus mode |
| 40 | + let vs = null |
| 41 | + if (!((modes.length === 1)&&(modes[0] === "salmon-run"))) { |
| 42 | + vs = { |
| 43 | + matches:await Promise.all(fetched.filter(({type, data}) => (type === "VS")&&(modes.includes(data.detail.vsRule.name.toLocaleLowerCase().replace(/ /g, "-")))).slice(0, _versus_limit).map(async ({data}) => ({ |
| 44 | + mode:{ |
| 45 | + name:data.detail.vsRule.name, |
| 46 | + icon:await imports.imgb64(assets.modes[data.detail.vsRule.name]), |
| 47 | + }, |
| 48 | + result:data.detail.judgement, |
| 49 | + knockout:data.detail.knockout ?? null, |
| 50 | + teams:await Promise.all([data.detail.myTeam, ...data.detail.otherTeams].map(async team => ({ |
| 51 | + color:`#${Math.round(255*team.color.r).toString(16)}${Math.round(255*team.color.g).toString(16)}${Math.round(255*team.color.b).toString(16)}`, |
| 52 | + score:((data.detail.vsRule.name === "Turf War") ? team.result?.paintRatio*100 : team.result?.score) ?? null, |
| 53 | + players:await Promise.all(team.players.map(async ({name, byname, weapon, paint, result, isMyself:self}) => ({ |
| 54 | + name, |
| 55 | + byname, |
| 56 | + self, |
| 57 | + weapon:{ |
| 58 | + name:weapon.name, |
| 59 | + icon:await imports.imgb64(assets.weapons[weapon.name]), |
| 60 | + }, |
| 61 | + special:{ |
| 62 | + name:weapon.specialWeapon.name, |
| 63 | + icon:await imports.imgb64(assets.specials[weapon.specialWeapon.name]), |
| 64 | + }, |
| 65 | + sub:{ |
| 66 | + name:weapon.subWeapon.name, |
| 67 | + icon:await imports.imgb64(assets.subweapons[weapon.subWeapon.name]), |
| 68 | + }, |
| 69 | + result:{ |
| 70 | + paint:paint ?? 0, |
| 71 | + kill:result?.kill ?? 0, |
| 72 | + death:result?.death ?? 0, |
| 73 | + assist:result?.assist ?? 0, |
| 74 | + special:result?.special ?? 0, |
| 75 | + } |
| 76 | + }))) |
| 77 | + }))), |
| 78 | + awards:data.detail.awards, |
| 79 | + date:data.detail.playedTime, |
| 80 | + duration:data.detail.duration, |
| 81 | + player:{ |
| 82 | + name:data.detail.player.name, |
| 83 | + byname:data.detail.player.byname, |
| 84 | + rank:data.listNode?.udemae ?? null, |
| 85 | + }, |
| 86 | + stage:{ |
| 87 | + name:data.detail.vsStage.name, |
| 88 | + icon:await imports.imgb64(assets.stages[data.detail.vsStage.name]), |
| 89 | + } |
| 90 | + }))) |
| 91 | + } |
| 92 | + vs.player = vs.matches.at(-1)?.player ?? null |
| 93 | + } |
| 94 | + |
| 95 | + //Salmon run |
| 96 | + let salmon = null |
| 97 | + if (modes.includes("salmon-run")) { |
| 98 | + salmon = { |
| 99 | + matches:await Promise.all(fetched.filter(({type}) => type === "COOP").slice(0, _salmon_limit).map(async ({data}) => ({ |
| 100 | + weapons:await Promise.all(data.detail.myResult.weapons.map(async ({name}) => ({name, icon:await imports.imgb64(assets.weapons[name])}))), |
| 101 | + special:{ |
| 102 | + name:data.detail.myResult.specialWeapon.name, |
| 103 | + icon:await imports.imgb64(assets.specials[data.detail.myResult.specialWeapon.name]) |
| 104 | + }, |
| 105 | + eggs:{ |
| 106 | + golden:data.detail.myResult.goldenDeliverCount, |
| 107 | + regular:data.detail.myResult.deliverCount, |
| 108 | + }, |
| 109 | + defeated:await Promise.all(data.detail.enemyResults.map(async ({defeatCount:count, enemy:{name}}) => ({name, count, icon:await imports.imgb64(assets.salmon[name])}))), |
| 110 | + rescues:data.detail.myResult.rescueCount, |
| 111 | + rescued:data.detail.myResult.rescuedCount, |
| 112 | + waves:data.detail.waveResults.map(({deliverNorm:quota, teamDeliverCount:delivered}) => ({quota, delivered})), |
| 113 | + failed:data.detail.resultWave, |
| 114 | + hazard:Math.round(data.detail.dangerRate*100), |
| 115 | + boss:data.detail.bossResult ? { |
| 116 | + defeated:data.detail.bossResult.hasDefeatBoss, |
| 117 | + name:data.detail.bossResult.boss.name, |
| 118 | + icon:await imports.imgb64(assets.salmon[data.detail.bossResult.boss.name]) |
| 119 | + } : null, |
| 120 | + stage:{ |
| 121 | + name:data.detail.coopStage.name, |
| 122 | + icon:await imports.imgb64(assets.stages[data.detail.coopStage.name]) |
| 123 | + }, |
| 124 | + date:data.detail.playedTime, |
| 125 | + grade:data.detail.afterGrade.name, |
| 126 | + player:data.detail.myResult.player.name, |
| 127 | + }))), |
| 128 | + } |
| 129 | + salmon.player = { |
| 130 | + name:salmon.matches.at(-1)?.player ?? null, |
| 131 | + grade:salmon.matches.at(-1)?.grade ?? null, |
| 132 | + } |
| 133 | + } |
| 134 | + |
| 135 | + //Results |
| 136 | + return { |
| 137 | + vs, |
| 138 | + salmon, |
| 139 | + icons:Object.fromEntries(await Promise.all(Object.entries(assets.icons).map(async ([k, v]) => [k, await imports.imgb64(v)]))) |
| 140 | + } |
| 141 | + } |
| 142 | + //Handle errors |
| 143 | + catch (error) { |
| 144 | + throw imports.format.error(error) |
| 145 | + } |
| 146 | +} |
| 147 | + |
0 commit comments