feat: add monitor mode

main
spacemeowx2 2022-10-23 06:52:08 +08:00
parent 1a08202ae2
commit e3a037dcac
6 changed files with 103 additions and 51 deletions

View File

@ -5,12 +5,13 @@ import { flags } from "./deps.ts";
const parseArgs = (args: string[]) => { const parseArgs = (args: string[]) => {
const parsed = flags.parse(args, { const parsed = flags.parse(args, {
string: ["profilePath", "exporter"], string: ["profilePath", "exporter"],
boolean: ["help", "noProgress"], boolean: ["help", "noProgress", "monitor"],
alias: { alias: {
"help": "h", "help": "h",
"profilePath": ["p", "profile-path"], "profilePath": ["p", "profile-path"],
"exporter": ["e"], "exporter": ["e"],
"noProgress": ["n", "no-progress"], "noProgress": ["n", "no-progress"],
"monitor": ["m"],
}, },
}); });
return parsed; return parsed;

View File

@ -17,7 +17,7 @@ import {
import { Cache, FileCache, MemoryCache } from "./cache.ts"; import { Cache, FileCache, MemoryCache } from "./cache.ts";
import { StatInkExporter } from "./exporters/stat.ink.ts"; import { StatInkExporter } from "./exporters/stat.ink.ts";
import { FileExporter } from "./exporters/file.ts"; import { FileExporter } from "./exporters/file.ts";
import { battleId, readline, showError } from "./utils.ts"; import { battleId, delay, readline, showError } from "./utils.ts";
export type Opts = { export type Opts = {
profilePath: string; profilePath: string;
@ -200,7 +200,12 @@ export class App {
statInkApiKey: key, statInkApiKey: key,
}); });
} }
out.push(new StatInkExporter(this.state.statInkApiKey!)); out.push(
new StatInkExporter(
this.state.statInkApiKey!,
this.opts.monitor ? "Monitoring" : "Manual",
),
);
} }
if (exporters.includes("file")) { if (exporters.includes("file")) {
@ -209,58 +214,16 @@ export class App {
return out; return out;
} }
async run() { async exportOnce() {
await this.readState();
const bar = !this.opts.noProgress const bar = !this.opts.noProgress
? new MultiProgressBar({ ? new MultiProgressBar({
title: "Export battles", title: "Export battles",
display: "[:bar] :text :percent :time eta: :eta :completed/:total", display: "[:bar] :text :percent :time eta: :eta :completed/:total",
}) })
: undefined; : undefined;
const exporters = await this.getExporters(); const exporters = await this.getExporters();
if (!this.state.loginState?.sessionToken) {
const sessionToken = await loginManually();
await this.writeState({
...this.state,
loginState: {
...this.state.loginState,
sessionToken,
},
});
}
const sessionToken = this.state.loginState!.sessionToken!;
console.log("Checking token...");
if (!await checkToken(this.state)) {
console.log("Token expired, refetch tokens.");
const { webServiceToken, userCountry, userLang } = await getGToken({
fApi: this.state.fGen,
sessionToken,
});
const bulletToken = await getBulletToken({
webServiceToken,
userLang,
userCountry,
appUserAgent: this.state.appUserAgent,
});
await this.writeState({
...this.state,
loginState: {
...this.state.loginState,
gToken: webServiceToken,
bulletToken,
},
userLang: this.state.userLang ?? userLang,
userCountry: this.state.userCountry ?? userCountry,
});
}
const fetcher = new BattleFetcher({ const fetcher = new BattleFetcher({
cache: new FileCache(this.state.cacheDir), cache: new FileCache(this.state.cacheDir),
state: this.state, state: this.state,
@ -302,7 +265,87 @@ export class App {
), ),
); );
console.log("\nDone.", stats); bar?.end();
console.log(
`Exported ${
Object.entries(stats)
.map(([name, count]) => `${name}: ${count}`)
.join(", ")
}`,
);
}
async monitor() {
while (true) {
await this.exportOnce();
await this.countDown(this.state.monitorInterval);
}
}
async countDown(sec: number) {
const bar = !this.opts.noProgress
? new MultiProgressBar({
title: "Killing time...",
display: "[:bar] :completed/:total",
})
: undefined;
for (const i of Array(sec).keys()) {
bar?.render([{
completed: i,
total: sec,
}]);
await delay(1000);
}
bar?.end();
}
async run() {
await this.readState();
if (!this.state.loginState?.sessionToken) {
const sessionToken = await loginManually();
await this.writeState({
...this.state,
loginState: {
...this.state.loginState,
sessionToken,
},
});
}
const sessionToken = this.state.loginState!.sessionToken!;
console.log("Checking token...");
if (!await checkToken(this.state)) {
console.log("Token expired, refetch tokens.");
const { webServiceToken, userCountry, userLang } = await getGToken({
fApi: this.state.fGen,
sessionToken,
});
const bulletToken = await getBulletToken({
webServiceToken,
userLang,
userCountry,
appUserAgent: this.state.appUserAgent,
});
await this.writeState({
...this.state,
loginState: {
...this.state.loginState,
gToken: webServiceToken,
bulletToken,
},
userLang: this.state.userLang ?? userLang,
userCountry: this.state.userCountry ?? userCountry,
});
}
if (this.opts.monitor) {
await this.monitor();
} else {
await this.exportOnce();
}
} }
/** /**
* Export battle list. * Export battle list.

View File

@ -1,7 +1,7 @@
import type { StatInkPostBody, VsHistoryDetail } from "./types.ts"; import type { StatInkPostBody, VsHistoryDetail } from "./types.ts";
export const AGENT_NAME = "s3si.ts"; export const AGENT_NAME = "s3si.ts";
export const S3SI_VERSION = "0.1.5"; export const S3SI_VERSION = "0.1.6";
export const NSOAPP_VERSION = "2.3.1"; export const NSOAPP_VERSION = "2.3.1";
export const WEB_VIEW_VERSION = "1.0.0-216d0219"; export const WEB_VIEW_VERSION = "1.0.0-216d0219";

View File

@ -42,7 +42,8 @@ const getStage = cache(_getStage);
*/ */
export class StatInkExporter implements BattleExporter<VsBattle> { export class StatInkExporter implements BattleExporter<VsBattle> {
name = "stat.ink"; name = "stat.ink";
constructor(private statInkApiKey: string) {
constructor(private statInkApiKey: string, private uploadMode: string) {
if (statInkApiKey.length !== 43) { if (statInkApiKey.length !== 43) {
throw new Error("Invalid stat.ink API key"); throw new Error("Invalid stat.ink API key");
} }
@ -203,7 +204,9 @@ export class StatInkExporter implements BattleExporter<VsBattle> {
agent: AGENT_NAME, agent: AGENT_NAME,
agent_version: S3SI_VERSION, agent_version: S3SI_VERSION,
agent_variables: undefined, agent_variables: {
"Upload Mode": this.uploadMode,
},
automated: "yes", automated: "yes",
start_at: startedAt, start_at: startedAt,
end_at: startedAt + vsDetail.duration, end_at: startedAt + vsDetail.duration,

View File

@ -15,10 +15,12 @@ export type State = {
// Exporter config // Exporter config
statInkApiKey?: string; statInkApiKey?: string;
fileExportPath: string; fileExportPath: string;
monitorInterval: number;
}; };
export const DEFAULT_STATE: State = { export const DEFAULT_STATE: State = {
cacheDir: "./cache", cacheDir: "./cache",
fGen: "https://api.imink.app/f", fGen: "https://api.imink.app/f",
fileExportPath: "./export", fileExportPath: "./export",
monitorInterval: 500,
}; };

View File

@ -113,3 +113,6 @@ export function parseVsHistoryDetailId(id: string) {
uuid, uuid,
}; };
} }
export const delay = (ms: number) =>
new Promise<void>((resolve) => setTimeout(resolve, ms));