-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbot.ts
206 lines (152 loc) · 6.54 KB
/
bot.ts
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
import { ActivityType, ApplicationCommandType, Client, ComponentType, InteractionType } from "discord.js";
import dotenv from "dotenv";
import k from "kleur";
import { allOfType, system, Stringifiable } from "svcorelib";
import botLogs from "@src/botLogs";
import { initRegistry, registerGuildCommands, registerEvents, getCommands, modalSubmitted, getCtxMenus, btnListener } from "@src/registry";
import { commands as slashCmds } from "@src/commands";
import { settings } from "@src/settings";
import { prisma } from "@database/client";
import { doContestStuff } from "@commands/fun/Contest/functions";
import { lavaRetrieveClient, clientReadyInitLava, clientUpdateVoiceStateLava } from "@src/lavalink/client";
import { getRedis } from "@src/redis";
import { registerFont } from "canvas";
const { env, exit } = process;
dotenv.config();
async function init()
{
console.log("Initializing...\n");
if(!allOfType([ env.BOT_TOKEN, env.CLIENT_ID ], "string"))
throw new Error("Missing environment variable(s). Please correct them according to the .env.template");
registerFont("src/assets/external/fonts/Roboto-Bold.ttf", {family: "Roboto"});
registerFont("src/assets/external/fonts/ChrysanthiUnicode-Regular.ttf", {family: "Chrysanthi Unicode"});
registerFont("src/assets/external/fonts/NotoSans-Regular.ttf", {family: "Noto Sans"});
const client = new Client({
intents: settings.client.intents,
});
lavaRetrieveClient(client);
client.login(env.BOT_TOKEN ?? "ERR_NO_ENV");
client.once("ready", async (cl) => {
const { user, guilds } = cl;
user.setPresence({
status: "dnd",
activities: [{ type: ActivityType.Playing, name: "starting up..." }]
});
await getRedis().connect();
botLogs.init(cl);
initRegistry(cl);
const evts = registerEvents().filter(e => e.enabled);
console.log(`• Registered ${k.green(evts.length)} client event${evts.length != 1 ? "s" : ""}`);
printDbgItmList(evts.map(e => e.constructor.name ?? e.names.join("&")));
await registerCommands(cl);
user.setPresence({
status: "online",
activities: [{ type: ActivityType.Watching, name: "ur mom" }],
});
console.log(`• Active in ${k.green(guilds.cache.size)} guild${guilds.cache.size != 1 ? "s" : ""}`);
printDbgItmList(guilds.cache.map(g => g.name), 4);
await doContestStuff(cl);
clientReadyInitLava(cl);
console.log(k.green(`\n${user.username} is ready.\n`));
process.stdin.isTTY && awaitKeypress();
ringBell();
});
client.on("error", err => {
console.error(`${k.red("Client error:")}\n${err}`);
});
client.on("raw", (d) => {
clientUpdateVoiceStateLava(d);
});
["SIGINT", "SIGTERM"].forEach(sig => process.on(sig, async () => {
console.log("Shutting down...");
await prisma.$disconnect();
client.user?.setPresence({ status: "dnd", activities: [{ type: ActivityType.Playing, name: "shutting down..." }] });
client.user?.setPresence({ status: "invisible", activities: [{ type: ActivityType.Playing, name: "shutting down..." }] });
setTimeout(() => exit(0), 100);
}));
}
async function awaitKeypress()
{
const key = await system.pause(`Actions: E${k.red("[x]")}it`);
switch(key)
{
case "x":
return process.exit(0);
}
awaitKeypress();
}
/**
* Registers all the bot's slash commands
* What gets registered is defined by the `index.ts` in the `commands` folder
*/
async function registerCommands(client: Client)
{
try
{
// register guild commands
// see https://discordjs.guide/interactions/slash-commands.html#guild-commands
const guilds = client.guilds.cache.map(g => g.id);
await registerGuildCommands(guilds);
}
catch(err)
{
console.error(k.red("Error while registering commands:\n") + (err instanceof Error) ? String(err) : "Unknown Error");
}
try
{
// listen for slash commands
const cmds = getCommands();
const ctxMenus = getCtxMenus();
if(!cmds)
throw new Error("No commands found to listen to");
console.log(`• Registered ${k.green(slashCmds.length)} slash command${slashCmds.length != 1 ? "s" : ""}`);
printDbgItmList(cmds.map(c => c.meta.name));
console.log(`• Registered ${k.green(ctxMenus.length)} context menu${ctxMenus.length != 1 ? "s" : ""}`);
printDbgItmList(ctxMenus.map(c => c.meta.name));
client.on("interactionCreate", async (int) => {
if(int.type === InteractionType.ApplicationCommand && int.commandType === ApplicationCommandType.ChatInput)
{
const { commandName, options } = int;
const opts = options.data && options.data.length > 0 ? options.data : undefined;
const cmd = cmds.find((cmd) => cmd.getFullCmdName(cmd.meta.name) === commandName);
if(!cmd || !cmd.enabled)
return;
await cmd.tryRun(int, Array.isArray(opts) ? opts[0] : opts);
}
else if(int.type === InteractionType.MessageComponent && int.componentType === ComponentType.Button)
btnListener.emitBtnPressed(int);
else if(int.type === InteractionType.ModalSubmit)
await modalSubmitted(int);
else if(int.type === InteractionType.ApplicationCommand) // It's implied that the type of command is either ApplicationCommandType.User or ApplicationCommandType.Message
{
const run = ctxMenus
.find(c => c.meta.name === int.commandName)
?.tryRun(int);
if(run instanceof Promise)
await run;
}
});
}
catch(err: unknown)
{
console.error(k.red("Error while listening for slash commands:\n") + k.red(err instanceof Error ? String(err) : "Unknown Error"));
}
}
/** Prints a styled list of items to the console * @param limit Max amount of items per line */
function printDbgItmList(list: string[] | Stringifiable[], limit = 6)
{
let msg = "";
list = list.map(itm => itm.toString()).sort();
while(list.length > 0)
{
const items = list.splice(0, limit);
msg += `│ ${k.gray(`${items.join(", ")}${items.length === 8 ? "," : ""}`)}\n`;
}
console.log(msg);
}
/** Triggers the console bell sound */
function ringBell()
{
settings.debug.bellOnReady && process.stdout.write("\u0007");
}
init();