2022-11-26 09:45:44 -05:00
|
|
|
import {
|
|
|
|
|
CoopInfo,
|
|
|
|
|
ExportResult,
|
|
|
|
|
Game,
|
|
|
|
|
GameExporter,
|
2022-11-26 10:15:40 -05:00
|
|
|
Summary,
|
2022-11-26 09:45:44 -05:00
|
|
|
VsInfo,
|
|
|
|
|
} from "../types.ts";
|
2022-10-22 06:40:20 -04:00
|
|
|
import { path } from "../../deps.ts";
|
2022-10-20 09:45:59 -04:00
|
|
|
import { NSOAPP_VERSION, S3SI_VERSION } from "../constant.ts";
|
2022-11-25 11:31:21 -05:00
|
|
|
import { parseHistoryDetailId, urlSimplify } from "../utils.ts";
|
2022-10-20 09:45:59 -04:00
|
|
|
|
2022-11-26 10:15:40 -05:00
|
|
|
export type FileExporterTypeCommon = {
|
2022-10-20 09:45:59 -04:00
|
|
|
nsoVersion: string;
|
|
|
|
|
s3siVersion: string;
|
|
|
|
|
exportTime: string;
|
|
|
|
|
};
|
|
|
|
|
|
2022-11-26 10:15:40 -05:00
|
|
|
export type FileExporterType =
|
|
|
|
|
& ({
|
|
|
|
|
type: "VS";
|
|
|
|
|
data: VsInfo;
|
|
|
|
|
} | {
|
|
|
|
|
type: "COOP";
|
|
|
|
|
data: CoopInfo;
|
|
|
|
|
} | {
|
|
|
|
|
type: "SUMMARY";
|
|
|
|
|
data: Summary;
|
|
|
|
|
})
|
|
|
|
|
& FileExporterTypeCommon;
|
|
|
|
|
|
2022-10-21 03:56:43 -04:00
|
|
|
/**
|
|
|
|
|
* Don't save url in exported file
|
|
|
|
|
*/
|
|
|
|
|
function replacer(key: string, value: unknown): unknown {
|
2022-11-25 11:31:21 -05:00
|
|
|
if (!["url", "maskImageUrl", "overlayImageUrl"].includes(key)) {
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return typeof value === "string" ? urlSimplify(value) : undefined;
|
2022-10-21 03:56:43 -04:00
|
|
|
}
|
|
|
|
|
|
2022-10-20 09:45:59 -04:00
|
|
|
/**
|
|
|
|
|
* Exporter to file.
|
|
|
|
|
*
|
|
|
|
|
* This is useful for debugging. It will write each battle detail to a file.
|
2022-10-20 23:14:29 -04:00
|
|
|
* Timestamp is used as filename. Example: 20210101T000000Z.json
|
2022-10-20 09:45:59 -04:00
|
|
|
*/
|
2022-10-24 14:45:36 -04:00
|
|
|
export class FileExporter implements GameExporter {
|
2022-10-20 09:45:59 -04:00
|
|
|
name = "file";
|
|
|
|
|
constructor(private exportPath: string) {
|
|
|
|
|
}
|
2022-10-20 23:14:29 -04:00
|
|
|
getFilenameById(id: string) {
|
2022-10-24 14:45:36 -04:00
|
|
|
const { uid, timestamp } = parseHistoryDetailId(id);
|
2022-10-21 03:56:43 -04:00
|
|
|
|
2022-10-22 06:40:20 -04:00
|
|
|
return `${uid}_${timestamp}Z.json`;
|
2022-10-20 23:14:29 -04:00
|
|
|
}
|
2022-11-26 04:25:25 -05:00
|
|
|
/**
|
|
|
|
|
* Get all exported files
|
|
|
|
|
*/
|
|
|
|
|
async exportedGames(
|
|
|
|
|
{ uid, type }: { uid: string; type: Game["type"] },
|
|
|
|
|
): Promise<{ id: string; getContent: () => Promise<Game> }[]> {
|
|
|
|
|
const out: { id: string; filepath: string; timestamp: string }[] = [];
|
|
|
|
|
|
|
|
|
|
for await (const entry of Deno.readDir(this.exportPath)) {
|
|
|
|
|
const filename = entry.name;
|
|
|
|
|
const [fileUid, timestamp] = filename.split("_", 2);
|
|
|
|
|
if (!entry.isFile || fileUid !== uid) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const filepath = path.join(this.exportPath, filename);
|
|
|
|
|
const content = await Deno.readTextFile(filepath);
|
|
|
|
|
const body = JSON.parse(content) as FileExporterType;
|
|
|
|
|
|
2022-11-26 10:15:40 -05:00
|
|
|
if (body.type === "SUMMARY") {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2022-11-26 04:25:25 -05:00
|
|
|
if (body.type === "VS" && type === "VsInfo") {
|
|
|
|
|
out.push({
|
|
|
|
|
id: body.data.detail.id,
|
|
|
|
|
filepath,
|
|
|
|
|
timestamp,
|
|
|
|
|
});
|
|
|
|
|
} else if (body.type === "COOP" && type === "CoopInfo") {
|
|
|
|
|
out.push({
|
|
|
|
|
id: body.data.detail.id,
|
|
|
|
|
filepath,
|
|
|
|
|
timestamp,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return out.sort((a, b) => b.timestamp.localeCompare(a.timestamp)).map((
|
|
|
|
|
{ id, filepath },
|
|
|
|
|
) => ({
|
|
|
|
|
id,
|
|
|
|
|
getContent: async () => {
|
|
|
|
|
const content = await Deno.readTextFile(filepath);
|
|
|
|
|
const body = JSON.parse(content) as FileExporterType;
|
|
|
|
|
|
2022-11-26 10:15:40 -05:00
|
|
|
// summary is excluded
|
|
|
|
|
return body.data as Game;
|
2022-11-26 04:25:25 -05:00
|
|
|
},
|
|
|
|
|
}));
|
|
|
|
|
}
|
2022-11-26 10:15:40 -05:00
|
|
|
async exportSummary(summary: Summary): Promise<ExportResult> {
|
|
|
|
|
const filename = `${summary.uid}_summary.json`;
|
|
|
|
|
const filepath = path.join(this.exportPath, filename);
|
|
|
|
|
|
|
|
|
|
const body: FileExporterType = {
|
|
|
|
|
type: "SUMMARY",
|
|
|
|
|
nsoVersion: NSOAPP_VERSION,
|
|
|
|
|
s3siVersion: S3SI_VERSION,
|
|
|
|
|
exportTime: new Date().toISOString(),
|
|
|
|
|
data: summary,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
await Deno.writeTextFile(
|
|
|
|
|
filepath,
|
2022-11-29 02:05:53 -05:00
|
|
|
JSON.stringify(body),
|
2022-11-26 10:15:40 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
status: "success",
|
|
|
|
|
url: filepath,
|
|
|
|
|
};
|
|
|
|
|
}
|
2022-11-26 09:45:44 -05:00
|
|
|
async exportGame(info: Game): Promise<ExportResult> {
|
2022-10-20 09:45:59 -04:00
|
|
|
await Deno.mkdir(this.exportPath, { recursive: true });
|
|
|
|
|
|
2022-10-24 14:45:36 -04:00
|
|
|
const filename = this.getFilenameById(info.detail.id);
|
2022-10-20 09:45:59 -04:00
|
|
|
const filepath = path.join(this.exportPath, filename);
|
|
|
|
|
|
2022-11-26 10:15:40 -05:00
|
|
|
const common: FileExporterTypeCommon = {
|
2022-10-20 09:45:59 -04:00
|
|
|
nsoVersion: NSOAPP_VERSION,
|
|
|
|
|
s3siVersion: S3SI_VERSION,
|
|
|
|
|
exportTime: new Date().toISOString(),
|
2022-11-26 10:15:40 -05:00
|
|
|
};
|
|
|
|
|
const dataType = info.type === "VsInfo"
|
|
|
|
|
? {
|
|
|
|
|
type: "VS" as const,
|
|
|
|
|
data: info,
|
|
|
|
|
}
|
|
|
|
|
: {
|
|
|
|
|
type: "COOP" as const,
|
|
|
|
|
data: info,
|
|
|
|
|
};
|
|
|
|
|
const body: FileExporterType = {
|
|
|
|
|
...common,
|
|
|
|
|
...dataType,
|
2022-10-20 09:45:59 -04:00
|
|
|
};
|
|
|
|
|
|
2022-10-21 03:56:43 -04:00
|
|
|
await Deno.writeTextFile(
|
|
|
|
|
filepath,
|
|
|
|
|
JSON.stringify(body, replacer),
|
|
|
|
|
);
|
2022-10-31 00:33:00 -04:00
|
|
|
|
|
|
|
|
return {
|
2022-11-26 09:45:44 -05:00
|
|
|
status: "success",
|
2022-10-31 00:33:00 -04:00
|
|
|
url: filepath,
|
|
|
|
|
};
|
2022-10-20 09:45:59 -04:00
|
|
|
}
|
2022-10-24 14:45:36 -04:00
|
|
|
async notExported({ list }: { list: string[] }): Promise<string[]> {
|
2022-10-20 18:50:25 -04:00
|
|
|
const out: string[] = [];
|
|
|
|
|
|
|
|
|
|
for (const id of list) {
|
2022-10-20 23:14:29 -04:00
|
|
|
const filename = this.getFilenameById(id);
|
2022-10-20 18:50:25 -04:00
|
|
|
const filepath = path.join(this.exportPath, filename);
|
|
|
|
|
const isFile = await Deno.stat(filepath).then((f) => f.isFile).catch(() =>
|
|
|
|
|
false
|
|
|
|
|
);
|
2022-10-20 22:57:08 -04:00
|
|
|
if (!isFile) {
|
2022-10-20 18:50:25 -04:00
|
|
|
out.push(id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return out;
|
2022-10-20 09:45:59 -04:00
|
|
|
}
|
|
|
|
|
}
|