feat: add init rank
parent
a5c68ad06b
commit
297e73e197
|
|
@ -0,0 +1 @@
|
|||
export { assertEquals } from "https://deno.land/std@0.160.0/testing/asserts.ts";
|
||||
|
|
@ -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.");
|
||||
|
|
@ -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());
|
||||
});
|
||||
|
|
@ -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) {}
|
||||
}
|
||||
11
src/state.ts
11
src/state.ts
|
|
@ -3,6 +3,15 @@ export type LoginState = {
|
|||
gToken?: 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 = {
|
||||
loginState?: LoginState;
|
||||
fGen: string;
|
||||
|
|
@ -10,6 +19,8 @@ export type State = {
|
|||
userLang?: string;
|
||||
userCountry?: string;
|
||||
|
||||
rankState?: RankState;
|
||||
|
||||
cacheDir: string;
|
||||
|
||||
// Exporter config
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
if (line !== "") {
|
||||
if (!skipEmpty || line !== "") {
|
||||
return line;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue