feat: add init rank

main
spacemeowx2 2022-10-28 17:07:18 +08:00
parent a5c68ad06b
commit 297e73e197
6 changed files with 248 additions and 2 deletions

1
dev_deps.ts Normal file
View File

@ -0,0 +1 @@
export { assertEquals } from "https://deno.land/std@0.160.0/testing/asserts.ts";

112
initRank.ts Normal file
View File

@ -0,0 +1,112 @@
/**
* If rankState in profile.json is not defined, it will be initialized.
*/
import { flags } from "./deps.ts";
import { getBulletToken, getGToken } from "./src/iksm.ts";
import { checkToken, getBattleDetail, getBattleList } from "./src/splatnet3.ts";
import { gameId, readline } from "./src/utils.ts";
import { FileStateBackend } from "./src/state.ts";
import { BattleListType } from "./src/types.ts";
import { RANK_PARAMS } from "./src/RankTracker.ts";
const parseArgs = (args: string[]) => {
const parsed = flags.parse(args, {
string: ["profilePath"],
alias: {
"help": "h",
"profilePath": ["p", "profile-path"],
},
});
return parsed;
};
const opts = parseArgs(Deno.args);
if (opts.help) {
console.log(
`Usage: deno run -A ${Deno.mainModule} [options]
Options:
--profile-path <path>, -p Path to config file (default: ./profile.json)
--help Show this help message and exit`,
);
Deno.exit(0);
}
const stateBackend = new FileStateBackend(opts.profilePath ?? "./profile.json");
let state = await stateBackend.read();
if (state.rankState) {
console.log("rankState is already initialized.");
Deno.exit(0);
}
if (!await checkToken(state)) {
const sessionToken = state.loginState?.sessionToken;
if (!sessionToken) {
throw new Error("Session token is not set.");
}
const { webServiceToken, userCountry, userLang } = await getGToken({
fApi: state.fGen,
sessionToken,
});
const bulletToken = await getBulletToken({
webServiceToken,
userLang,
userCountry,
appUserAgent: state.appUserAgent,
});
state = {
...state,
loginState: {
...state.loginState,
gToken: webServiceToken,
bulletToken,
},
userLang: state.userLang ?? userLang,
userCountry: state.userCountry ?? userCountry,
};
await stateBackend.write(state);
}
const battleList = await getBattleList(state, BattleListType.Bankara);
if (battleList.length === 0) {
console.log("No anarchy battle found. Did you play anarchy?");
Deno.exit(0);
}
const { vsHistoryDetail: detail } = await getBattleDetail(state, battleList[0]);
console.log(
`Your latest battle is played at ${
new Date(detail.playedTime).toLocaleString()
}. Please enter your rank after this battle(format: RANK,POINT. S+0,300):`,
);
while (true) {
const userInput = await readline();
const [rank, point] = userInput.split(",");
const pointNumber = parseInt(point);
if (!RANK_PARAMS.find((i) => i.rank === rank)) {
console.log("Invalid rank. Please enter again:");
} else if (isNaN(pointNumber)) {
console.log("Invalid point. Please enter again:");
} else {
state = {
...state,
rankState: {
gameId: await gameId(detail.id),
rank,
rankPoint: pointNumber,
},
};
break;
}
}
await stateBackend.write(state);
console.log("rankState is initialized.");

7
src/RankTracker.test.ts Normal file
View File

@ -0,0 +1,7 @@
import { RankTracker } from "./RankTracker.ts";
import { assertEquals } from "../dev_deps.ts";
Deno.test("RankTracker", () => {
const tracker = new RankTracker();
assertEquals(tracker, new RankTracker());
});

113
src/RankTracker.ts Normal file
View File

@ -0,0 +1,113 @@
import { RankState } from "./state.ts";
import { GameFetcher } from "./GameFetcher.ts";
type RankParam = {
rank: string;
pointRange: [number, number];
entrance: number;
openWin: number;
openLose: number;
rankUp?: boolean;
};
const splusParams = () => {
const out: RankParam[] = [];
for (let i = 0; i < 50; i++) {
const level = i % 10;
const item: RankParam = {
rank: `S+${i}`,
pointRange: [300 + level * 350, 300 + (level + 1) * 350],
entrance: 160,
openWin: 8,
openLose: 5,
};
if (level === 9) {
item.rankUp = true;
}
out.push(item);
}
out.push({
rank: "S+50",
pointRange: [0, 9999],
entrance: 160,
openWin: 8,
openLose: 5,
});
return out;
};
export const RANK_PARAMS: RankParam[] = [{
rank: "C-",
pointRange: [0, 200],
entrance: 0,
openWin: 8,
openLose: 1,
}, {
rank: "C",
pointRange: [200, 400],
entrance: 20,
openWin: 8,
openLose: 1,
}, {
rank: "C+",
pointRange: [400, 600],
entrance: 40,
openWin: 8,
openLose: 1,
rankUp: true,
}, {
rank: "B-",
pointRange: [100, 350],
entrance: 55,
openWin: 8,
openLose: 2,
}, {
rank: "B",
pointRange: [350, 600],
entrance: 70,
openWin: 8,
openLose: 2,
}, {
rank: "B+",
pointRange: [600, 850],
entrance: 85,
openWin: 8,
openLose: 2,
rankUp: true,
}, {
rank: "A-",
pointRange: [200, 500],
entrance: 100,
openWin: 8,
openLose: 3,
}, {
rank: "A",
pointRange: [500, 800],
entrance: 110,
openWin: 8,
openLose: 3,
}, {
rank: "A+",
pointRange: [800, 1100],
entrance: 120,
openWin: 8,
openLose: 3,
rankUp: true,
}, {
rank: "S",
pointRange: [300, 1000],
entrance: 150,
openWin: 8,
openLose: 4,
rankUp: true,
}, ...splusParams()];
/**
* if state is empty, it will not track rank.
*/
export class RankTracker {
constructor(private state?: RankState) {}
}

View File

@ -3,6 +3,15 @@ export type LoginState = {
gToken?: string; gToken?: string;
bulletToken?: string; bulletToken?: string;
}; };
export type RankState = {
// generated by gameId(battle.id)
// If the gameId does not exist, the tracker will assume that the user has
// not played a bankara match. And it will start tracking from the first match
gameId?: string;
// C-, B, A+, S, S+0, S+12
rank: string;
rankPoint: number;
};
export type State = { export type State = {
loginState?: LoginState; loginState?: LoginState;
fGen: string; fGen: string;
@ -10,6 +19,8 @@ export type State = {
userLang?: string; userLang?: string;
userCountry?: string; userCountry?: string;
rankState?: RankState;
cacheDir: string; cacheDir: string;
// Exporter config // Exporter config

View File

@ -19,9 +19,11 @@ export function urlBase64Decode(data: string) {
); );
} }
export async function readline() { export async function readline(
{ skipEmpty = true }: { skipEmpty?: boolean } = {},
) {
for await (const line of stdinLines) { for await (const line of stdinLines) {
if (line !== "") { if (!skipEmpty || line !== "") {
return line; return line;
} }
} }