2022-11-17 06:19:11 -05:00
|
|
|
import { DeepReadonly } from "../deps.ts";
|
|
|
|
|
import { DEFAULT_ENV, Env } from "./env.ts";
|
|
|
|
|
|
2022-10-18 08:08:26 -04:00
|
|
|
export type LoginState = {
|
2022-10-18 20:36:58 -04:00
|
|
|
sessionToken?: string;
|
|
|
|
|
gToken?: string;
|
|
|
|
|
bulletToken?: string;
|
2022-10-18 08:08:26 -04:00
|
|
|
};
|
2022-10-28 05:07:18 -04:00
|
|
|
export type RankState = {
|
|
|
|
|
// generated by gameId(battle.id)
|
2022-10-28 08:45:56 -04:00
|
|
|
gameId: string;
|
2022-12-03 14:19:32 -05:00
|
|
|
// extract from battle.id
|
|
|
|
|
timestamp?: number;
|
2022-10-28 05:07:18 -04:00
|
|
|
// C-, B, A+, S, S+0, S+12
|
|
|
|
|
rank: string;
|
|
|
|
|
rankPoint: number;
|
|
|
|
|
};
|
2022-10-18 09:16:51 -04:00
|
|
|
export type State = {
|
|
|
|
|
loginState?: LoginState;
|
2022-10-18 20:36:58 -04:00
|
|
|
fGen: string;
|
|
|
|
|
appUserAgent?: string;
|
2022-10-19 04:56:18 -04:00
|
|
|
userLang?: string;
|
|
|
|
|
userCountry?: string;
|
2022-10-20 09:45:59 -04:00
|
|
|
|
2022-10-28 05:07:18 -04:00
|
|
|
rankState?: RankState;
|
|
|
|
|
|
2022-10-20 09:45:59 -04:00
|
|
|
cacheDir: string;
|
|
|
|
|
|
|
|
|
|
// Exporter config
|
|
|
|
|
statInkApiKey?: string;
|
|
|
|
|
fileExportPath: string;
|
2022-10-22 18:52:08 -04:00
|
|
|
monitorInterval: number;
|
2023-02-27 23:52:40 -05:00
|
|
|
mongoDbUri?: string;
|
2022-10-18 09:16:51 -04:00
|
|
|
};
|
|
|
|
|
|
2022-10-18 20:36:58 -04:00
|
|
|
export const DEFAULT_STATE: State = {
|
2022-10-20 09:45:59 -04:00
|
|
|
cacheDir: "./cache",
|
2022-10-18 20:36:58 -04:00
|
|
|
fGen: "https://api.imink.app/f",
|
2022-10-20 09:45:59 -04:00
|
|
|
fileExportPath: "./export",
|
2022-10-22 18:52:08 -04:00
|
|
|
monitorInterval: 500,
|
2022-10-18 20:36:58 -04:00
|
|
|
};
|
2022-10-23 04:25:10 -04:00
|
|
|
|
|
|
|
|
export type StateBackend = {
|
|
|
|
|
read: () => Promise<State>;
|
|
|
|
|
write: (newState: State) => Promise<void>;
|
|
|
|
|
};
|
|
|
|
|
|
2022-11-17 06:19:11 -05:00
|
|
|
export class InMemoryStateBackend implements StateBackend {
|
|
|
|
|
state: State;
|
|
|
|
|
|
|
|
|
|
constructor(state?: State) {
|
|
|
|
|
this.state = state ?? DEFAULT_STATE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
read() {
|
|
|
|
|
return Promise.resolve(this.state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
write(newState: State) {
|
|
|
|
|
this.state = newState;
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-23 04:25:10 -04:00
|
|
|
export class FileStateBackend implements StateBackend {
|
|
|
|
|
constructor(private path: string) {}
|
|
|
|
|
|
2022-11-17 06:19:11 -05:00
|
|
|
async read(): Promise<DeepReadonly<State>> {
|
2022-10-24 08:47:25 -04:00
|
|
|
const data = await Deno.readTextFile(this.path);
|
|
|
|
|
const json = JSON.parse(data);
|
2022-10-23 04:25:10 -04:00
|
|
|
return json;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async write(newState: State): Promise<void> {
|
2022-10-24 08:47:25 -04:00
|
|
|
const data = JSON.stringify(newState, undefined, 2);
|
2022-10-23 04:25:10 -04:00
|
|
|
const swapPath = `${this.path}.swap`;
|
2022-10-24 08:47:25 -04:00
|
|
|
await Deno.writeTextFile(swapPath, data);
|
2022-10-23 04:25:10 -04:00
|
|
|
await Deno.rename(swapPath, this.path);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-17 06:19:11 -05:00
|
|
|
|
|
|
|
|
export class Profile {
|
|
|
|
|
protected _state?: State;
|
|
|
|
|
protected stateBackend: StateBackend;
|
|
|
|
|
protected env: Env;
|
|
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
|
{ stateBackend, env = DEFAULT_ENV }: {
|
|
|
|
|
stateBackend: StateBackend;
|
|
|
|
|
env?: Env;
|
|
|
|
|
},
|
|
|
|
|
) {
|
|
|
|
|
this.stateBackend = stateBackend;
|
|
|
|
|
this.env = env;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get state(): DeepReadonly<State> {
|
|
|
|
|
if (!this._state) {
|
|
|
|
|
throw new Error("state is not initialized");
|
|
|
|
|
}
|
|
|
|
|
return this._state;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async writeState(newState: State) {
|
|
|
|
|
this._state = newState;
|
|
|
|
|
await this.stateBackend.write(newState);
|
|
|
|
|
}
|
|
|
|
|
async readState() {
|
|
|
|
|
try {
|
|
|
|
|
const json = await this.stateBackend.read();
|
|
|
|
|
this._state = {
|
|
|
|
|
...DEFAULT_STATE,
|
|
|
|
|
...json,
|
|
|
|
|
};
|
|
|
|
|
} catch (e) {
|
|
|
|
|
this.env.logger.warn(
|
|
|
|
|
`Failed to read config file, create new config file. (${e})`,
|
|
|
|
|
);
|
|
|
|
|
await this.writeState(DEFAULT_STATE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|