import config from './config.json' with { type: 'json' }; // Bun yêu cầu cú pháp này cho JSON import { Client, GatewayIntentBits, Partials } from 'discord.js'; import { readdirSync } from 'fs'; import { join } from 'path'; import type { Command } from './types'; import { MusicQueue } from './utils/MusicQueue'; const commandsDir = join(import.meta.dir, 'Commands'); // Bun sử dụng import.meta.dir thay vì __dirname const commands: Command[] = readdirSync(commandsDir) .filter(file => file.endsWith('.ts') || file.endsWith('.js')) .map(file => { try { const commandModule = require(join(commandsDir, file)); // Bun hỗ trợ require, nhưng ESM import tốt hơn return commandModule.default; } catch (error) { console.error(`❌ Failed to load command ${file}:`, error); return undefined; } }) .filter((cmd): cmd is Command => cmd !== undefined); const clients: Client[] = []; const queues = new Map(); // Quản lý hàng đợi nhạc theo guildId config.tokens.forEach((token: string, index: number) => { const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent, GatewayIntentBits.DirectMessages, GatewayIntentBits.GuildVoiceStates, // Thêm intent cho voice ], partials: [Partials.Channel], }); client.once('ready', () => { console.log(`✅ Client ${index + 1} - ${client.user?.username} is ready!`); clients.push(client); }); // Xử lý prefix commands client.on('messageCreate', async message => { if (message.author.bot || !message.content.startsWith(config.PREFIX)) return; const args = message.content.slice(config.PREFIX.length).trim().split(/\s+/); const commandName = args.shift()?.toLowerCase(); const command = commands.find(cmd => cmd?.data?.name === commandName); if (!command) { return message.reply(`❌ Lệnh \`${commandName}\` không tồn tại. Dùng \`${config.PREFIX}help\` để xem danh sách lệnh.`); } if (command.ownersOnly && !config.owners.includes(message.author.id)) { return message.reply('⛔ Bạn không có quyền dùng lệnh này.'); } try { await command.execute(message, args, client); } catch (error) { console.error(`❌ Lỗi khi xử lý lệnh ${commandName}:`, error); await message.reply('❌ Có lỗi xảy ra khi chạy lệnh.').catch(console.error); } }); // Auto-stop khi người dùng rời voice channel client.on('voiceStateUpdate', (oldState, newState) => { const queue = queues.get(oldState.guild.id); if (!queue || !queue.connection) return; const botVoiceChannel = queue.connection.joinConfig.channelId; if (oldState.channelId === botVoiceChannel && newState.channelId !== botVoiceChannel) { const channel = oldState.guild.channels.cache.get(botVoiceChannel); if (channel) { const members = channel.members.filter(member => !member.user.bot); if (members.size === 0) { queue.songs = []; queue.playing = false; queue.currentSong = null; queue.player.stop(); if (queue.connection) { queue.connection.destroy(); queue.connection = null; } console.log(`Auto-stopped music in guild ${oldState.guild.name} - no users in voice channel`); } } } }); client.login(token).catch(error => { console.error(`❌ Failed to login Client ${index + 1}:`, error); }); }); export { clients, commands, queues }; // Xuất queues để sử dụng trong các lệnh process.on('unhandledRejection', error => { console.error('❗ Unhandled promise rejection:', error); }); process.on('uncaughtException', error => { console.error('❗ Uncaught exception:', error); });