feat: initial Papo bot scaffold

This commit is contained in:
Pascal.P
2025-11-30 11:04:41 +01:00
commit 000481a3b0
12168 changed files with 1584750 additions and 0 deletions

View File

@@ -0,0 +1,94 @@
import { REST, Routes, Collection, Client, ChatInputCommandInteraction, GatewayIntentBits } from 'discord.js';
import fs from 'fs';
import path from 'path';
import { SlashCommand } from '../utils/types.js';
import { env } from '../config/env.js';
import { logger } from '../utils/logger.js';
export class CommandHandler {
private commands = new Collection<string, SlashCommand>();
constructor(private client: Client) {}
public async loadCommands() {
const commandsPath = path.join(process.cwd(), 'src', 'commands');
const commandFiles = this.getCommandFiles(commandsPath);
for (const file of commandFiles) {
const mod = await import(file);
const command: SlashCommand = mod.default;
if (command?.data && command?.execute) {
this.commands.set(command.data.name, command);
logger.info(`Loaded command ${command.data.name}`);
}
}
}
private getCommandFiles(dir: string): string[] {
const entries = fs.readdirSync(dir, { withFileTypes: true });
const files: string[] = [];
for (const entry of entries) {
const res = path.resolve(dir, entry.name);
if (entry.isDirectory()) {
files.push(...this.getCommandFiles(res));
} else if (entry.isFile() && (entry.name.endsWith('.ts') || entry.name.endsWith('.js'))) {
files.push(res);
}
}
return files;
}
public async registerSlashCommands() {
const rest = new REST({ version: '10' }).setToken(env.token);
const body = this.commands.map((command) => command.data.toJSON());
const guilds = env.guildIds.length ? env.guildIds : [];
if (guilds.length) {
for (const gid of guilds) {
try {
await rest.put(Routes.applicationGuildCommands(env.clientId, gid), { body });
logger.info(`Registered ${body.length} slash commands for guild ${gid}`);
} catch (err) {
logger.error(`Failed to register slash commands for guild ${gid}`, err);
}
}
} else {
try {
await rest.put(Routes.applicationCommands(env.clientId), { body });
logger.info(`Registered ${body.length} global slash commands`);
} catch (err) {
logger.error('Failed to register global slash commands', err);
}
}
}
public async registerGuildCommands(guildId: string) {
const rest = new REST({ version: '10' }).setToken(env.token);
const body = this.commands.map((command) => command.data.toJSON());
try {
await rest.put(Routes.applicationGuildCommands(env.clientId, guildId), { body });
logger.info(`Registered ${body.length} slash commands for guild ${guildId}`);
} catch (err) {
logger.error(`Failed to register commands for guild ${guildId}`, err);
}
}
public async handleInteraction(interaction: ChatInputCommandInteraction) {
const command = this.commands.get(interaction.commandName);
if (!command) return;
if (command.guildOnly && !interaction.inGuild()) {
await interaction.reply({ content: 'Dieser Befehl funktioniert nur auf Servern.', ephemeral: true });
return;
}
try {
await command.execute(interaction, this.client);
} catch (err) {
logger.error(`Command ${interaction.commandName} failed`, err);
if (interaction.deferred || interaction.replied) {
await interaction.followUp({ content: 'Es ist ein Fehler aufgetreten.', ephemeral: true });
} else {
await interaction.reply({ content: 'Es ist ein Fehler aufgetreten.', ephemeral: true });
}
}
}
}