feature/devops #1
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"name": "butlerbotng",
|
||||
"name": "butlerbot",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"start": "node dist/index.js",
|
||||
"build": "tsup src/index.ts --minify",
|
||||
"register-slash-commands": "tsx src/register-slash-commands.ts",
|
||||
"register-slash-commands": "tsx src/registerSlashCommands.ts",
|
||||
"dev": "tsx watch src/index.ts",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.3t.network/terriblecodeclub/butlerbotng.git"
|
||||
"url": "https://git.3t.network/terriblecodeclub/butlerbot.git"
|
||||
},
|
||||
"author": "Butlersaurus",
|
||||
"license": "ISC",
|
||||
@@ -23,13 +23,13 @@
|
||||
"dotenv": "^16.4.5",
|
||||
"glob": "^11.0.0",
|
||||
"mongodb": "^6.8.1",
|
||||
"prettier": "^3.3.3",
|
||||
"replicate": "^0.32.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/glob": "^8.1.0",
|
||||
"@types/node": "^20.4.0",
|
||||
"eslint": "^9.9.1",
|
||||
"prettier": "^3.3.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsup": "^8.2.4",
|
||||
"tsx": "^4.19.1",
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import * as birthday from './commands/birthday';
|
||||
import * as corrupt from './commands/corrupt';
|
||||
import * as countdown from './commands/countdown';
|
||||
import * as eyecandy from './commands/eyecandy';
|
||||
import * as game from './commands/game';
|
||||
import * as image from './commands/image';
|
||||
import * as imdb from './commands/imdb';
|
||||
import * as kanye from './commands/kanye';
|
||||
import * as magicEightBall from './commands/magic8Ball';
|
||||
import * as payday from './commands/payday';
|
||||
import * as plant from './commands/plant';
|
||||
import * as reminder from './commands/reminder';
|
||||
import * as servertime from './commands/servertime';
|
||||
import * as taylor from './commands/taylor';
|
||||
import * as twentyTwenty from './commands/twentyTwenty';
|
||||
|
||||
export const commands = {
|
||||
birthday,
|
||||
corrupt,
|
||||
countdown,
|
||||
eyecandy,
|
||||
game,
|
||||
image,
|
||||
imdb,
|
||||
kanye,
|
||||
magicEightBall,
|
||||
payday,
|
||||
plant,
|
||||
reminder,
|
||||
servertime,
|
||||
taylor,
|
||||
twentyTwenty,
|
||||
};
|
||||
@@ -10,9 +10,10 @@ interface Config {
|
||||
omdbApiKey: string;
|
||||
replicateApiKey: string;
|
||||
mongodbUri: string;
|
||||
registerSlashCommands?: boolean;
|
||||
}
|
||||
|
||||
const getEnv = (key: string, required = true): string | undefined => {
|
||||
const getEnv = (key: string, required = true): string | boolean | undefined => {
|
||||
const value = process.env[key];
|
||||
if (!value && required) {
|
||||
throw new Error(`${key} is not set in the environment variables.`);
|
||||
@@ -28,6 +29,7 @@ const config: Config = {
|
||||
omdbApiKey: getEnv('OMDB_API_KEY') as string,
|
||||
replicateApiKey: getEnv('REPLICATE_API_KEY') as string,
|
||||
mongodbUri: getEnv('MONGODB_URI') as string,
|
||||
registerSlashCommands: getEnv('REGISTER_SLASH_COMMANDS', false) as boolean,
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
35
src/index.ts
35
src/index.ts
@@ -6,25 +6,50 @@ import {
|
||||
GatewayIntentBits,
|
||||
Interaction,
|
||||
} from 'discord.js';
|
||||
import { commands } from './commands';
|
||||
import { getCommands, registerSlashCommands } from './utils/commands';
|
||||
import { Command } from './utils/types';
|
||||
import config from './config';
|
||||
|
||||
// Define an extended version of the Client interface to include commands
|
||||
interface ExtendedClient extends Client {
|
||||
commands: Collection<string, any>;
|
||||
commands: Collection<string, Command>;
|
||||
}
|
||||
|
||||
const client: ExtendedClient = new Client({
|
||||
intents: [GatewayIntentBits.Guilds],
|
||||
}) as ExtendedClient;
|
||||
|
||||
// Add the commands to the client
|
||||
client.commands = getCommands();
|
||||
|
||||
// If REGISTER_SLASH_COMMANDS is set to true, register the commands.
|
||||
if (config.registerSlashCommands) {
|
||||
registerSlashCommands(client.commands)
|
||||
.then(() => {
|
||||
console.log('Successfully registered slash commands');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to register slash commands:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
// Register the event listener for command interactions.
|
||||
client.on(Events.InteractionCreate, async (interaction: Interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
const { commandName } = interaction;
|
||||
const command = client.commands.get(interaction.commandName);
|
||||
|
||||
if (commands[commandName as keyof typeof commands]) {
|
||||
commands[commandName as keyof typeof commands].execute(interaction);
|
||||
if (command) {
|
||||
try {
|
||||
await command.execute(interaction);
|
||||
} catch (error) {
|
||||
console.error(`Error executing ${interaction.commandName}:`, error);
|
||||
await interaction.reply({
|
||||
content: 'There was an error executing that command!',
|
||||
ephemeral: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { REST, Routes } from 'discord.js';
|
||||
import { commands } from './commands';
|
||||
import config from './config';
|
||||
|
||||
// Register all slash commands, globally across all Guilds.
|
||||
const commandData = Object.values(commands).map(
|
||||
(command) => command.data.toJSON() as any
|
||||
);
|
||||
|
||||
const rest = new REST().setToken(config.discordApiKey);
|
||||
|
||||
try {
|
||||
console.log('Started refreshing application (/) commands.');
|
||||
|
||||
// Refresh all slash commands globally.
|
||||
rest.put(Routes.applicationCommands(config.discordApplicationId), {
|
||||
body: commandData,
|
||||
});
|
||||
|
||||
// Exit the process.
|
||||
process.exit();
|
||||
} catch (error) {
|
||||
// And of course, make sure you catch and log any errors!
|
||||
console.error(error);
|
||||
}
|
||||
12
src/registerSlashCommands.ts
Normal file
12
src/registerSlashCommands.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { getCommands, registerSlashCommands } from './utils/commands';
|
||||
|
||||
const commands = getCommands();
|
||||
|
||||
registerSlashCommands(commands)
|
||||
.then(() => {
|
||||
console.log('Successfully registered slash commands');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to register slash commands:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
58
src/utils/commands.ts
Normal file
58
src/utils/commands.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { Collection, REST, Routes } from 'discord.js';
|
||||
import * as birthday from '../commands/birthday';
|
||||
import * as corrupt from '../commands/corrupt';
|
||||
import * as countdown from '../commands/countdown';
|
||||
import * as eyecandy from '../commands/eyecandy';
|
||||
import * as game from '../commands/game';
|
||||
import * as image from '../commands/image';
|
||||
import * as imdb from '../commands/imdb';
|
||||
import * as kanye from '../commands/kanye';
|
||||
import * as magicEightBall from '../commands/magic8Ball';
|
||||
import * as payday from '../commands/payday';
|
||||
import * as plant from '../commands/plant';
|
||||
import * as reminder from '../commands/reminder';
|
||||
import * as servertime from '../commands/servertime';
|
||||
import * as taylor from '../commands/taylor';
|
||||
import * as twentyTwenty from '../commands/twentyTwenty';
|
||||
import config from '../config';
|
||||
import { Command } from './types';
|
||||
|
||||
/**
|
||||
* Get all commands as a collection.
|
||||
* @returns A collection of commands.
|
||||
*/
|
||||
export function getCommands(): Collection<string, Command> {
|
||||
const commands = new Collection<string, Command>();
|
||||
|
||||
commands.set(birthday.data.name, birthday);
|
||||
commands.set(corrupt.data.name, corrupt);
|
||||
commands.set(countdown.data.name, countdown);
|
||||
commands.set(eyecandy.data.name, eyecandy);
|
||||
commands.set(game.data.name, game);
|
||||
commands.set(image.data.name, image);
|
||||
commands.set(imdb.data.name, imdb);
|
||||
commands.set(kanye.data.name, kanye);
|
||||
commands.set(magicEightBall.data.name, magicEightBall);
|
||||
commands.set(payday.data.name, payday);
|
||||
commands.set(plant.data.name, plant);
|
||||
commands.set(reminder.data.name, reminder);
|
||||
commands.set(servertime.data.name, servertime);
|
||||
commands.set(taylor.data.name, taylor);
|
||||
commands.set(twentyTwenty.data.name, twentyTwenty);
|
||||
|
||||
return commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all slash commands globally across all Guilds.
|
||||
* @param commands A collection of commands to register.
|
||||
*/
|
||||
export async function registerSlashCommands(
|
||||
commands: Collection<string, Command>
|
||||
) {
|
||||
const commandData = commands.map((command) => command.data.toJSON());
|
||||
const rest = new REST({ version: '10' }).setToken(config.discordApiKey);
|
||||
await rest.put(Routes.applicationCommands(config.discordApplicationId), {
|
||||
body: commandData,
|
||||
});
|
||||
}
|
||||
18
src/utils/types.ts
Normal file
18
src/utils/types.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import {
|
||||
SlashCommandBuilder,
|
||||
SlashCommandSubcommandsOnlyBuilder,
|
||||
SlashCommandOptionsOnlyBuilder,
|
||||
ChatInputCommandInteraction,
|
||||
} from 'discord.js';
|
||||
|
||||
// Create a generic type to cover all relevant SlashCommandBuilder types
|
||||
export type CommandBuilder =
|
||||
| SlashCommandBuilder
|
||||
| SlashCommandSubcommandsOnlyBuilder
|
||||
| SlashCommandOptionsOnlyBuilder;
|
||||
|
||||
// Define the Command interface
|
||||
export interface Command {
|
||||
data: CommandBuilder; // Use the generic CommandBuilder type
|
||||
execute: (interaction: ChatInputCommandInteraction) => Promise<void>;
|
||||
}
|
||||
Reference in New Issue
Block a user