如果我沒記錯的話,Messenger、Line和Telegram的機器人都是用webhook運作。
而今天要講的Discord機器人,不同於上述的機器人,他需要被部署在伺服器中。
這個專案應該會做很大,所以我選擇用node.js代替Python(我不太會整理python的code,最後可能直接寫成一個檔案)。
整整齊齊的,js真香
先看到主程式
./bot.js
const Discord = require('discord.js');
require('dotenv').config();
const client = new Discord.Client({ partials: ['MESSAGE', 'CHANNEL', 'REACTION'], intents: ['FLAGS.GUILDS']});
client.commands = new Discord.Collection();
client.events = new Discord.Collection();
for(const handler of ['command_handler', 'event_handler']){
require(`./handlers/${handler}`)(client, Discord)
}
client.login(process.env.DISCORD_TOKEN)
很簡單易懂地就是調用了 headlers 目錄下的兩個文件。
我這邊使用了 dotenv 這個套件來設置 discord 機器人的 token 和 prefix。
接下來,我將 hendlers 分成兩類: command 和 event,command 就是用來處理 cmds 目錄下的 js 文件,而 event 則是處裡 events 下的文件。Discord 的 event 有很多,目前我只做了兩個比較重要的。cmds 我之所以要分開放就是為了能更方便地擴充新的指令。
總之,還是先看到event的處理程序,這裡展示的程式碼都有經過刪減,只讓他有最基礎的功能,全部的code我可能哪天想到才會放Github,自己一個人寫的小東西真的很懶得丟那裡。
handlers/event_handler.js
const fs = require('fs');
module.exports = (client, Discord) => {
const load_dir = (dir) => {
const event_files = fs.readdirSync(`./events/${dir}`).filter(file => file.endsWith('.js'));
for(const file of event_files){
const event = require(`../events/${dir}/${file}`);
const event_name = file.split('.')[0];
client.on(event_name, event.bind(null, Discord, client));
}
}
['client', 'guild'].forEach(e => load_dir(e));
}
噢對,在查資料的時候,我第一次知道用``
就可以把變數放進去,超酷。
通過 fs 這個套件就可以讀取目錄下的檔案,之所以不用 bot.js 是因為處理程序就兩個,不會再增加,隨便拿一個陣列存就好。就如同剛才所說,Discord 的 event 有很多種,現在只弄了兩個而已,以後一定會增加。
handlers/command_handler.js
const fs = require('fs');
module.exports = (client, Discord) => {
const cmdFiles = fs.readdirSync('./cmds/').filter(file => file.endsWith('.js'));
for(const file of cmdFiles){
const command = require(`../cmds/${file}`);
if(command.name){
client.commands.set(command.name, command)
}else{
continue;
}
}
}
這個跟上一個差不多,而且更少,所以我就不多說了,就是讀取 cmds 目錄下的 js 檔案而已。
events/guild/message.js
module.exports = (Discord, client, message) => {
const prefix = process.env.PREFIX;
if(!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const cmd = args.shift().toLowerCase();
const command = client.commands.get(cmd) || client.commands.find(a => a.aliases && a.aliases.includes(cmd));
try{
if(command) command.execute(message, args, cmd, client, Discord);
}catch(e){
console(`Error: ${e}`);
return;
}
這個程式就是說,當我們的機器人接收到任何非機器人發送的訊息時,就會判斷該訊息是否有使用特定的前綴字。
如果是,就把整條訊息分解成 指令(cmd) 和 參數(args) ,之後再去找有沒有訊息中提及的那條指令。
噢對,這裡還用了aliases來給相同功能不同的名字和單一檔案更多的程式。
events/client/ready.js
module.exports = (Discord, client, message) => {
console.log(`快樂柴犬機器人已上線!`);
client.user.setActivity('Sword Art Online');
//setActivity默認是playing,若要換成watching或streaming之類的就要寫成
//client.user.setActivity('Sword Art Online', { type: 'WATCHING' });
}
當我們的機器人啟動時,會執行這裡面的程式,主要是用來初始化機器人,像是設定狀態什麼的。
之後還要再加已遊玩時間
文章也接近尾聲,最後來一個官方文件上的功能吧!
cmds/ping.js
module.exports = {
name: 'ping',
description: '輸入ping,回覆你pong的東西',
async execute(msg){
msg.channel.send('pong!');
}
}
效果如下:
如果要提及下指令的人的話,可以改成 msg.reply('pong!');
。
因為我的機器人已經部署到VPS上,要改也頗麻煩,所以就不演示了。
後記
這應該算是個系列文章,目前我的機器人已完成音樂功能,大概跟市面上的那個 Rythm 差不多了。
希望路過的各位能幫忙抓bug,感激不盡。
本來還以為DC的chatbot是用webhook實作,讀過文件才發現不是。(還想說就放在cloud hosting,結果最後用上了我設日本VPN的那台VPS),而音樂系統也因此而做了修改,把ffmpeg拿掉,不然帳單應該會很貴。
附錄
微笑柴柴的音樂指令:
- @play 參數可用連結,或關鍵字搜尋。
- @skip 跳過當前播放的歌曲。
- @stop 清空播放清單。
- @queue 顯示播放清單。
- @np 顯示正在播放的歌曲。
- @replay 重新播放當前歌曲。
@pause 暫停播放當前歌曲。@start 繼續播放當前歌曲。(因為沒有時實裝ffmpeg所以移除的功能)