ใน Workshop นี้ เราจะสร้างบอตที่สามารถทำนายอนาคตของคุณ โดยการสุ่มคำทำนายจากข้อมูลที่เรากำหนดไว้
- ไปที่ https://codesandbox.io/p/devbox/fortune-teller-template-th-6kzpwn
- กดปุ่ม "Fork" ที่มุมขวาบนของหน้า
หลังจาก Fork แล้ว โปรเจคที่เห็นในจอจะเปลี่ยนเป็นโปรเจคที่เป็นของคุณเอง และสามารถแก้ไขได้ตามต้องการ
- กดปุ่ม "Share" ที่มุมขวาบนของหน้า
- ที่ "Change permissions" ให้เลือก "Unlisted" เพื่อทำให้โปรเจคสามารถเข้าถึงได้
- กดที่กล่องสี่เหลี่ยมที่มุมซ้ายบน แล้วกด "Restart Devbox"
- สร้างบอตใหม่ตามขั้นตอนในไฟล์นี้
- หลังจากสร้างบอตเสร็จแล้ว ไปที่หน้า "Basic settings" ของบอต
- คัดลอก Channel secret ไปแทนที่คำว่า
CHANNEL_SECRET
ในไฟล์index.js
- ไปที่หน้า "Messaging API" ของบอต
- คัดลอก Channel access token ไปแทนที่คำว่า
CHANNEL_ACCESS_TOKEN
ในไฟล์เดียวกัน - ใน Codesandbox, กด Save (
Ctrl
+S
), โปรแกรมจะเริ่มใหม่โดยอัตโนมัติ
โปรเจคที่เพิ่งก็อปไปประกอบไปด้วยไฟล์สำคัญ 2 ไฟล์ คือ
index.js
ไฟล์หลักของโปรเจค ที่เราจะเขียนโค้ดของบอต โปรแกรมจะเริ่มทำงานจากไฟล์นี้fortune.json
เก็บข้อมูลคำทำนายเพื่อใช้ส่งกลับไปยังผู้ใช้
- ในไฟล์
index.js
, เพิ่มโค้ดต่อไปนี้ใต้ตัวแปรlineConfig
const messagingClient = new line.messagingApi.MessagingApiClient(lineConfig);
messagingClient
ที่เพิ่งสร้างนี้เป็นตัวช่วยในการใช้งาน LINE เพื่อส่งข้อความกลับไปยังผู้ใช้
- เพิ่มโค้ดต่อไปนี้ที่ล่างสุดของไฟล์
index.js
function reply(event, ...messages) {
return messagingClient.replyMessage({
replyToken: event.replyToken,
messages: messages.map(message => ({
type: "text",
text: message,
})),
});
}
ฟังก์ชั่น reply
เรียกใช้ตัวช่วย messagingClient
ให้เราส่งข้อความกลับไปยังผู้ใช้ได้ง่ายขึ้น
- และเพิ่มโค้ดต่อไปนี้ที่ล่างสุดของไฟล์
index.js
ต่อจากโค้ดที่เพิ่งเพิ่มไป
function handleLineEvent(event) {
return reply(event, "สวัสดีจากดูดวง Bot");
}
- กด Save
- ใน Codesandbox จอครึ่งขวาจะเป็น Browser ที่มีลิงค์อยู่ด้านบน ให้ Copy ลิงค์นั้นไปวางในหน้า "Messaging API" ของบอต ในช่อง "Webhook URL"
- กด "Update" แล้วกด "Verify" เพื่อทดสอบการเชื่อมต่อระหว่าง LINE กับบอต; ถ้าทดสอบผ่านจะขึ้นคำว่า "Success"
- กดเปิด "Use webhook" ถ้าหากยังไม่ได้เปิด
- ตั้งค่าบอทเพิ่มเล็กน้อยตามไฟล์นี้
- ลองส่งอะไรก็ได้ใน LINE ดู
- สร้าง constant แทนค่าดวงแต่ละประเภทเพื่อความสะดวกในการใช้งาน
const FortuneCategory = {
Love: "love",
Career: "career",
Finance: "finance",
Health: "health",
};
- สร้างฟังก์ชั่น
getFortune
สำหรับการสุ่มดวงในประเภทนั้นๆ
function getFortune(category) {
const index = Math.floor(Math.random() * fortunes[category].length);
return fortunes[category][index];
}
- สร้างตัวแปร
counter
ไว้เช็คว่าเราดูดวงไปกี่รอบแล้ว
let count = 1;
- สร้างฟังก์ชัน
getAllFortuneMessages
สำหรับสุ่มดวงประเภทละอัน แล้วเอามาทำเป็นข้อความ Line
function getAllFortuneMessages() {
const fortuneMessages = [
`ดูดวงประจำวันครั้งที่ ${count++}`,
`ด้านความรัก\n${getFortune(FortuneCategory.Love)}`,
`ด้านการงาน\n${getFortune(FortuneCategory.Career)}`,
`ด้านการเงิน\n${getFortune(FortuneCategory.Finance)}`,
`ด้านสุขภาพ\n${getFortune(FortuneCategory.Health)}`,
];
return fortuneMessages;
}
- เปลี่ยนฟังก์ชั่น
handleLineEvent
ให้ตอบข้อความด้วยดวงที่เราพึ่งสุ่มมา
function handleLineEvent(event) {
return reply(event, ...getAllFortuneMessages());
}
- กด Save แล้วลองส่งอะไรก็ได้ใน LINE ดู
ถึงตอนนี้ บอตจะให้ดวงกลับมาทุกครั้งที่เราส่งข้อความไป แต่เราต้องการให้บอตตอบดวงกลับมาเฉพาะเมื่อเราพิมพ์ไปว่า "ดูดวง" เท่านั้น
- เปลี่ยนฟังก์ชั่น
handleLineEvent
ให้เช็คก่อนว่าข้อความที่ถูกส่งมาคือคำว่า "ดูดวง" รึเปล่า
function handleLineEvent(event) {
// หยิบเอา text จาก message ใน event ออกมา
const text = event.message.text;
if (text === "ดูดวง") {
return reply(event, ...getAllFortuneMessages());
}
}
-
กด Save แล้วลองส่งอะไรก็ได้ใน LINE ดู; ถ้าไม่ได้ส่ง "ดูดวง" บอตจะไม่ตอบอะไรกลับมา
-
เปลี่ยนฟังก์ชั่น
handleLineEvent
อีกครั้งให้ส่งอย่างอื่นกลับไปถ้าข้อความที่ถูกส่งมาไม่ใช่คำว่า "ดูดวง"
function handleLineEvent(event) {
// หยิบเอา text จาก message ใน event ออกมา
const text = event.message.text;
if (text === "ดูดวง") {
return reply(event, ...getAllFortuneMessages());
} else {
return reply(event, "???"); // << ใส่ข้อความตรง ???
}
}
- กด Save แล้วลองส่งอะไรก็ได้ใน LINE ดู
สำหรับคนที่ตามไม่ทัน ก็อปโค้ดนี้ไปแทนที่โค้ดในไฟล์ index.js
, เปลี่ยน CHANNEL_SECRET
และ CHANNEL_ACCESS_TOKEN
แล้วกด Save แล้วลองส่งอะไรก็ได้ใน LINE ดู
โค้ดเต็ม index.js
const line = require("@line/bot-sdk");
const express = require("express");
const fortunes = require("./fortune.json");
const lineConfig = {
channelSecret: "CHANNEL_SECRET",
channelAccessToken: "CHANNEL_ACCESS_TOKEN",
};
const messagingClient = new line.messagingApi.MessagingApiClient(lineConfig);
const app = express();
app.post("/", line.middleware(lineConfig), handlePostRequest);
app.listen(3000);
async function handlePostRequest(req, res) {
const { events } = req.body;
const eventHandledPromises = events.map(handleLineEvent);
const result = await Promise.all(eventHandledPromises);
return res.send(result);
}
function reply(event, ...messages) {
return messagingClient.replyMessage({
replyToken: event.replyToken,
messages: messages.map((message) => ({
type: "text",
text: message,
})),
});
}
function handleLineEvent(event) {
const text = event.message.text;
if (text === "ดูดวง") {
return reply(event, ...getAllFortuneMessages());
} else {
return reply(event, "ไม่เข้าใจจ้า พิมพ์ 'ดูดวง' เพื่อดูดวง");
}
}
const FortuneCategory = {
Love: "love",
Career: "career",
Finance: "finance",
Health: "health",
};
function getFortune(category) {
const index = Math.floor(Math.random() * fortunes[category].length);
return fortunes[category][index];
}
let count = 1;
function getAllFortuneMessages() {
const fortuneMessages = [
`ดูดวงประจำวันครั้งที่ ${count++}`,
`ด้านความรัก\n${getFortune(FortuneCategory.Love)}`,
`ด้านการงาน\n${getFortune(FortuneCategory.Career)}`,
`ด้านการเงิน\n${getFortune(FortuneCategory.Finance)}`,
`ด้านสุขภาพ\n${getFortune(FortuneCategory.Health)}`,
];
return fortuneMessages;
}