const { Client, GatewayIntentBits } = require('discord.js'); const { joinVoiceChannel, createAudioPlayer, createAudioResource, AudioPlayerStatus, StreamType, NoSubscriberBehavior } = require('@discordjs/voice'); const express = require('express'); const app = express(); const server = require('http').createServer(app); const io = require('socket.io')(server, { cors: { origin: "*", methods: ["GET", "POST"] } }); const { PassThrough } = require('stream'); const prism = require('prism-media'); const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent ] }); let voiceConnection; const player = createAudioPlayer({ behaviors: { noSubscriber: NoSubscriberBehavior.Play, // Tiếp tục chạy ngay cả khi không có ai nghe }, }); let aiStream = null; function setupAudioPipeline() { // Nếu đang có luồng cũ thì kết thúc nó để làm sạch bộ nhớ if (aiStream) { aiStream.destroy(); } aiStream = new PassThrough(); const demuxer = new prism.opus.WebmDemuxer(); // Pipe dữ liệu qua demuxer để lấy Opus chuẩn const resource = createAudioResource(aiStream.pipe(demuxer), { inputType: StreamType.Opus, inlineVolume: true }); if (resource.volume) { resource.volume.setVolume(2.0); // Ép âm lượng to lên 200% } player.play(resource); console.log("🎙️ Discord Player: Luồng giải mã đã kích hoạt và đang chờ dữ liệu..."); } app.get('/join', (req, res) => { const channelId = '1167842044992028697'; const guildId = '1167842044497104997'; const channel = client.channels.cache.get(channelId); if (!channel) return res.status(404).send("Không tìm thấy phòng!"); voiceConnection = joinVoiceChannel({ channelId: channel.id, guildId: guildId, adapterCreator: channel.guild.voiceAdapterCreator, selfDeaf: false, // TẮT gạch chéo tai nghe selfMute: false // TẮT gạch chéo mic }); voiceConnection.subscribe(player); setupAudioPipeline(); res.send("Bot đã vào phòng voice và đã tháo tai nghe!"); }); io.on('connection', (socket) => { console.log("🌐 [Socket] Web AI đã kết nối thành công!"); socket.on('ai_audio_stream', (audioData) => { if (audioData && audioData.byteLength > 0) { // Nếu player bị Idle (hết dữ liệu cũ), khởi động lại pipeline if (player.state.status === AudioPlayerStatus.Idle) { setupAudioPipeline(); } if (aiStream && aiStream.writable) { aiStream.write(Buffer.from(audioData)); } } }); socket.on('disconnect', () => { console.log("📴 [Socket] Web AI đã ngắt kết nối."); }); }); client.on('ready', () => console.log(`🤖 Bot ${client.user.tag} đã sẵn sàng!`)); client.login(process.env.DISCORD_TOKEN); server.listen(process.env.PORT || 7860, () => console.log("🚀 Server đang chạy..."));