
前言
前一篇對於 Discord 已經具有一定基礎後,當然要來做一點小東西了,當然不會太難,大多都是前面所寫過跟使用過的東西哩。
專案準備
前面當然要準備一下專案,為了方便你練習,我也會盡可能先提供好相關的程式碼跟指令,所以你可以先輸入以下指令來建立專案:
1 2 3 4 5 6
| mkdir example-discord-bot-crawler cd example-discord-bot-crawler npm init -y touch index.js touch register.js touch .env
|
接下來就是安裝可能會需要使用的套件
1
| npm i discord.js cheerio dotenv
|
實作
接下來前面基礎的程式碼我就不重複說明了,直接貼上來:
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
| require('dotenv').config();
const { Client, GatewayIntentBits, Partials, Events, } = require('discord.js');
const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent, ], partials: [ Partials.Channel, ], });
client.once(Events.ClientReady, () => { console.log('Ready!'); });
client.login(process.env.DISCORD_TOKEN);
|
1 2 3
| # .env DISCORD_TOKEN=你的 Discord Token DISCORD_CLIENT_ID=你的 Discord Client ID
|
我相信你看到我安裝 cheerio 就大概知道我們預期要做什麼了,沒錯,我們要來爬蟲了!
那麼我們這次需求要做什麼呢?
- 爬取 IsRayNotArray 的文章標題與連結
- 當使用者輸入
!blog 時,機器人會回傳一篇文章的標題與連結
- 每天晚上九點自動發送一篇文章的標題與連結
爬取文章標題與連結
那麼爬蟲方面的話,其實也是前面寫過的範例程式碼
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 35 36 37
| const fs = require('fs'); const cheerio = require('cheerio');
const getData = async (url) => { try { const response = await fetch(url); const data = await response.text(); return data; } catch (error) { console.log(error); } }
const crawler = async () => { const target = 'https://israynotarray.com/'; const html = await getData(target);
const $ = cheerio.load(html);
const postTitleLink = $('.post-title-link');
const data = [];
postTitleLink.each((index, element) => { const title = $(element).text(); const url = $(element).attr('href'); data.push({ title, url: `${target}${url}`, }); });
return data; }
module.exports = crawler;
|
只是最後我們把它導出,這樣我們就可以在其他檔案使用了。
註冊指令
接下來為了實現可以輸入 !blog 時,機器人會回傳一篇文章的標題與連結,我們就需要註冊一個指令,這邊我們就使用前面所寫過的 register.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 29 30
| require('dotenv').config();
const { REST, Routes } = require('discord.js');
const list = [ { name: 'blog', description: '取得首頁第一頁隨機文章', }, ];
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
const registerCommands = async (commands) => { try { console.log('Started refreshing application (/) commands.');
await rest.put(Routes.applicationCommands(process.env.DISCORD_CLIENT_ID), { body: commands });
console.log('Successfully reloaded application (/) commands.'); } catch (error) { console.error(error); } }
registerCommands(list);
|
其實註冊指令也非常簡單,絕大部分都是不需要調整的,只需要調整 list 這個陣列就可以了,這邊我們只需要註冊一個指令,所以就只有一個物件,如果你要註冊多個指令,就可以在陣列中新增物件。
完成以上後,你可以先輸入 node register.js 來註冊指令,如果沒有錯誤的話,你就可以在 Discord 看到你剛剛註冊的指令了。
Note
查看方式很簡單,只需要在 Discord 中輸入 / 就可以看到你剛剛註冊的指令了;如果沒看到指令的話,你可以嘗試按下 Ctrl + R(Windows)或 Cmd + R(Mac)重新整理 Discord。
機器人
首先我們會需要使用一個事件,也就是 interactionCreate 事件,這個事件會在使用者與機器人互動時觸發,例如:使用者輸入指令、點擊按鈕等等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const { Client, GatewayIntentBits, Partials, Events, } = require('discord.js');
client.on(Events.InteractionCreate, async (interaction) => { console.log(interaction); });
client.login(process.env.DISCORD_TOKEN);
|
接下來請你先輸入 node index.js 啟動機器人,並試著在 Discord 上輸入 /blog,你會看到終端機上輸出了一個物件,這個物件就是 interaction,我們可以透過這個物件來判斷使用者輸入的指令是什麼

接下來我們必須判斷使用者輸入的指令是否為 /blog,如果是的話,我們就要回傳一篇文章的標題與連結,如果你不這樣做的話,當你有多個指令時,就會無法正確判斷使用者輸入的指令是什麼,而判斷方式很簡單,在 interaction 物件有一個 commandName 的屬性,這個屬性就是使用者輸入的指令名稱,所以我們只需要判斷這個屬性是否為 blog 就可以了
1 2 3 4 5
| client.on(Events.InteractionCreate, async (interaction) => { if (interaction.commandName === 'blog') { } });
|
接下來當然就是要引入我們的爬蟲程式碼了,這邊我們就直接引入 crawler.js,然後呼叫 crawler 函式,這個函式會回傳一個陣列,裡面包含了文章的標題與連結,我們只需要隨機取出一個就可以了,程式碼方面也不用擔心,我會補上註解說明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const crawler = require('./crawler');
client.on(Events.InteractionCreate, async (interaction) => { if (interaction.commandName === 'blog') { const data = await crawler(); const randomIndex = Math.floor(Math.random() * data.length); const randomData = data[randomIndex]; await interaction.reply(`文章標題:${randomData.title}\n文章連結:${randomData.url}`); } });
|
接下來我們就可以來啟動機器人了,請輸入 node index.js,然後在 Discord 上輸入 /blog,你就會看到機器人回傳了一篇文章的標題與連結了!而且也是隨機的!

Note
當使用指令呼叫後,務必要使用 interaction.reply 來回傳資料給使用者,如果你使用 interaction.channel.send 的話,會發生錯誤,這是因為 interaction.channel.send 是用來回傳訊息的,而不是用來回傳指令的。
定時發送
接下來就是要來實作每天晚上九點自動發送一篇文章的標題與連結,這邊我們會使用到 cron 這個套件,這個套件可以讓我們定時執行程式碼,所以我們可以透過這個套件來實作。
首先我們先來安裝 cron:
接著建立一個 postCron.js
並輸入以下程式碼
1 2 3 4 5 6 7 8
| const cron = require('cron');
const job = new cron.CronJob('0 21 * * *', () => { console.log('每天晚上九點執行一次'); });
job.start();
|
你可以先改成 * * * * *,這樣就會每分鐘執行一次,這樣你就可以先測試一下,看看有沒有正確執行了,若沒有問題的話,我們要將程式碼改成以下:
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
| const cron = require('cron'); const crawler = require('./crawler');
const postCron = (client) => { const job = new cron.CronJob('* * * * *', async () => { const data = await crawler(); const randomIndex = Math.floor(Math.random() * data.length); const randomData = data[randomIndex]; const channel = client.channels.cache.get('頻道 ID'); await channel.send(`每日晚間九點隨機推薦 Ray 的一篇文章:[${randomData.title}](${randomData.url})`); });
return job; }
module.exports = postCron;
|
Note
頻道 ID 的取得方式很簡單,只需要在 Discord 上右鍵點擊頻道,然後點擊「複製 ID」就可以了。
接著回到 index.js,引入 postCron.js,並在 client.once(Events.ClientReady, () => { ... }) 中啟動 job
1 2 3 4 5 6 7 8 9
| const postCron = require('./postCron');
client.once(Events.ClientReady, () => { console.log('Ready!'); postCron(client).start(); });
|
接下來當你啟動機器人之後,每天晚上九點就會自動發送一篇文章的標題與連結了!
你可以看到我將 client 作為參數傳入 postCron,這樣的好處是,我們可以在 postCron 中使用 client,這樣就可以取得頻道,並發送訊息了。
Note
如果你想要在本機測試的話,請記得要把 0 21 * * * 改成 * * * * *,這樣就會每分鐘執行一次了;如果覺得 cron.js 中的程式碼跟 index.js 的程式碼過於雷同,你可以嘗試抽出來作為一個函式使用。
透過以上範例,你可以將你前面所學到的東西整合起來唷!那麼這一篇也差不多要告一個段落了,我們下一篇見。
Note
我先前也有分享過「用 OpenAI GPT-3 建立一個 Discord 聊天機器人」,若有興趣的話也可以參考唷。
整理這些技術筆記真的很花時間,如果你願意 關閉 Adblock 支持我,我會把這份感謝轉換成更多「踩坑轉避坑」的內容給你!ヽ(・∀・)ノ
Advertisement