從零開始的Discord機器人(2) - 斜線指令

本篇會幫Discord機器人新增斜線指令,並能根據指令做出回應。

  在上一篇中,我們已經順利建立好環境、新增一個機器人帳號,並成功讓它登入了。接下來我們要幫機器人加上重點的功能──斜線指令(Slash Commands)。

  早期的機器人通常是接收成員傳送的訊息,分解訊息的內容,確認成員要執行什麼指令並做出相應的行為。你需要直接送出例如/hello @成員或是!join之類的訊息,但如果沒有看過機器人的文件說明,你根本無從得知機器人有什麼指令可以使用。

  於是Discord在2022年9月推出了全新的斜線指令作為互動的新標準,只要輸入「/」就可以直接瀏覽能夠使用的指令,使用者可以直接點擊命令並輸入參數來使用,相比傳統的文字指令更加一目了然。

  在本文中,我們將一步步幫機器人加上斜線指令,等到熟悉之後,你也可以幫機器人加上各式各樣的指令囉!

1. 新增一個斜線指令

  在discord.js v14中,要新增一個斜線指令,我們可以使用SlashCommandBuilder來幫助我們完成。首先先在最上面的require中加入SlashCommandBuilder:

// ---------------START---------------
const { Client, Events, GatewayIntentBits } = require ('discord.js');
// ----------------END----------------

  接下來,我們在ClientReady這邊新增一個SlashCommandBuilder來建立新的斜線指令。

client.once (Events.ClientReady, c => {
    console.log (`歡迎登入 ${c.user.username}`);
// ---------------START---------------
    new SlashCommandBuilder ()
        .setName ('ping')
        .setDescription ('Replies with "Pong!"');
// ----------------END----------------
});

  接著,我們要讓機器人監聽InteractionCreate的事件。不管是觸發斜線指令、送出互動視窗、點擊按鈕、或是使用下拉選單,全部都是屬於InteractionCreate的事件。我們來新增以下內容:

// ---------------START---------------
client.on(Events.InteractionCreate, interaction => {
    if (!interaction.isChatInputCommand ()) return;
    if (interaction.commandName === "ping") {
        interaction.reply ("Pong!");
    }
});
// ----------------END----------------

  有人使用了斜線指令等一系列動作觸發InteractionCreate的事件後,會得到一個interaction,當我們根據指令完成工作後,可以使用interaction的reply ()來回應使用者。

  特別需要提到的是,interaction的reply ()只有3秒,超過時間就會被當作沒有回應。如果確定回應時間會超過3秒的話,有幾種方法可以解決:

  1. 先送出一個reply (),等動作完成後再使用editReply ()編輯該回應。
  2. 使用deferReply (),Discord會幫你傳送一個「機器人正在思考」的訊息,並且延長到15分鐘內都可以再編輯回應。
  3. 先送出一個reply (),等動作完成後再使用followUp ()傳送第二則回應。當你使用deferReply ()再使用followUp (),此時並不會傳送第二則回應,而是直接編輯「機器人正在思考」的訊息。

  我們新增了斜線指令,並且機器人登入之後會開始監聽InteractionCreate事件並做出回應,也許這時候你會想試著使用看看斜線指令了。但你實際上到伺服器輸入「/」之後,並沒有我們剛剛新增的斜線指令可以使用,這是為什麼呢?

  除了在這裡新增斜線指令之外,我們還必須另外向Discord註冊斜線指令,這樣Discord才知道我們的機器人身上有什麼指令可以使用。想要註冊斜線指令的話,我們可以這麼做:

client.once (Events.ClientReady, c => {
    console.log (`歡迎登入 ${c.user.username}`);

    const ping = new SlashCommandBuilder ()
        .setName ('ping')
        .setDescription ('Replies with "Pong!"');
// ---------------START---------------
    client.application.commands.create (ping, "填入伺服器的ID");
// ----------------END----------------
});

  如此一來,我們就成功註冊了一個在伺服器內可以使用的斜線指令,你可以在伺服器內使用剛剛新增的指令了!

  (請注意這裡將指令註冊給指定伺服器,在其他伺服器內是無法使用這個指令的,後續會說明如何註冊指令讓所有伺服器都能使用。)

2. 再新增一個斜線指令

  現在機器人有了第一個指令,就讓我們再新增一個新的指令吧。這次我們來新增一個帶有參數的指令,讓機器人可以獲得成員輸入的參數並加以利用:

client.once (Events.ClientReady, c => {
    console.log (`歡迎登入 ${c.user.username}`);

    const ping = new SlashCommandBuilder ()
        .setName ('ping')
        .setDescription ('Replies with "Pong!"');
// ---------------START---------------
    const hello = new SlashCommandBuilder ()
        .setName ('hello')
        .setDescription ('Say hello to someone!')
        .addUserOption (option =>
            option
                .setName ('user')
                .setDescription ('The user you want to say hi to')
                .setRequired (true)
        );
// ----------------END----------------

    client.application.commands.create (ping, "填入伺服器的ID");
// ---------------START---------------
    client.application.commands.create (hello, "填入伺服器的ID");
// ----------------END----------------
});

  在新的指令中加入了新的部分addUserOption,它的作用是可以新增一個讓使用者輸入成員進去的參數。除了成員之外,Discord共提供了以下類型的參數:

  • addSubcommand 子命令
  • addSubcommandGroup 子命令群組
  • addStringOption 字串
  • addIntegerOption 整數
  • addBooleanOption 布林值
  • addUserOption @成員 或 成員的ID
  • addChannelOption #頻道 或 頻道的ID
  • addRoleOption @身分組 或 身分組的ID
  • addMentionableOption @成員/@身分組 或 成員/身分組的ID
  • addNumberOption 浮點數
  • addAttachmentOption 附加檔案

  如果需要擴充參數,我們只要根據需要填入的參數,在最後面插入相對應的方法即可。你可以看到參數本身也可以設定名稱與說明,在我們剛剛新增的指令中,填入成員的參數使用了setRequired 將參數設為必填,如果使用者沒有填入內容將無法送出指令。由於指令與每一種參數所包含的方法太多,這邊就不一一列舉,如果有需要可以閱讀官方文件確認。

  子命令與子命令群組可以讓你將同一類的命令包成一個群組。例如Ban人與踢人指令,可以包成/manage指令底下的user子命令群組的Ban子命令與kick子命令,之後當你想尋找管理使用者相關的指令時,只要輸入/manage user便會列出底下所有的子命令,是不是很方便呢!

  不好意思,扯得有點遠了。讓我們繼續把機器人接收斜線指令後回應的部分完成吧:

client.on(Events.InteractionCreate, interaction => {
    if (!interaction.isChatInputCommand ()) return;
    if (interaction.commandName === "ping") {
        interaction.reply ("Pong!");
    }
// ---------------START---------------
    if (interaction.commandName === "hello") {
        const user = interaction.options.getUser ('user');
        interaction.reply (`Hello <@${user.id}>`);
    }
// ----------------END----------------
});

  當使用了帶有參數的指令,我們可以使用interaction.options內的一系列get方法去取得那些參數的值。例如我們的指令所包含的參數是填入成員,因此我們使用getUser來接收,你可以看到getUser的引數是填入我們剛剛幫option設定的名稱,這樣我們就能抓到想要的參數了。

3. 接下來要做的事情

  到目前為止,我們幫機器人新增了斜線指令,並且使用者可以傳遞參數給機器人使用,基本的機器人功能已經算是完成了。但你可能會發現,如果繼續擴充機器人的指令,現在的寫法將會變得很難維護。接下來我們會改善機器人的架構,讓你可以方便擴充與維護機器人的功能。

  感謝你看到這邊,我們下次見。