Next Level Discord Bot กับการแจ้งเตือนประชุมหลบวันหยุด กิจกรรมในทีม และวันลา

Programming Apr 22, 2022

ความเดิมตอนที่แล้ว เราเขียนบล็อกสร้างบอท Discord ด้วย discord.js ให้คนในทีมสามารถเล่นบอทได้ และแจ้งเตือนประชุมได้แล้ว แต่เหมือนว่าเป็นบอทที่ยังไม่เสร็จดี เพราะต้องอิงกับปฏิทินด้วย

ทำ Discord bot แจ้งเตือนคนในทีมให้ไปประชุม
การประชุมเป็นส่วนหนึ่งในกิจวัตรประจำวัน ตามพิธีกรรมใน Agile ดังนั้น เราก็เลยลองทำดู ว่ามันทำยังไงได้บ้างนะ
สร้างบอท Discord ไว้คุยกับทีมแบบง่ายๆกันเถอะ
พอดีทีมใช้ Discord ในการคุยงานกัน และจะสร้างบอทวันลา เลยลองมาสร้างบอทใน Discord ดูกันเนอะ

หน่ึงปีผ่านไปไวเหมือนโกหก บอทแจ้งเตือนที่เตือนทุกวันจันทร์ถึงศุกร์ แต่ดันไปเตือนวันหยุดด้วยเป็นสิ่งที่น่าแก้ไขสุดๆสำหรับเรา เราคิดว่าถ้าให้บอทไป check ว่า เอ้ยยย วันนี้วันหยุดนะ ไม่ต้องยิงบอทให้ทุกคนเข้า standup meeting ก็คงจะดีไม่น้อยเลย

รูปเทรสมาจาก https://www.youtube.com/watch?v=IMpXNQ-MLT4

สำหรับใครที่ไม่เคยทำ 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 ขึ้นมาดูตามนี้ได้เลย

Discord Developer Portal — API Docs for Bots and Developers
Integrate your service with Discord — whether it’s a bot or a game or whatever your wildest imagination can come up with.

จากนั้นเก็บ 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 นั่นเอง ทั้งสองอันนั้นคืออันเดียวกัน ต่างกันที่ภาษา

บอทแจ้งเตือนในทีมเราใช้ วันหยุดในไทย เพราะ พอเป็นภาษาอังกฤษก็จะงงๆว่าวันอะไรนะ โดยวิธีการนำ 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 วันหยุดของวันนี้ ว่าวันนี้มีวันหยุดไหม และตรงกับวันอะไร

Events: list | Calendar API | Google Developers

ก่อนอื่น เราต้องใช้ API Key ในการเข้าใช้งาน วึ่งเราจะต้องสร้างแอพใน Google Cloud ขึ้นมาเสียก่อน ดูตามนี้เลยแล้วกัน

Create a Google Cloud project | Google Workspace for Developers | Google Developers

จากนั้นเรียกใช้ 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}>

Mentions - Embed Generator
To mention someone or something you need to use the api format

ซึ่งก็ไม่ใช่ทุกคนที่ได้ 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 time
  • holiday แสดงวันหยุด 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 จ้า

discord.js Guide
Imagine a guide... that explores the many possibilities for your discord.js bot.
https://discordjs.guide/creating-your-bot/creating-commands.html#registering-commands

github ของโปรเจกนี้จ้า

GitHub - mikkipastel/bb8-discord-bot
Contribute to mikkipastel/bb8-discord-bot development by creating an account on GitHub.

สามารถ support ค่ากาแฟเจ้าของบล็อกได้ที่ปุ่มแดงส้มสุดน่ารักที่มุมซ้ายล่าง หรือกดปุ่มตรงนี้ก็ได้จ้า

Buy Me a Coffee at ko-fi.com

ติดตามข่าวสารและบทความใหม่ๆได้ที่

อย่าลืมกด like กด share บทความกันด้วยนะคะ :)

Posted by MikkiPastel on Sunday, 10 December 2017

Subscribe ช่อง YouTube ของเราได้ที่

mikkicoding
Android Developer & Content Creator
https://www.youtube.com/channel/UCtGbMSe4i7NJiKQ271Fezcg

download แอพอ่านบล็อกใหม่ของเราได้ที่นี่

MikkiPastel - Apps on Google Play
First application from “MikkiPastel” on play store beta feature- read blog from https://www.mikkipastel.com by this application- read blog content by chrome custom tab- update or refresh new content by pull to refresh- share content to social network
https://play.google.com/store/apps/details?id=com.mikkipastel.blog

Tags

Minseo Chayabanjonglerd

I am a full-time Android Developer and part-time contributor with developer community and web3 world, who believe people have hard skills and soft skills to up-skill to da moon.