-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.mjs
117 lines (106 loc) · 3.76 KB
/
index.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import { setTimeout as wait } from 'node:timers/promises';
import { Client, Collection, GatewayIntentBits } from 'discord.js';
import { importDir } from './util/importDir.mjs';
import { pkgRelPath } from './util/pkgRelPath.mjs';
import { importJSON } from './util/importJSON.mjs';
const {
token,
developmentGuildTestChannel,
} = await importJSON(pkgRelPath('./config.json'));
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
// To hopefully reduce AbortError: The user aborted a request:
restRequestTimeout: 60_000,
retryLimit: 5,
});
// Import each command module and save it to a collection
client.commands = new Collection();
const commands = await importDir(pkgRelPath('./commands/'));
for (const command of commands) {
client.commands.set(command.data.name, command);
}
client.reportError = async (content) => {
const channel = await client.channels.fetch(developmentGuildTestChannel);
await channel.send(content);
};
// Get a list of event modules from the /events directory
const events = await importDir(pkgRelPath('./events/'));
// Attach event listeners declared in each event module
for (const event of events) {
if (event.once) {
client.once(event.name, (...args) => event.execute(...args));
}
else {
client.on(event.name, (...args) => event.execute(...args));
}
}
// The previous steps were just setup, which didn't actually require the client
// to be logged in, but for routines, there's no point in starting them before
// log in, so we await:
console.log('Attempting to log in...');
const LOGIN_RETRIES = 5;
const LOGIN_BASE_DELAY_MS = 100;
const LOGIN_EXPONENT_BASE = 2;
let attempts = 0;
while (true) {
try {
await client.login(token);
break;
}
catch (HTTPError) {
++attempts;
if (attempts >= LOGIN_RETRIES) {
throw new Error(
`Failed to log in after ${attempts} retries`,
{ cause: HTTPError },
);
}
console.error(`Login attempt ${attempts} failed. Trying again...`);
await wait(LOGIN_BASE_DELAY_MS * LOGIN_EXPONENT_BASE ** (attempts - 1));
}
}
console.log('Success! Logged in.');
// Get a list of routine modules from the /routines directory. These are
// basically scripts that run on a set frequency/schedule.
// See /routines/README.md for more info.
const routines = await importDir(pkgRelPath('./routines/'));
// Initialize routines via setTimeout:
const timeoutIds = [];
// Whether to run every routine immediately when the bot logs in, or whether
// to wait for each one's timeout first:
const RUN_ON_STARTUP = false;
let index = 0;
for (const routine of routines) {
if (routine.enabled !== undefined && !routine.enabled) {
continue;
}
const loopTimeout = async () => {
console.log(`Running routine "${routine.name}"...`);
try {
await routine.execute(client);
}
catch (RoutineError) {
// If a routine has an error, all we need to do to temporarily disable it
// is to return without calling setTimeout again:
console.error(`Routine "${routine.name}" failed with the following error, and was disabled. Restart index.mjs to run this routine again:`, RoutineError);
try {
await client.reportError(`Routine "${routine.name}" failed with a ${RoutineError.name} error. Please restart the bot to run this routine again. "${RoutineError.message}"`);
}
catch (ErrorReportingError) {
console.log(`ERROR! Failed to report an error with routine "${routine.name}". Restarting to hopefully fix this...`);
process.kill(process.pid, 'SIGTERM');
}
return;
}
console.log(`Routine "${routine.name}" completed.`);
timeoutIds[index] = setTimeout(loopTimeout, routine.interval_ms);
};
if (RUN_ON_STARTUP) {
loopTimeout();
}
else {
const initialDelay = routine.initialDelay_ms ?? routine.interval_ms;
timeoutIds[index] = setTimeout(loopTimeout, initialDelay);
}
++index;
}