137 lines
4.9 KiB
TypeScript
137 lines
4.9 KiB
TypeScript
import { REST, Routes, Collection, Client, ChatInputCommandInteraction, GatewayIntentBits } from 'discord.js';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import { SlashCommand } from '../utils/types';
|
|
import { env } from '../config/env';
|
|
import { logger } from '../utils/logger';
|
|
import { AdminService } from './adminService';
|
|
import { StatuspageService } from './statuspageService';
|
|
import { settingsStore } from '../config/state';
|
|
import { ModuleKey } from './moduleService';
|
|
|
|
export class CommandHandler {
|
|
private commands = new Collection<string, SlashCommand>();
|
|
private moduleMap: Record<string, ModuleKey> = {
|
|
// Tickets
|
|
ticket: 'ticketsEnabled',
|
|
ticketpanel: 'ticketsEnabled',
|
|
transcript: 'ticketsEnabled',
|
|
close: 'ticketsEnabled',
|
|
claim: 'ticketsEnabled',
|
|
// Music
|
|
play: 'musicEnabled',
|
|
skip: 'musicEnabled',
|
|
stop: 'musicEnabled',
|
|
pause: 'musicEnabled',
|
|
resume: 'musicEnabled',
|
|
loop: 'musicEnabled',
|
|
queue: 'musicEnabled',
|
|
// Level
|
|
rank: 'levelingEnabled',
|
|
// Statuspage
|
|
status: 'statuspageEnabled',
|
|
// Birthday
|
|
birthday: 'birthdayEnabled',
|
|
// Events
|
|
event: 'eventsEnabled',
|
|
events: 'eventsEnabled'
|
|
};
|
|
|
|
constructor(private client: Client, private admin?: AdminService, private statuspage?: StatuspageService) {}
|
|
|
|
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;
|
|
}
|
|
if (interaction.inGuild()) {
|
|
const moduleKey = this.moduleMap[interaction.commandName];
|
|
if (moduleKey) {
|
|
const cfg = settingsStore.get(interaction.guildId!);
|
|
const enabled = cfg?.[moduleKey];
|
|
if (enabled === false) {
|
|
await interaction.reply({ content: 'Dieses Modul ist fuer diese Guild deaktiviert.', ephemeral: true });
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
try {
|
|
this.admin?.trackCommand(interaction.guildId);
|
|
if (interaction.guildId) this.admin?.trackGuildEvent(interaction.guildId, 'commands');
|
|
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 });
|
|
}
|
|
}
|
|
}
|
|
}
|