kiến thức Tạo một bot Discord phát nhạc đơn giản bằng Discord.JS

NosferatuVN

Junior Member
Chắc chẳng ai còn lạ gì Discord nữa nhỉ, ngoài dùng để giao tiếp, Discord còn cho phép người dùng tạo những con bot rất thú vị nhờ API của mình. Mình mới vọc vạch được ít lâu nên mạo muội chia sẻ cách tạo một con bot phát nhạc từ Youtube đơn giản cho người mới bắt đầu, ai cao thủ rồi thì bỏ qua nhé <(")

1. Tạo bot:
Để tạo một bot, đầu tiên vào trang Discord Developers, đăng nhập, chọn New Application và nhập tên mà bạn muốn (đây không phải tên bot):
bot.png


Chọn Bot -> Add Bot -> Sửa tên, avatar của bot theo ý, và copy cái Token này (quan trọng):
bot.png


Vậy là xong bước tạo bot, muốn bot vào kênh của mình thì bạn lên mục OAuth2 -> tích vào bot -> copy link dán vào trình duyệt:
1587823892495.png


2. Cài những công cụ cần thiết:
Trong bài viết này mình sẽ dùng thư viện Discord.JS để tương tác với API của Discord. Discord.JS là 1 module NodeJS nên nếu chưa cài NodeJS thì hãy lên trang chủ để tải nhé : nodejs.org

Sau khi cài xong NodeJS, cài các thư viện cần thiết, để phát được nhạc từ youtube thì ngoài Discord.JS bot cần opusscript hoặc @discordjs/opus, ffmpeg và ytdl (hoặc bất cứ thằng nào tương tự).

Trước tiên là tạo file index.js (hoặc abc.js hay bất cứ tên gì bạn muốn) ở thư mục bạn muốn đặt mã nguồn (source code) của bot, có thể tạo thêm file config.json để lưu những giá trị quan trọng nếu muốn. Để coding thì bạn dùng bất cứ phần mềm text editor nào cũng được (notepad, notepad++,...), trong bài viết này mình dùng Sublime Text 3.

Mở Command Prompt (cmd) ở thư mục chứa file index.js tạo ở trên, gõ:
Code:
npm init -y
npm install discord.js @discordjs/opus ffmpeg-static ytdl-core --save

Sau khi cài xong chúng ta sẽ tiến hành coding.

3. Coding:
Mở file index.js mà bạn vừa tạo ở trên, nhập các module vào:
Code:
const Discord = require('discord.js');
const ytdl = require('ytdl-core');

const prefix = '!'; // tiền tố để nhận biết lệnh (!play, !stop), đổi thành cái gì tùy ý bạn
const token = ""; //token của bot

Đăng nhập để bot online:
Code:
const client = new Discord.Client();
client.login(token)

Trong bài hướng dẫn này chúng ta chỉ sử dụng 2 event là ready (khi bot đăng nhập thành công) và message (khi có tin nhắn mới được gửi), ngoài ra còn rất nhiều event hay ho khác bạn có thể đọc trong tài liệu của DiscordJS.

Đầu tiên là event ready:
Code:
client.once('ready', () =>{
    console.log('Ready'); //bot đăng nhập thành công
});
Đoạn code đầu tiên sẽ giống như thế này:
1587825917961.png

Thử mở cmd ở thư mục chứa file index.js gõ: node index.js
Nếu màn hình cmd in ra chữ Ready nghĩa là bạn đã thành công bước đầu.

Tiếp theo là event message (khi có tin nhắn mới được gửi trong các kênh mà bot tham gia):
Code:
client.on('message', async msg => {
    if (msg.author.bot) return; //nếu người gửi là bot thì khỏi chạy tiếp
    if (msg.content === 'ping') { 
          msg.reply('Pong!'); //nếu tin nhắn có nội dung là ping thì bot trả lời Pong~
  }
}

Ctrl + C trong cmd và node index.js lại, thử chat trong kênh là ping, nếu bot trả lời Pong! nghĩa là code hoạt động tốt, đến lúc bắt tay vào code phần phát nhạc.

Mình sẽ lập constructor cho 2 đối tượng QueueSong và tạo một đối tượng queues sẵn để tiện cho việc sử dụng:
Code:
const queues = new Map();
class Queue {
    constructor(voiceChannel) {
        this.voiceChannel = voiceChannel;
        this.connection = null;
        this.songs = [];
        this.volume = 100;
        this.playing = true;
        this.repeat = false;
    }
}

class Song {
    constructor(title, url) {
        this.title = title;
        this.url = url;
    }
}
Hàm Map() sẽ tạo 1 đối tượng chứa các cặp key-value. Mục đích của việc này là bot có thể phát nhạc ở nhiều máy chủ khác nhau. Mỗi máy chủ chỉ phát được ở 1 channel 1 lúc nên key sẽ là ID của máy chủ, value là cái Queue chứa thông tin (kênh phát, luồng phát, bài hát, âm lượng,...).
1587826951611.png


Giờ chúng ta code để bot biết khi người dùng gửi lệnh phát. Trong sự kiện message ở trên, thêm code vào như sau (mình sẽ giải thích ở từng dòng cho tiện). Tạm thời mình chỉ hướng dẫn cách đơn giản là phát nhạc khi nhập chính xác url của video. Bạn nào muốn nâng cao hơn (tìm kiếm bài hát) thì tham khảo Youtube API.
Code:
if (msg.content.startsWith(prefix)) { //prefix là tiền tố để xác định lệnh đã được khai báo ở trên ("!")
        const args = msg.content.slice(prefix.length).split(/ +/); //bỏ đi tiền tố và chia thành nhiều chuỗi nhỏ theo khoảng trắng
        const command = args[0]; //lệnh sẽ là chuỗi đầu tiên
        if (command === 'play') { //lệnh phát nhạc
            let voiceChannel = msg.member.voice.channel; //kênh voice của người gửi lệnh
            if (!voiceChannel) return msg.reply("Vào kênh âm nhạc trước đã!"); //nếu người dùng chưa vào kênh voice nào sẽ thông báo
            let permissions = msg.member.voice.channel.permissionsFor(msg.client.user); //kiểm tra quyền vào kênh và nói của bot
            if (!permissions.has('CONNECT')||!permissions.has('SPEAK')) return msg.reply("Thiếu quyền vào kênh âm nhạc hoặc phát nhạc!");
            let url = args.slice(1).join(' '); //bỏ lệnh đi thì ta có url
            let video = await ytdl.getInfo(url); //dùng ytdl lấy thông tin video từ url
            if (!video) return msg.reply("Url không hợp lệ!"); 
            const song = new Song(video.title, video.video_url); //tạo đối tượng Song, có 2 giá trị là title và url lấy từ video ở trên
            const serverQueue = queues.get(msg.guild.id); //kiểm tra trong đối tượng queues đã có value (giá trị) của cái id guild chưa
            if (!serverQueue) { //nếu chưa có value của guild id tức là chưa phát nhạc
                let queue = new Queue(voiceChannel); tạo đối tượng Queue chứa thông tin về lần phát nhạc ở server này
                queues.set(msg.guild.id, queue); //đặt key là id guild, value là đối tượng queue tạo ở dòng trên
                queue.songs.push(song); //thêm bài hát vào danh sách phát
                let connection = await voiceChannel.join(); //bot tham gia vào kênh voice
                queue.connection = connection; 
                playSong(msg); //hàm playSong sẽ viết ở dưới
            } else { //nếu serverQueue tồn tại tức là đang phát nhạc
                serverQueue.songs.push(song); //thêm bài hát vào danh sách phát và thông báo
                msg.channel.send(`:notes: Thêm vào hàng chờ: ${song.title}`);
            }
            return
        }
}
}
Hàm playSong, thực ra chỗ ytdl chỉ cần ytdl(song.url) là đủ, nhưng đôi khi bị lỗi chưa phát hết bài đã ngừng vì vậy phải thêm option highWaterMark: 1<<25, mấy cái còn lại không quan trọng lắm.
Code:
async function playSong(msg) {
    const serverQueue = queues.get(msg.guild.id); //kiểm tra xem có tồn tại value cho cái id guild không
    if (!serverQueue) return; //nếu value không tồn tại thì dừng lại
    if (serverQueue.songs.length < 1) { //nếu value tồn tại nhưng danh sách phát trống (phát hết rồi)
        serverQueue.voiceChannel.leave(); //bot rời kênh nhạc
        queues.delete(msg.guild.id); //xóa cặp key-value của guild id khỏi đối tượng queues
        return msg.channel.send("Hết nhạc!");
    }
    let song = serverQueue.songs[0]; //lấy thông tin bài đầu tiên của danh sách phát
    let dispatcher = serverQueue.connection.play(ytdl(song.url, {filter: 'audioonly', highWaterMark: 1<<25, type: 'opus'})); //phát nhạc
    dispatcher.setVolume(serverQueue.volume/100); //âm lượng
    msg.channel.send(`:notes: Bắt đầu phát: ${song.title}`);
    dispatcher.on('finish', () => { //khi luồng phát kết thúc
        if (!serverQueue.repeat) serverQueue.songs.shift(); //nếu repeat (lặp lại bài đang phát) = false thì bỏ bài hát đầu tiên bằng hàm shift()
        return playSong(msg); //tiếp tục phát cho đến khi danh sách phát  = 0 (<1 ở trên) thì thôi
    });
}
Code sẽ như thế này:
1587828360234.png


Về cơ bản thì bot đã phát nhạc tốt, nhưng cần thêm vài chức năng như stop, pause, resume, skip, set volume để hoàn chỉnh hơn:

Chức năng stop:
Code:
if (command === 'stop') {
            const serverQueue = queues.get(msg.guild.id);
            if (!serverQueue) return msg.reply("Có bài nào đâu!");
            serverQueue.songs = []; //xóa toàn bộ danh sách phát
            serverQueue.connection.dispatcher.end(); //kết thúc luồng đang phát
            return
}
Chức năng skip, về cơ bản thì tương tự stop, chỉ khác cái là không xóa danh sách phát:
Code:
if (command === 'skip') {
            const serverQueue = queues.get(msg.guild.id);
            if (!serverQueue) return msg.reply("Có bài nào đâu!");
            serverQueue.connection.dispatcher.end();
            msg.channel.send(`:notes: Bỏ qua bài: ${serverQueue.songs[0].title}`);
            return
}
DiscordJS cung cấp sẵn các chức năng pause, resume, setVolume...
Code:
if (command === 'volume') {
            const serverQueue = queues.get(msg.guild.id);
            if (!serverQueue) return msg.reply("Có bài nào đâu!");
            let volume = parseInt(args.slice(1).join(""));
            if (!isNaN(volume)) return msg.reply("Âm lượng không hợp lệ!");
            serverQueue.volume = volume;
            serverQueue.connection.dispatcher.setVolume(volume/100);
            return
}
if (command === 'pause') {
            const serverQueue = queues.get(msg.guild.id);
            if (!serverQueue) return msg.reply("Có bài nào đâu!");
            serverQueue.connection.dispatcher.pause();
            return
}
if (command === 'resume') {
            const serverQueue = queues.get(msg.guild.id);
            if (!serverQueue) return msg.reply("Có bài nào đâu!");
            serverQueue.connection.dispatcher.resume();
            return
}
Muốn lặp lại bài đang phát thì chỉ cần đặt Queue.repeat = true và ngược lại là được, vì ở trong hàm playMusic nếu repeat = true thì sẽ không xóa phần tử (bài hát đầu tiên), vì vậy nó sẽ lặp lại mãi đến khi người dùng tắt thì thôi.
Code:
if (command === 'repeat') {
            const serverQueue = queues.get(msg.guild.id);
            if (!serverQueue) return msg.reply("Có bài nào đâu!");
            serverQueue.repeat = true;
            msg.channel.send(`:notes: Lặp lại bài: ${serverQueue.songs[0].title}`);
            return
}
if (command === 'offrepeat') {
            const serverQueue = queues.get(msg.guild.id);
            if (!serverQueue) return msg.reply("Có bài nào đâu!");
            serverQueue.repeat = false;
            msg.channel.send(`:notes: Ngừng lặp lại bài: ${serverQueue.songs[0].title}`);
            return
}
Lấy danh sách phát:
Code:
if (command === 'queue') {
            const serverQueue = queues.get(msg.guild.id);
            if (!serverQueue) return msg.reply("Có bài nào đâu!");
            let result = serverQueue.songs.map((song, i) => {
                return `${(i == 0) ? `\`Đang phát:\`` : `${i}.`} ${song.title}`
            }).join('\n');
            msg.channel.send(result);
            return
}

Vậy là chúng ta đã tạo xong một con bot Discord với chức năng phát nhạc đơn giản, đây cũng là lần đầu mình viết hướng dẫn (mới học JS vài tháng) nên nếu có sai sót gì mong mn thông cảm nhé.

Mọi người có thể xem source code tại: Github
 
mình chưa hiểu lắm 1 vài bước, hy vọng có thể trao đổi với bạn để có thể hiểu rõ hơn
 
Back
Top