[DC機器人] 機器人框架

如果我沒記錯的話,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所以移除的功能)
按讚

發佈留言

%d 位部落客按了讚: