上一篇完成了基本的Discord bot建置,可以做到關鍵字的自動答覆,接下來講一下Command/Event Handling,除非你只要做一個小型簡易的bot,不然將所有command以及監聽event放入 index.js
、deploy-commands.js
是不明智的做法,我們要建構一個更系統化的專案!
Command Handling 首先在根目錄下新增一個 commands
資料夾,用來存放所有的指令。接者以 /ping
指令為例,在這個資料夾底下新增 ping.js
(其他command.js參考底下Reference):
1 2 3 4 5 6 7 8 9 10 11 12 13 const { SlashCommandBuilder } = require ('discord.js' );module .exports = { data : new SlashCommandBuilder () .setName ('ping' ) .setDescription ('Replies with Pong!' ), async execute (interaction ) { await interaction.reply ('Pong!' ); }, };
更改 index.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + const fs = require('node:fs'); + const path = require('node:path'); - const { Client, GatewayIntentBits } = require('discord.js'); + const { Client, Collection, GatewayIntentBits } = require('discord.js'); const { token } = require('./config.json'); const client = new Client({ intents: [GatewayIntentBits.Guilds] }); // 加入 commands/ 底下的所有指令 + client.commands = new Collection(); + const commandsPath = path.join(__dirname, 'commands'); + const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js')); + for (const file of commandFiles) { + const filePath = path.join(commandsPath, file); + const command = require(filePath); + // Set a new item in the Collection + // With the key as the command name and the value as the exported module + client.commands.set(command.data.name, command); + }
接著,在 deploy-commands.js
做類似的事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 + const fs = require('node:fs'); + const path = require('node:path'); const { SlashCommandBuilder, Routes } = require('discord.js'); const { REST } = require('@discordjs/rest'); const { clientId, guildId, token } = require('./config.json'); - const commands = [ - new SlashCommandBuilder().setName('ping').setDescription('Replies with pong!'), - new SlashCommandBuilder().setName('server').setDescription('Replies with server info!'), - new SlashCommandBuilder().setName('user').setDescription('Replies with user info!'), - ] - .map(command => command.toJSON()); + const commands = []; + const commandsPath = path.join(__dirname, 'commands'); + const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js')); + + for (const file of commandFiles) { + const filePath = path.join(commandsPath, file); + const command = require(filePath); + commands.push(command.data.toJSON()); + } const rest = new REST({ version: '10' }).setToken(token); rest.put(Routes.applicationGuildCommands(clientId, guildId), { body: commands }) .then((data) => console.log(`Successfully registered ${data.length} application commands.`)) .catch(console.error);
完成指令的加入後,在 index.js
將原本監聽的function改成以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 client.on('interactionCreate', async interaction => { if (!interaction.isChatInputCommand()) return; + const command = interaction.client.commands.get(interaction.commandName); + if (!command) return; + try { + await command.execute(interaction); + } catch (error) { + console.error(error); + await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true }); + } });
未來想要新增指令,只要將指令的js檔新增到 commands/
底下就完成。記得只要指令有更改過都要執行 node deploy-commands.js
一次。
Event Handling 和Command Handling相同,若是將所有event放在 index.js
內會顯得龐雜無序,因此我們需要用到Event Handling的概念幫助我們建構一個更系統化的專案。首先一樣在根目錄底下新增 events
資料夾,用來存放所有的event,並新增每個event的js file,如: ready.js
(其他event.js參考底下Reference):
1 2 3 4 5 6 7 module .exports = { name : 'ready' , once : true , execute (client ) { console .log (`Ready! Logged in as ${client.user.tag} ` ); }, };
接著在 index.js
做以下更改來讀取所有event檔:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 - client.once('ready', () => { - console.log('Ready!'); - }); - - client.on('interactionCreate', async interaction => { - if (!interaction.isChatInputCommand()) return; - - const command = interaction.client.commands.get(interaction.commandName); - - if (!command) return; - - try { - await command.execute(interaction); - } catch (error) { - console.error(error); - await interaction.reply({ content: 'There was an error while executing this command!', - ephemeral: true }); - } - }); + const eventsPath = path.join(__dirname, 'events'); + const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js')); + + for (const file of eventFiles) { + const filePath = path.join(eventsPath, file); + const event = require(filePath); + if (event.once) { + client.once(event.name, (...args) => event.execute(...args)); + } else { + client.on(event.name, (...args) => event.execute(...args)); + } + } client.login(token);
重新執行一次 node index.js
就完成囉! 這篇文章並沒有對bot任何新功能做講解,主要是將檔案整理並系統化,避免日後留下過多技術債🤣 下一篇會講如何透過Discord bot讀取Ethereum smart contract。
最終檔案架構:1 2 3 4 5 6 7 8 9 10 11 12 13 14 Demo-bot/ ├── commands/ ├── ping.js ├── server.js └── user.js ├── events/ ├── interactionCreate.js └── ready.js ├── node_modules/ ├── config.json ├── deploy-commands.js ├── index.js ├── package-lock.json └── package.json
Reference