Next Level Discord Bot กับการแจ้งเตือนประชุมหลบวันหยุด กิจกรรมในทีม และวันลา
ความเดิมตอนที่แล้ว เราเขียนบล็อกสร้างบอท Discord ด้วย discord.js ให้คนในทีมสามารถเล่นบอทได้ และแจ้งเตือนประชุมได้แล้ว แต่เหมือนว่าเป็นบอทที่ยังไม่เสร็จดี เพราะต้องอิงกับปฏิทินด้วย
หน่ึงปีผ่านไปไวเหมือนโกหก บอทแจ้งเตือนที่เตือนทุกวันจันทร์ถึงศุกร์ แต่ดันไปเตือนวันหยุดด้วยเป็นสิ่งที่น่าแก้ไขสุดๆสำหรับเรา เราคิดว่าถ้าให้บอทไป check ว่า เอ้ยยย วันนี้วันหยุดนะ ไม่ต้องยิงบอทให้ทุกคนเข้า standup meeting ก็คงจะดีไม่น้อยเลย
สำหรับใครที่ไม่เคยทำ Discord bot เราจะพยายามค่อยๆไปด้วยกันเนอะ
ปล. บล็อกนี้ต้องขอบคุณน้องในทีมที่คอยแก้โค้ดให้เนอะ
ยิงแจ้งเตือนประชุม
ก่อนอื่น ลง library ดังต่อไปนี้
- node-cron ใช้ในการยิง cron job ต่างๆ ในที่นี้ใช้ยิงแจ้งทีมประชุม
- dotenv เก็บพวก key ต่างๆที่ต้องใช้ โดยไม่ต้องอัพขึ้น git ใดๆนะ สร้างไฟล์ที่ชื่อว่า
.env
ไป ignore ไฟล์นี้ใน.gitignore
กันด้วยนะ - discord.js พระเอกของเราาาา เอามาใช้งานบอท
npm install node-cron
npm install dotenv
npm install discord.js
จากนั้นสร้างไฟล์ js อันนึง อ่ะชื่อว่า app.js
ก็แล้วกัน มา include ของเหล่านี้เนอะ
const cron = require('node-cron');
const dotenv = require('dotenv');
dotenv.config();
const { Client, Intents, WebhookClient } = require('discord.js');
const client = new Client({ intents: [Intents.FLAGS.GUILDS] });
จากนั้นมา implement ในส่วนที่จะยิงข้อความไปใน discord ของเรา จะใช้ client.once()
เนอะ ประมาณนี้
client.once('ready', async () => {
console.log('Ready!');
try {
//todo
} catch (error) {
console.error('Error trying to send a message: ', error);
}
});
แล้วก็สร้าง Discord bot ขึ้นมาดูตามนี้ได้เลย
จากนั้นเก็บ bot token ไว้ใช้ login ด้วยนะ
client.login(process.env.DISCORD_BOT_TOKEN);
เราใช้ cron job ในการให้บอทยิงเตือนในวันจันทร์ถึงศุกร์ เวลาสิบโมงนะ
มีวิธียิง webhook อยู่ 2 วิธีด้วยกัน แต่ต้องมี permission ในการดึงของพวกนี้ออกมาด้วยนะ
1) ยิงผ่าน Webhook
เป็นวิธีที่เคยทำในบล็อกก่อนเนอะ ก็คือสร้างตัวแปร webhook เอา WEBHOOK_ID
กับ WEBHOOK_TOKEN
มาใส่ จะได้จาก url ของ webhook หน้าตาจะเป็นแบบนี้ https://discord.com/api/webhooks/{
WEBHOOK_ID
}/{WEBHOOK_TOKEN
}
.
2) ยิงผ่าน Channel
วิธีนี้ได้จากน้องในทีม เป็นคนใส่มาในโค้ด ซึ่งวิธีการทำไม่ยากเลย โดยการคลิกขวาที่ channel แล้ว copy ID หื้มมม
แต่วิธีนี้อาจจะใช้ไม่ได้เสมอไป ถ้า text channel นั้นไม่ได้เข้าถึงได้ทุกคนเนอะ ก็คือ permission bot ไม่ถึง ก็ไม่น่าจะใช้ได้
.
ผลที่ได้ทั้งคู่ คือ เมื่อถึงเวลา standup meeting ของทีมสิบโมงเช้า บอทจะแจ้งเตือนให้มาประชุมในวันทำงาน นั่นเอง
วันหยุดห้ามให้บอทยิงให้แจ้งเตือนประชุม
แน่นอนว่าไม่ใช่เราจะไม่ได้ทำงานทุกวันจันทร์ถึงศุกร์เนอะ ยังมีเรื่องวันหยุด ดังนั้นวันหยุดก็คือเราหยุดทำงาน จึงไม่ควรแจ้งเตือนให้ประชุมในวันหยุด
เนื่องจากใช้ Google Calendar API ตาม document แล้วติดปัญหา ก็เลยเรียกผ่าน API โดยใช้ axios
แทนเนอะ
การเอา calendarId มาใช้
ปฏิทินที่ใช้ในบล็อกนี้ก็คือ ปฏิทินวันหยุดที่มีในทุก Google Calendar นั่นเอง ทั้งสองอันนั้นคืออันเดียวกัน ต่างกันที่ภาษา
- Holiday in Thailand :
en.th#[email protected]
- วันหยุดในไทย :
th.th#[email protected]
บอทแจ้งเตือนในทีมเราใช้ วันหยุดในไทย เพราะ พอเป็นภาษาอังกฤษก็จะงงๆว่าวันอะไรนะ โดยวิธีการนำ calendar id เพื่อนำไปใช้ต่อ จะเป็นดังนี้
- ไปที่ Calendar ที่เราต้องการ ไปกดจุดสามจุด แล้วไปที่ Setting
- มันจะพามาหน้า Calendar settings เลื่อนลงมาเรื่อยๆจะเจอ Integrate calendar แล้วเราจะเจอ Calendar ID ก็ก๊อปมาใช้งานได้จ้า
การทำงานก็คือ เมื่อถึงเวลา standup meeting จะทำการ check ว่าวันนี้วันหยุดไหม ถ้าใช่ก็แค่บอกว่าวันนี้เป็นวันอะไร ถ้าไม่ใช่ก็เรียกประชุมเหมือนเดิมจ้า ก็จะประมาณนี้แหละเนอะ
ทำการต่อ API ของ Google Calendar เพื่อเรียกดูกิจกรรมทั้งหมด
เนื่องจากการใช้ library Google Calendar มีขั้นตอนที่ซับซ้อนซ่อนเงื่อนเอามากๆ แล้วเอาโค้ดเก่ามาลองใช้ก็ติด ก็เลยไปเจอวิธีว่า มันก็เรียกผ่าน API ได้นี่นา เลยลองเรียกผ่านในหน้าเว็บเขาเอง แล้วก็บน postman เอ้อมันได้เนอะ
ในที่นี้เราจะใช้ get event list ใน Google Calendar API เพื่อทำการดู event วันหยุดของวันนี้ ว่าวันนี้มีวันหยุดไหม และตรงกับวันอะไร
ก่อนอื่น เราต้องใช้ API Key ในการเข้าใช้งาน วึ่งเราจะต้องสร้างแอพใน Google Cloud ขึ้นมาเสียก่อน ดูตามนี้เลยแล้วกัน
จากนั้นเรียกใช้ API โดยหน้าตาโค้ดทั้งหมดจะเป็นแบบนี้
อธิบายแต่ละส่วน เริ่มจาก calendarId
กันก่อนเลย อันนี้ก็ตรงไปตรงมา คือ id ของปฏิทินนั้นๆ
แต่ในเคสของวันหยุดมันจะมีลักษณะของมันที่แตกต่างจากชาวบ้าน คือมีเจ้า # มา เวลาเรียก API มันจะตัดหลัง # ออกไปเลย ทำให้เรียกยังไง ก็ไม่ได้ซะที โดยวันหยุดในไทยให้ใช้อันนี้แทน
th.th%23holiday%40group.v.calendar.google.com
เข้าใจว่า %23
แทน # และ %40
แทน @ นะ
parameter ที่ใช้
orderBy
เราจะให้มันเรียงตามอะไร มีstartTime
และupdated
ในที่นี้เราจะใช้startTime
เนอะsingleEvents
อันนี้ใช้เป็นtrue
เพราะอยากได้เหตุการณ์ที่เกิดขึ้นครั้งเดียว แต่ไม่ต้องใส่ ก็ได้แหละkey
API Key ของ Google Cloud Project ของเราmaxResults
จำนวนผลลัพธ์มากที่สุดที่เราต้องการ ในที่นี้ใส่ 1 พอ เพื่อต้องการ check ว่าวันนี้วันหยุดไหมtimeMin
ให้เริ่มที่วันไหน ถ้าไม่ใส่มันอาจจะดึงตั้งแต่ event แรกของ calendar นี้ก็ได้นะ ในโค้ดเราจะใส่เวลาตอนนี้ที่เรียกบอทมาเลย เพื่อเอา event ที่เกิดขึ้นในวันนี้มาแสดง อันนี้แนะนำให้ใส่เลย
let minNow = new Date();
minNow.setHours(7, 0, 0, 0);
const timeMin = minNow.toISOString();
timeMax
อันนี้แล้วแต่ว่าจะใส่ไหม ซึ่งในที่นี้ใส่เป็นวันพรุ่งนี้ เป็นวันสิ้นสุดในการดึง event ออกมา
let maxNow = new Date();
maxNow.setDate(maxNow.getDate() + 1);
maxNow.setHours(7, 0, -1, 0);
const timeMax = (new Date(maxNow)).toISOString();
ใน document ได้กล่าวไว้ว่า timeMin
ต้องเล็กกว่า timeMax
เสมอ เช่น เราให้ timeMin
เป็นวันจันทร์ที่ 1 เจ้า timeMax
ก็ควรมากกว่า timeMin
เช่น timeMax
เป็นวันอังคารที่ 2 ไม่ใช่วันศุกร์ที่ 30 ประมาณนี้
เมื่อเราได้ response มาแล้ว เราจะทำอะไรต่อนะ?
ทำการ check length ที่ได้ว่าเราได้ของมาไหม และของที่ได้มีวันที่ที่ตรงกันอ่ะเป่า
ถ้ามันเป็น true
ทำการส่ง webhook ไปเพื่อบอกว่า วันนี้เป็นวันหยุด (จะไม่ส่งก็ได้เช่นกัน แล้วแต่เราจะ design) แต่ถ้าไม่ใช่วันหยุด บอทจะแจ้งเตือนให้เราไปประชุมกันเถอะ
const nowDate = timeMin.split('T')[0];
if (
response.data.items.length > 0
&& nowDate == response.data.items[0].start.date
) {
const printText = 'วันนี้เป็น' + response.data.items[0].summary
console.log(printText);
webhook.send(printText, {});
} else {
console.log('Today is not Holiday');
webhook.send('@everyone, standup meeting', {});
}
ในที่นี้เราจะพ่นเป็น wording ปกติก่อน ยังไม่ได้ทำให้สวยงาม
ถ้าเป็นวันหยุด ผลจะเป็นแบบนี้แหละ
ไม่แท็ก everyone แล้วจะแท็ก role อื่นได้ไหมนะ?
เนื่องจากไม่ใช่คนใน server ทุกคนที่ต้องเข้าประชุม จึงต้องมี role แยก แล้วค่อย tag คนใน role นั้นไปประชุมเนอะ
จากใน document นี้น้านนน ได้บอกเราไว้ว่า ถ้าเราต้องการให้บอทแท็กบาง role จะต้องใช้ roleId
ในการแท็ก ซึ่งมี syntax คือ <@&{roleId}>
ซึ่งก็ไม่ใช่ทุกคนที่ได้ roleId
มานะ แล้วแต่ permission ด้วยว่าเราได้สิทธิ์แค่ไหนใน server นั้นๆเนอะ
การ get roleId
มาใช้ ให้คลิกขวาที่ server จากนั้นเลือก Server Settings จากนั้นเข้าไปใน role ที่เราต้องการ กดตุ่มสามจุด แล้วรีพอร์ต ไม่ใช่หล่ะ กดตุ่มสามจุดแล้ว Copy ID มาเนอะ
จากนั้นเราเอา roleId
ที่ได้ เอาไปใส่ไว้ในไฟล์ .env
ไว้ ตั้งชื่อว่า process.env.ROLE_ID
แล้วกันเนอะ
แล้วก็สร้าง function ขึ้นมาตัวนึง เพื่อเอาค่า roleId
มารวมร่าง เพื่อให้เป็นไปตาม syntax นี้ <@&{roleId}>
.
ปล. เราจะใส่ roleId
เป็น parameter ก็ได้นะ
ทำบอทตรวจสอบวันลาของทีม
ในที่นี้เราจะอัพเกรดมัน เป็นแบบใช้ Slash Commands ซึ่งตัว library เป็น v.13 แล้วนะ
ก่อนอื่น ลง library ดังนี้
npm install @discordjs/builders @discordjs/rest discord-api-types
แล้วก็ include ของพวกนี้ไปเพิ่มนะ
const { SlashCommandBuilder } = require('@discordjs/builders');
const { REST } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v9');
จากนั้นเพิ่ม command ต่างๆที่เราต้องการ เป็นตัวแปรที่ชื่อว่า command
เพิ่มไป 2 อันพอแล้วกัน คือ
ping
ให้ตอบ pong ไป พร้อม latency timeholiday
แสดงวันหยุด 10 วันข้างหน้าเนอะ
แล้ว map list ทั้งหมดเป็น json
.
จากนั้นสร้างตัวแปร rest
เพื่อเชื่อมต่อ
.
ค่าที่ต้องเอามาใส่
DISCORD_BOT_TOKEN
อันนี้คือ bot token ที่เราเอามาใช้ในการ login ในตอนแรกBOT_CLIENT_ID
เข้าไปที่ Application แล้วเข้าไปที่ bot ของเรา แล้วทำการ copy Application ID ออกมา
SERVER_ID
วิธีการเอาไปใช้ ไปที่ server setting -> Widget แล้วทำการ copy ออกมา
เกือบสุดท้าย ทำการ register command ของเรา โดยใช้ event interactionCreate
ถ้าไม่ใช่ command เราก็จะข้ามมมมันไป ถ้าใช่เราก็มา check ว่าใช่ command นี้ของเราไหม แล้วทำงานที่เราต้องการ
ถ้าเป็น ping
ก็ทำการ reply pong ออกมา น้องในทีมทำให้มันพิเศษขึ้น โดยใส่เวลา latency เข้าไปด้วย
และไฮไลท์ ก็คือ holiday
เป็นการดึงวันหยุดที่จะเกิดใน 10 events ข้างหน้าเนอะ
.
และสุดท้าย เราสร้าง getHolidayList()
ใน calendar.js
แล้วเอามาใช้เมื่อถูกเรียก command holiday นะ
.
หลังจาก deploy แล้วลองดู command ที่เราสร้างกันเสียหน่อย
ทดสอบการใช้งานส่วน calendar ก็จะประมาณนี้เนอะ
document ส่วนการใช้ Slash Commands จ้า
github ของโปรเจกนี้จ้า
สามารถ support ค่ากาแฟเจ้าของบล็อกได้ที่ปุ่มแดงส้มสุดน่ารักที่มุมซ้ายล่าง หรือกดปุ่มตรงนี้ก็ได้จ้า
ติดตามข่าวสารและบทความใหม่ๆได้ที่
Subscribe ช่อง YouTube ของเราได้ที่
download แอพอ่านบล็อกใหม่ของเราได้ที่นี่