char-slop/city-role-bot

discord auto role applicator bot for charlotte polycule city

git clone https://git.t4t.associates/char-slop/city-role-bot

Charlotte Somsimplifyd72003f

main
1.7 KiB63 linesraw
1import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
2
3export interface RolePanel {
4  channelID: string
5  /** unicode emoji, or `name:id` for custom emoji */
6  emoji: string
7  guildID: string
8  /** users known to have the reaction, kept in step with role grants */
9  reactors: Set<string>
10  roleID: string
11}
12
13type StoredPanel = Omit<RolePanel, 'reactors'> & { reactors?: string[] }
14
15const DATA_DIR = new URL('../data/', import.meta.url)
16const FILE = new URL('role-panels.json', DATA_DIR)
17
18function load(): Map<string, RolePanel> {
19  let raw: string
20  try {
21    raw = readFileSync(FILE, 'utf8')
22  } catch (error) {
23    if ((error as NodeJS.ErrnoException).code === 'ENOENT') return new Map()
24    throw error
25  }
26  return new Map(
27    Object.entries(JSON.parse(raw) as Record<string, StoredPanel>).map(([messageID, panel]) => [
28      messageID,
29      // files written before reactors were tracked
30      { ...panel, reactors: new Set(panel.reactors ?? []) }
31    ])
32  )
33}
34
35function save(): void {
36  mkdirSync(DATA_DIR, { recursive: true })
37  const json = JSON.stringify(
38    Object.fromEntries(panels),
39    (_, value: unknown) => (value instanceof Set ? [...value] : value),
40    2
41  )
42  writeFileSync(FILE, json)
43}
44
45/** Panel registry, keyed by panel message ID. */
46export const panels = load()
47
48export function setPanel(messageID: string, panel: RolePanel): void {
49  panels.set(messageID, panel)
50  save()
51}
52
53export function deletePanel(messageID: string): boolean {
54  if (!panels.delete(messageID)) return false
55  save()
56  return true
57}
58
59export function setReactor(panel: RolePanel, userID: string, reacted: boolean): void {
60  if (reacted) panel.reactors.add(userID)
61  else panel.reactors.delete(userID)
62  save()
63}