feature/devops #1
@@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "butlerbotng",
|
"name": "butlerbot",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node dist/index.js",
|
"start": "node dist/index.js",
|
||||||
"build": "tsup src/index.ts --minify",
|
"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",
|
"dev": "tsx watch src/index.ts",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.3t.network/terriblecodeclub/butlerbotng.git"
|
"url": "https://git.3t.network/terriblecodeclub/butlerbot.git"
|
||||||
},
|
},
|
||||||
"author": "Butlersaurus",
|
"author": "Butlersaurus",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
@@ -23,13 +23,13 @@
|
|||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"glob": "^11.0.0",
|
"glob": "^11.0.0",
|
||||||
"mongodb": "^6.8.1",
|
"mongodb": "^6.8.1",
|
||||||
"prettier": "^3.3.3",
|
|
||||||
"replicate": "^0.32.1"
|
"replicate": "^0.32.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/glob": "^8.1.0",
|
"@types/glob": "^8.1.0",
|
||||||
"@types/node": "^20.4.0",
|
"@types/node": "^20.4.0",
|
||||||
"eslint": "^9.9.1",
|
"eslint": "^9.9.1",
|
||||||
|
"prettier": "^3.3.3",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"tsup": "^8.2.4",
|
"tsup": "^8.2.4",
|
||||||
"tsx": "^4.19.1",
|
"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;
|
omdbApiKey: string;
|
||||||
replicateApiKey: string;
|
replicateApiKey: string;
|
||||||
mongodbUri: 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];
|
const value = process.env[key];
|
||||||
if (!value && required) {
|
if (!value && required) {
|
||||||
throw new Error(`${key} is not set in the environment variables.`);
|
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,
|
omdbApiKey: getEnv('OMDB_API_KEY') as string,
|
||||||
replicateApiKey: getEnv('REPLICATE_API_KEY') as string,
|
replicateApiKey: getEnv('REPLICATE_API_KEY') as string,
|
||||||
mongodbUri: getEnv('MONGODB_URI') as string,
|
mongodbUri: getEnv('MONGODB_URI') as string,
|
||||||
|
registerSlashCommands: getEnv('REGISTER_SLASH_COMMANDS', false) as boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
35
src/index.ts
35
src/index.ts
@@ -6,25 +6,50 @@ import {
|
|||||||
GatewayIntentBits,
|
GatewayIntentBits,
|
||||||
Interaction,
|
Interaction,
|
||||||
} from 'discord.js';
|
} 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
|
// Define an extended version of the Client interface to include commands
|
||||||
interface ExtendedClient extends Client {
|
interface ExtendedClient extends Client {
|
||||||
commands: Collection<string, any>;
|
commands: Collection<string, Command>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const client: ExtendedClient = new Client({
|
const client: ExtendedClient = new Client({
|
||||||
intents: [GatewayIntentBits.Guilds],
|
intents: [GatewayIntentBits.Guilds],
|
||||||
}) as ExtendedClient;
|
}) 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.
|
// Register the event listener for command interactions.
|
||||||
client.on(Events.InteractionCreate, async (interaction: Interaction) => {
|
client.on(Events.InteractionCreate, async (interaction: Interaction) => {
|
||||||
if (!interaction.isChatInputCommand()) return;
|
if (!interaction.isChatInputCommand()) return;
|
||||||
|
|
||||||
const { commandName } = interaction;
|
const command = client.commands.get(interaction.commandName);
|
||||||
|
|
||||||
if (commands[commandName as keyof typeof commands]) {
|
if (command) {
|
||||||
commands[commandName as keyof typeof commands].execute(interaction);
|
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