import { createEntry, Entry, Type } from "./entry.ts"; const configPath = Deno.env.get("CONFIG_PATH") ?? "./config.json"; let pendingWrite = Promise.resolve(); export function writeConfig(entries: Entry[]): Promise { const data = `${JSON.stringify(entries, null, 2)}\n`; pendingWrite = pendingWrite .catch(() => undefined) .then(async () => { const temporaryPath = `${configPath}.tmp`; await Deno.writeTextFile(temporaryPath, data); await Deno.chmod(temporaryPath, 0o600); await Deno.rename(temporaryPath, configPath); }); return pendingWrite; } export function readConfig(): Entry[] { let data: string; try { data = Deno.readTextFileSync(configPath); } catch (error) { if (error instanceof Deno.errors.NotFound) return []; throw error; } const parsed: unknown = JSON.parse(data); if (!Array.isArray(parsed)) throw new Error("config must contain an array"); return parsed.map((value, index) => parseEntry(value, index)); } function parseEntry(value: unknown, index: number): Entry { if (!value || typeof value !== "object") { throw new Error(`config entry ${index} is invalid`); } const raw = value as Record; if (typeof raw.server !== "string" || raw.server.length === 0) { throw new Error(`config entry ${index} has no server ID`); } const entry = createEntry(raw.server); if (typeof raw.output_channel === "string") entry.output_channel = raw.output_channel; if (raw.channel_type === Type.INCLUDE || raw.channel_type === Type.EXCLUDE) { entry.channel_type = raw.channel_type; } if (Array.isArray(raw.channels)) entry.channels = uniqueStrings(raw.channels); if (raw.emoji_type === Type.INCLUDE || raw.emoji_type === Type.EXCLUDE) { entry.emoji_type = raw.emoji_type; } if (Array.isArray(raw.emojis)) entry.emojis = uniqueStrings(raw.emojis); if (typeof raw.score === "number" && Number.isFinite(raw.score) && raw.score > 0) { entry.score = raw.score; } if (raw.awards && typeof raw.awards === "object" && !Array.isArray(raw.awards)) { entry.awards = Object.fromEntries( Object.entries(raw.awards).filter( (item): item is [string, string] => typeof item[1] === "string", ), ); } return entry; } function uniqueStrings(values: unknown[]): string[] { return [...new Set(values.filter((item): item is string => typeof item === "string"))]; }