|
1 | 1 | import {readFile} from 'node:fs/promises'; |
| 2 | +import {pull} from './git.ts'; |
| 3 | +import {typedEntries} from './javascript-helper.ts'; |
| 4 | +import type {EventDirectory, EventEntry, EventId} from './types.ts'; |
2 | 5 |
|
3 | | -async function getAll(): Promise<string[]> { |
4 | | - const data = await readFile('eventfiles/all.txt', 'utf8'); |
5 | | - const list = data.split('\n').filter(element => element !== ''); |
6 | | - return list; |
7 | | -} |
| 6 | +let directory: EventDirectory = {}; |
| 7 | +let namesOfEvents: Readonly<Record<EventId, string>> = {}; |
| 8 | + |
| 9 | +setInterval(async () => update(), 1000 * 60 * 30); // Every 30 minutes |
| 10 | +await update(); |
| 11 | +console.log(new Date(), 'eventfiles loaded'); |
8 | 12 |
|
9 | | -export async function count(): Promise<number> { |
10 | | - const allEvents = await getAll(); |
11 | | - return allEvents.length; |
| 13 | +async function update() { |
| 14 | + await pull( |
| 15 | + 'eventfiles', |
| 16 | + 'https://github.com/HAWHHCalendarBot/eventfiles.git', |
| 17 | + ); |
| 18 | + const directoryString = await readFile('eventfiles/directory.json', 'utf8'); |
| 19 | + directory = JSON.parse(directoryString) as EventDirectory; |
| 20 | + namesOfEvents = await generateMapping(); |
12 | 21 | } |
13 | 22 |
|
14 | | -export async function exists(name: string): Promise<boolean> { |
15 | | - const allEvents = await getAll(); |
16 | | - return allEvents.includes(name); |
| 23 | +async function generateMapping(): Promise<Readonly<Record<EventId, string>>> { |
| 24 | + const namesOfEvents: Record<EventId, string> = {}; |
| 25 | + |
| 26 | + function collect(directory: EventDirectory) { |
| 27 | + for (const subDirectory of Object.values(directory.subDirectories ?? {})) { |
| 28 | + collect(subDirectory); |
| 29 | + } |
| 30 | + |
| 31 | + Object.assign(namesOfEvents, directory.events ?? {}); |
| 32 | + } |
| 33 | + |
| 34 | + collect(directory); |
| 35 | + return namesOfEvents; |
17 | 36 | } |
18 | 37 |
|
19 | | -export async function nonExisting(names: readonly string[]): Promise<string[]> { |
20 | | - const allEvents = new Set(await getAll()); |
21 | | - const result: string[] = []; |
22 | | - for (const event of names) { |
23 | | - if (!allEvents.has(event)) { |
24 | | - result.push(event); |
| 38 | +function getSubdirectory(path: string[]): EventDirectory | undefined { |
| 39 | + let resolvedDirectory = directory; |
| 40 | + |
| 41 | + for (const part of path) { |
| 42 | + const subDirectory = resolvedDirectory.subDirectories?.[part]; |
| 43 | + if (subDirectory === undefined) { |
| 44 | + return undefined; |
25 | 45 | } |
| 46 | + |
| 47 | + resolvedDirectory = subDirectory; |
| 48 | + } |
| 49 | + |
| 50 | + return resolvedDirectory; |
| 51 | +} |
| 52 | + |
| 53 | +export function directoryHasContent(directory: EventDirectory): boolean { |
| 54 | + const events = Object.keys(directory.events ?? {}).length; |
| 55 | + const subDirectories = Object.keys(directory.subDirectories ?? {}).length; |
| 56 | + return events > 0 || subDirectories > 0; |
| 57 | +} |
| 58 | + |
| 59 | +export function directoryExists(path: string[]): boolean { |
| 60 | + if (path.length === 0) { |
| 61 | + // Toplevel always exists |
| 62 | + return true; |
26 | 63 | } |
27 | 64 |
|
28 | | - return result; |
| 65 | + const directory = getSubdirectory(path); |
| 66 | + return Boolean(directory && directoryHasContent(directory)); |
29 | 67 | } |
30 | 68 |
|
31 | | -export async function find( |
32 | | - pattern: string | RegExp, |
33 | | - ignore: readonly string[] = [], |
34 | | -): Promise<readonly string[]> { |
35 | | - const allEvents = await getAll(); |
| 69 | +export function getEventName(id: EventId): string { |
| 70 | + return namesOfEvents[id] ?? id; |
| 71 | +} |
| 72 | + |
| 73 | +export function count(): number { |
| 74 | + return Object.keys(namesOfEvents).length; |
| 75 | +} |
| 76 | + |
| 77 | +export function exists(id: EventId): boolean { |
| 78 | + return id in namesOfEvents; |
| 79 | +} |
| 80 | + |
| 81 | +export function find( |
| 82 | + path: string[], |
| 83 | + pattern: string | RegExp | undefined, |
| 84 | +): EventDirectory { |
| 85 | + if (!pattern) { |
| 86 | + return getSubdirectory(path) ?? {}; |
| 87 | + } |
| 88 | + |
36 | 89 | const regex = new RegExp(pattern, 'i'); |
37 | | - const filtered = allEvents.filter(event => |
38 | | - regex.test(event) && !ignore.includes(event)); |
39 | | - return filtered; |
| 90 | + const accumulator: Record<EventId, string> = {}; |
| 91 | + |
| 92 | + function collect(directory: EventDirectory) { |
| 93 | + for (const [eventId, name] of typedEntries(directory.events ?? {})) { |
| 94 | + if (regex.test(name)) { |
| 95 | + accumulator[eventId] = name; |
| 96 | + } |
| 97 | + } |
| 98 | + |
| 99 | + for (const subDirectory of Object.values(directory.subDirectories ?? {})) { |
| 100 | + collect(subDirectory); |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + collect(getSubdirectory(path) ?? {}); |
| 105 | + return { |
| 106 | + events: Object.fromEntries(typedEntries(accumulator).sort((a, b) => a[1].localeCompare(b[1]))), |
| 107 | + }; |
| 108 | +} |
| 109 | + |
| 110 | +export async function loadEvents(eventId: EventId): Promise<EventEntry[]> { |
| 111 | + const content = await readFile(`eventfiles/events/${eventId}.json`, 'utf8'); |
| 112 | + return JSON.parse(content) as EventEntry[]; |
40 | 113 | } |
0 commit comments