Skip to content
This repository was archived by the owner on May 15, 2025. It is now read-only.

Commit 551ebda

Browse files
committed
EVENT LISTENER WOOO
1 parent ccfff18 commit 551ebda

File tree

6 files changed

+186
-10
lines changed

6 files changed

+186
-10
lines changed

RM5_LICENSE.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Copyright 2023 Jaiden S (imskyyc)
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4+
5+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6+
7+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"build": "tsc",
99
"build:watch": "tsc -w",
1010
"start:prod": "node .",
11-
"start:dev": "node . --debug",
11+
"start:dev": "nodemon . --debug",
1212
"start:ci": "pnpm run build && echo Successfully built! && node . --ci"
1313
},
1414
"keywords": [],

src/events/ready.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { ActivityType } from "discord.js";
2+
import { Client } from 'discord.js';
3+
import { KOGBot } from "index.js";
4+
5+
export default async (kogBot: KOGBot, client: Client) => {
6+
console.log(`KOG Bot Ready!`);
7+
8+
kogBot.discord_client.user?.setPresence({
9+
status: "idle",
10+
activities: [{
11+
name: "with your mom",
12+
type: ActivityType.Playing
13+
}]
14+
});
15+
};

src/index.ts

Lines changed: 135 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
import { Client, IntentsBitField, Message, REST } from "discord.js";
1+
import { Client, Collection, IntentsBitField, REST, RESTPostAPIApplicationCommandsJSONBody, SlashCommandBuilder, SlashCommandSubcommandBuilder, Routes } from "discord.js";
22
import fs from 'fs';
33
import knex, { Knex } from "knex";
44
import toml from 'toml';
55
import * as Sentry from '@sentry/node';
66
import { nodeProfilingIntegration } from "@sentry/profiling-node";
7+
import { readdir, lstat } from "fs/promises";
8+
import url from "url";
9+
10+
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
711

812
class DBot extends Client {
913
kogBot: KOGBot;
1014
REST: REST = new REST();
15+
slashCommands: SlashCommandBuilder[] = [];
1116

1217
constructor (kogBot: KOGBot) {
1318
super({
@@ -20,21 +25,143 @@ class DBot extends Client {
2025
this.kogBot = kogBot;
2126

2227
this.REST.setToken(this.kogBot.environment.discord.client_token);
28+
this.events.load().then(() => {
29+
console.log("Events loaded successfully.");
30+
})
2331

2432
if (!kogBot.ci_workflow) {
2533
this.login(this.kogBot.environment.discord.client_token);
34+
}
35+
}
36+
37+
commands = {
38+
list: new Collection<string, Collection<string, SlashCommand>>(),
39+
40+
read: async(): Promise<Collection<string, Collection<string, SlashCommand>>> => {
41+
const categoryDirs = await readdir(`${__dirname}/commands`);
42+
const commandList = new Collection<string, Collection<string, SlashCommand>>
43+
44+
for (const categoryDir of categoryDirs) {
45+
const category = new Collection<string, SlashCommand>();
46+
const commandFiles = await readdir(`${__dirname}/commands/${categoryDir}`)
47+
for (const commandFile of commandFiles) {
48+
const commandFileStat = await lstat(`${__dirname}/commands/${categoryDir}/${commandFile}`);
49+
const commandClass = (await import(`./commands/${categoryDir}/${commandFile}${commandFileStat.isDirectory() && '/index.js' || ''}`)).default;
50+
const commandData: SlashCommand = new commandClass(this.kogBot);
51+
52+
if (commandFileStat.isDirectory()) {
53+
if (!commandData.subcommands) {
54+
commandData.subcommands = [];
55+
}
56+
commandData.isIndexer = true;
57+
}
58+
category.set(commandData.name, commandData);
59+
}
60+
commandList.set(categoryDir, category);
61+
}
62+
return commandList
63+
},
64+
65+
push: async () => {
66+
const commandsList = await this.commands.read();
67+
this.commands.list = commandsList;
68+
69+
const parsedCommands: RESTPostAPIApplicationCommandsJSONBody[] = [];
70+
71+
for (const categoryName of commandsList.keys()) {
72+
const category = commandsList.get(categoryName);
73+
74+
if (!category) {
75+
console.log(`Category ${categoryName} is null!`)
76+
continue;
77+
}
78+
79+
for (const command of category.values()) {
80+
const slashCommand = new SlashCommandBuilder();
81+
slashCommand.setName(command.name);
82+
slashCommand.setDescription(command.description);
83+
slashCommand.setDefaultMemberPermissions(command.defaultMemberPermissions);
84+
85+
for (const option of command.options || []) {
86+
slashCommand.options.push(option);
87+
}
88+
89+
if (command.isIndexer) {
90+
const subCommandFiles = await readdir(`${__dirname}/commands/${categoryName}/${command.name}`);
91+
92+
for (const subCommandFile of subCommandFiles) {
93+
if (subCommandFile.includes('index.')) continue;
94+
95+
const subCommandClass = (await import(`./commands/${categoryName}/${command.name}/${subCommandFile}`)).default;
96+
const subCommandData: SlashCommand = new subCommandClass(this.kogBot);
97+
98+
const subCommand = new SlashCommandSubcommandBuilder();
2699

27-
this.on('ready', () => {
28-
console.log(`Logged in as ${this.user?.tag}`);
29-
});
100+
subCommand.setName(subCommandData.name);
101+
subCommand.setDescription(subCommandData.description);
30102

31-
this.on('messageCreate', (message: Message) => {
32-
if (message.content === 'hi') {
33-
message.reply('hi');
103+
for (const option of subCommandData.options || []) {
104+
subCommand.options.push(option);
105+
}
106+
107+
command.subcommands?.push(subCommandData);
108+
109+
slashCommand.addSubcommand(subCommand);
110+
}
111+
}
112+
113+
this.slashCommands.push(slashCommand);
114+
115+
parsedCommands.push(slashCommand.toJSON())
34116
}
35-
});
117+
}
118+
119+
if (this.kogBot.ci_workflow) {
120+
console.log("Pushing commands disabled in CI mode.")
121+
process.exit(0);
122+
}
123+
124+
await this.REST.put(Routes.applicationGuildCommands(
125+
this.kogBot.environment.DISCORD_CLIENT_ID,
126+
this.kogBot.environment.DISCORD_GUILD_ID
127+
), {
128+
body: parsedCommands
129+
})
36130
}
37131
}
132+
133+
events: BotEventObject = {
134+
functions: [],
135+
136+
read: async () => {
137+
const eventFiles = await readdir(`${__dirname}/events`);
138+
const events: BotEvent[] = [];
139+
140+
for (const eventFile of eventFiles) {
141+
const eventFunction = (await import(`./events/${eventFile}`)).default;
142+
143+
events.push({
144+
name: eventFile.replace(/\.[^/.]+$/, ""),
145+
function: eventFunction.bind(null, this.kogBot)
146+
});
147+
}
148+
149+
return events;
150+
},
151+
152+
load: async () => {
153+
for (const event of Object.values(this.events.functions)) {
154+
this.removeAllListeners(event.name);
155+
}
156+
157+
const events = await this.events.read();
158+
this.events.functions = events;
159+
160+
for (const event of events) {
161+
this.on(event.name, event.function);
162+
}
163+
}
164+
};
38165
}
39166

40167
class KOGBot_Client {

src/types/main.d.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
declare interface SlashCommand {
2+
name: Lowercase<string>;
3+
description: string;
4+
defaultMemberPermissions?: string | number | bigint | null | undefined;
5+
subcommands?: ICommand[];
6+
options?: ApplicationCommandOptionBase[];
7+
developer?: boolean;
8+
mr?: boolean;
9+
hr?: boolean
10+
11+
isIndexer?: boolean;
12+
hspsManager: HSPSManager;
13+
14+
execute(interaction: ChatInputCommandInteraction): Promise<void>;
15+
autocomplete?(interaction: AutocompleteInteraction): Promise<void>;
16+
}
17+
18+
interface BotEventObject {
19+
functions: IEvent[];
20+
read: () => Promise<IEvent[]>
21+
load: () => Promise<void>;
22+
}
23+
24+
declare interface BotEvent {
25+
name: string,
26+
function: () => Promise<void>
27+
}

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
/* Language and Environment */
1414
"target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
15-
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
15+
"lib": ["ESNext"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
1616
// "jsx": "preserve", /* Specify what JSX code is generated. */
1717
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
1818
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */

0 commit comments

Comments
 (0)