วิธีทำ LINE Chatbot สำหรับ developer มือใหม่ ไม่ง้อ DialogFlow
หลายๆคนอาจจะเพิ่งลองทำ LINE chatbot แล้วประสบปัญหาทำใน DialogFlow แต่ใช้ไม่ได้เพราะไม่มีบัตรเครดิต ถ้าอยากลองเขียนต้องทำยังไงบ้างนะ?
เนื่องจากบล็อกที่แล้ว ก็ค่อนข้างยาวแหละ เป็นครั้งแรกที่ทำ LINE Chatbot โดยไม่ใช้ DialogFlow และเขียนด้วยภาษา Javascript นะ
ดังนั้นเราเลยทำบล็อกนี้มาเพื่อให้มือใหม่นำไปใช้ตามได้ เหมือนช่วยจับมือทำทางไกลเลยทีเดียว
ในตอนนี้เรามา checklist กันก่อน ว่ามีขั้นตอนหลักๆอะไรบ้าง
- สร้าง LINE OA
- สร้างโปรเจก Firebase
- ทำการ setup Firebase Project ให้เรียบร้อย
- มาเขียน echo chatbot กันเถอะ
- ทำการตั้งค่าเพื่อทำ Firebase Emulator Suite
- ทดสอบใน localhost ด้วย Firebase Emulator Suite และ ngrok
- ทำการ deploy จริง และนำ webhook เพื่อให้ LINE Chatbot ใช้งานได้
ดังนั้นให้ลืม DialogFlow ไปก่อน เพราะเราทำการ implement chatbot ผ่าน Cloud Functions for Firebase ซึ่งสะดวกกว่าทำผ่าน inline editor ของ DialogFlow และทำการทดสอบด้วย Firebase Emulator Suite และ ngrok แล้วนำตัว url ที่ได้จาก ngrok มาใส่ใน LINE chatbot ของเรานั่นเอง
สร้าง LINE OA
ขออนุญาติลักไก่แปะลิ้งนี้เนอะ
สร้างโปรเจก Firebase
ไปที่ Firebase Console กันก่อนเลย เพื่อกด Add project เพื่อสร้างโปรเจกใหม่นี้กัน
ตั้งชื่อโปรเจกที่เราต้องการ เมื่อตั้งชื่อแล้วสามารถเปลี่ยน id ของ project ได้ด้วยนะ โดยกดเปลี่ยนที่ปากกา ใต้ชื่อ project name ของเรา
ต่อมาปิด Analytics ไปก่อน แล้วกดปุ่ม Continue
จากนั้นรอสร้างโปรเจกแปปนึง
เสร็จแล้วจะได้หน้าตาแบบนี้
กด Continue เสร็จแล้วจะได้หน้าตาแบบนี้เนอะ
ทำการ setup Firebase Project ให้เรียบร้อย
ก่อนอื่น ให้ทำการ install Node.js และ Firebase CLI ให้เรียบร้อยก่อนเนอะ ผ่าน command line ด้วยคำสั่งนี้
npm install -g firebase-tools
สร้าง folder เปล่าๆที่เป็นชื่อโปรเจกของเรามาอันนึง และ copy pathname เก็บเอาไว้
จากนั้นทำการเข้าถึง path ของที่ตั้งของ folder นี้ด้วย Terminal ด้วยคำสั่ง command line
แล้วทำการ login Firebase ผ่าน Firebase CLI
firebase login
และทำการ initial project ของเรา ในที่นี้เราจะใช้ Cloud Functions for Firebase นะ
firebase init functions
เมื่อกี้เราสร้าง project ใน Firebase มาแล้ว ดังนั้นเราจะเลือก Use an existing project เน้อ
จากนั้นเลือกโปรเจกที่เราเพิ่งสร้างได้เลย
ตัว Cloud Functions for Firebase รองรับ JavaScript และ TypeScript ก็ต้องเลือก JavaScript สิค๊าบบ เพราะเราเขียน TypeScript ไม่เป็น ตึ๋งโป๊ะ!!~
ตอนนี้ยังไม่ใช้ ESLint นะ ข้ามไปก่อน
ถามต่ออีกว่าจะ install dependencies ต่างๆที่จะใช้ด้วย npm หรือไม่ เอาสิรอรัย~
และตอนนี้ก็เสร็จแล้วจ้า วิ้งๆ
มาดู folder ของโปรเจกกันคร่าวๆ เราจะเจอ folder ของ functions
ซึ่งเราจะมาเขียนโค้ดกันในนี้ ข้างใน functions
จะมี package.json
ขึ้นมา และตัว dependencies ต่างๆจะอยู่ใน node_modules
นะ
Reference :
มาเขียน echo chatbot กันเถอะ
เปิดไฟล์ index.js
ซึ่งจะอยู่ใน folder functions
จะเป็นดังนี้
ให้ทำการลบ comment เขียวๆทิ้งไป เดี๋ยวเราจะค่อยๆเล่าทีละส่วนเนอะ
ก่อนอื่นทำการ import ตัว Cloud Functions for Firebase และเราทำการ set region เป็นที่สิงคโปร หรือ asia-southeast1
เนอะ เพราะใกล้สุดนั่นเอง
ดังนั้นเราจะทำการ import ตัว Cloud Functions for Firebase เข้ามา พร้อมระบุ region ที่ใกล้ที่สุดเข้ามานั่นเอง
const functions = require("firebase-functions");
const region = "asia-southeast1";
นำ access token จากฝั่ง LINE OA เอามาใช้เพื่อทำการ authentication โดยไปที่หน้า console ที่ LINE OA ของเรา ไปที่แท็บ Messaging API
จากนั้นเลื่อนไปที่ข้างล่าง จะเจอ Channel access token เนอะ ในที่นี้ใช้แบบ long-lived ไปก่อนเนอะ แต่จริงๆสามารถเปลี่ยนเป็นแบบอื่นๆได้ทีหลังเนอะ ทำการกด Issue เพื่อ get token ไปใช้ต่อในบอทของเราจ้า
โค้ดในส่วน authentication จะเป็นดังนี้
const LINE_MESSAGING_API = "https://api.line.me/v2/bot";
const LINE_HEADER = {
"Content-Type": "application/json",
Authorization: `Bearer ${LINE_ACCESS_TOKEN}`
};
ต่อมา ทำการ import library เข้าไปอีกตัว คือ axios เข้าไป โดยทำการ import ผ่าน npm โดยเราเข้าไป import ใน folder functions
เนอะ เพื่อความถูกต้องเนอะ และใช้คำสั่ง
npm install axios --save
ผลที่ได้คือ ใน package.json
ต้องมี axios เพิ่มขึ้นมาเนอะ
ต่อมา สร้าง function ที่เป็น export ชื่อว่า อะไรดีน้าาา การตั้งชื่อเป็นอะไรที่อาจจะไม่ง่าย และควรตั้งให้เราและทีมเข้าใจได้ง่าย ในที่นี้ชื่อว่า lineBot
แล้วกันเนอะ โค้ดในส่วนนี้จะเป็นแบบนี้เนอะ เดี๋ยวเราจะมาอธิบายในแต่ละส่วนกัน
exports.lineBot = functions.region(region).https.onRequest((request, response) => {
if (request.method === "POST") {
const messageType = request.body.events[0].message.type;
console.log(request.body.events[0]);
if (messageType == 'text') {
const textMessage = request.body.events[0].message.text;
reply(
request.body.events[0].replyToken,
{ type: "text", text: textMessage }
);
}
}
return response.status(200).send(request.method)
});
const reply = (token, payload) => {
return axios({
method: "post",
url: `${LINE_MESSAGING_API}/message/reply`,
headers: LINE_HEADER,
data: JSON.stringify({
replyToken: token,
messages: [payload]
})
});
};
exports.lineBot
เป็นการ export function ที่ชื่อว่าlineBot
เพื่อนำไปใช้ต่อ ในที่นี้คือเป็น webhook ในการเชื่อมต่อระหว่าง LINE OA ที่เป็น LINE bot ของเรา กับตัวบอทเนอะregion(region)
ใส่ region ที่เราต้องการ เน้นใกล้เราไว้ก่อน จะได้โหลดไวๆrequest.method === "POST"
ดักว่าเป็น method ที่เป็นPOST
ไหม ถ้าใช้เราจะทำงานต่อนะ- ตัว
request.body.event[0]
ที่ได้ จะเป็นประมาณนี้
จากการทดลองรันใน Firebase Emulator นะ สิ่งที่เราสนใจ คือ type ที่เป็น message.type
เพื่อเอามาใช้ในการส่งข้อความที่เราพิมพ์กลับไปเป็น echo bot เนอะ และ replyToken
ใช้ในการส่งข้อความจากบอทไปให้ user เนอะ
- เราจะต้องดักก่อนว่า message ที่ได้มานั้น มี type เป็น
text
ถึงจะทำงานต่อ เพราะถ้าเราไม่ได้ดักไว้ อาจจะมีข้อความ type อื่นเช่น sticker จะทำให้โค้ดที่เราเขียนนั้น crash ได้จ้า เพราะเราไม่สามารถดึงmessage.text
ได้นั่นเอง - เรานำข้อความที่ได้จากการที่ user พิมพ์เข้ามา ซึ่งก็คือ
request.body.events[0].message.text
ทำการส่งกลับให้เป็น echo bot ซะ - ทำการปั้นข้อความ แล้ว reply ส่งไปให้ user โดยจะส่ง
replyToken
และตัว payload ไปกับตัว json ที่เป็น message โดยใช้ axios เข้าช่วยจ้า ซึ่งแยกเป็นreply
ออกมา เผื่อใช้หลาย function เนอะ โดยการส่งจะเป็นตามใน document นี้นะ
และโค้ด echo bot ทั้งหมดจะอยู่ในนี้นะ
แน่นอนว่าคนอ่านไม่น่าจะทำ echo bot แหละเนอะ ดังนั้นการ implement ต่างๆนั้น คนอ่านสามารถเอาไปต่อยอดได้เลยจ้าว่าอยากทำบอทอะไร
ตัวอย่างสดๆร้อนๆก็คือ การทำแชทบอทแมวทิพย์ของเราในงาน Stupid Hackathon Thailand ครั้งที่ 5 นั่นเอง โดยต่อยอดจาก echobot เมื่อกี้นี่แหละ
เราจะให้น้องแมวทิพย์ที่ชื่อว่าลิลลี่ทำการตอบกลับแบบเมี้ยวๆไปให้ ซึ่งเราจะมี wording ต่างๆที่เราทำเป็นลักษณะที่เรียกว่ากาชาดังนี้
.
เราเลยเขียนไฟล์ใหม่แยกไปโดยเฉพาะ สร้าง class ใหม่ขึ้นมา ชื่อว่า Preset
ในนั้นจะมี word และ weight ของแต่ละคำ และมีผลรวมกันเป็น 100
จากนั้นเราทำการ random เลขออกมาตัวนึงระหว่าง 0 - 100 จากนั้นวนลูปหาว่าเลขนี้มันตกในช่วงไหน ที่พิเศษคือถ้าสุ่มได้ช่วงคำว่า meow เราจะเติม w ให้มันดูเสียงยาวๆหน่อย
สุดท้ายอย่าลืม export มันออกมาเนาะ
กลับไปที่ index.js
เพื่อ import ที่เราทำเมื่อกี้เข้ามา แล้วที่นี้อ่ะ แมวมันไม่ค่อยมี wording เยอะ และตัวนึงที่เราสนใจก็คือ Purrrr ซึ่งเป็นเสียงจากการลูกแมว จึงแยกอีก condition นึงไว้ต่างหาก แต่ที่นี้เรารู้สึกว่าส่ง text มันดูแห้งๆไปอ่ะ ก็เลยให้ส่งเป็นเสียงไป ให้รู้สึกว่าเราลูบมันจริงๆ 555
การส่ง message type ต่างๆ สามารถดูได้เพิ่มเติมที่นี่จ้า
ถ้าอยากให้น้องลิลลี่ทำอะไรเพิ่มบอกได้เลยน้าา~~~ และสามารถเล่นกับน้องได้ที่นี่เลย
github ของโค้ดทั้งหมด ผ่านการเซ็นเซอร์ key ต่างๆด้วย Firebase Environment configuration จ้า
โค้ดทั้งหมดอ้างอิงจากของพี่ตี๋ในนี้แหละจ้า
ทำการตั้งค่าเพื่อทำ Firebase Emulator Suite
ไปที่ไฟล์ firebase.json
ซึ่งอยู่ชั้นนอกสุด เข้าไปจะเจอ json เปล่าๆอยู่
ให้ทำการใส่อันนี้ลงไป เพื่อเป็นการ setup local port ในการรัน Cloud Functions for Firebase ผ่าน Firebase Emulator Suite จ้า
ทดสอบใน localhost ด้วย Firebase Emulator Suite และ ngrok
เป็นการทดสอบโดยใช้ Firebase Emulator Suite ร่วมกันกับ ngrok ก็ตามบล็อกนี้ของพี่ตี๋เลย
อันนี้เราจะมา wrap-up สรุปขั้นตอนให้เลยว่าทำอย่างไรเนอะ
เมื่อเราเขียนบอทจนพอใจในระดับนึงแล้ว อยากเทส local ก่อน ใช้ Firebase Emulator Suite ได้เลย แบบนี้
firebase emulators:start --only functions
ผลที่ได้คือ เราจะได้ path ที่เป็น localhost พร้อม port ที่สามารถใช้งานได้หล่ะ โดยเราจะใช้ port 5001 เพราะตัว emulator functions จะเป็น localhost:5001
นั่นเอง
ลองเปิดเว็บ UI ดูสิว่าเป็นอย่างไร เราสามารถดูหน้า UI ของ Firebase Emulator Suite ได้ด้วยนะ
แต่ด้วยความ localhost คือเทส API ไว้ดูอ่ะได้ แต่เทสบอทเนี่ย ตัว LINE OA ของเราจะรู้จักเจ้า localhost ไหม? ก็ไม่
ดังนั้นทำการเชื่อมอุโมงด้วย ngrok จ้า ติดตั้งโปรแกรมให้เรียบร้อย โดยการแตกซิปผ่าน command line เน้อ
จากนั้นทำการเชื่อมต่อกับตัว ngrok โดยไป get token ของ account ของเราจากตัว dashboard มา แล้วใส่ token ที่ได้ตามหลังแบบนี้
/ngrok authtoken YOUR_AUTH_TOKEN
ซึ่งทำเฉพาะครั้งแรกพอนะในการเชื่อมต่อ account หน่ะนะ
สุดท้าย ทำการเปิด Terminal อีกหน้าต่างนึง เพื่อทำการเชื่อมต่อ localhost ของเรากับอุโมง ซึ่ง localhostดังนั้นเราจะเชื่อมต่อกันด้วย port 5001นะ
./ngrok http 5001
เราก็จะได้ url ที่ผ่านการเข้าอุโมง นำไปใช้ต่อใน chatbot ของเราแล้วจ้า
เราสามารถตรวจสอบได้ด้วยการเข้าไปที่ url path ที่ผ่านการต่ออุโมงมา ซึ่งจะต้องเข้าหน้า console ของ Firebase Emulator Suite ได้เหมือนกัน
จากนั้นนำไปใส่ใน webhook url บนหน้า console ของ LINE OA เนอะ
มาดู url ที่ได้จากการ run Firebase Emulator Suite กันก่อนเลยจ้า
http://localhost:5001/{project_id}/asia-northeast1/lineBot
ถ้าเราเอาไป deploy จริง สิ่งที่เปลี่ยนก็คือตรง root ที่เปลี่ยนไปเนอะ
เอา url ที่ได้จากการเชื่อมอุโมงเมื่อสักครู่ไปใส่แทน localhost จะได้ดังนี้
https://47fcb2a3a8e7.ngrok.io/{project_id}/asia-northeast1/lineBot
เราก็ก้อปปี้ตัว path ที่เป็น https นี้ไปใส่ได้เลย เมื่อใส่ถูกต้องแล้วทำการกด Update เลยจ้า
แล้วก็อย่าลืมเปิดให้ใช้ webhook ด้วยนะ
และก่อนใช้ อย่าลืมปิด auto-response messages ก่อนด้วยนะ
ทดสอบการทำงาน เพิ่มเพื่อน LINE OA ที่เป็น chatbot ตัวนี้กันไปก่อนนะ ผ่าน QR Code
ผลที่ได้จะเป็นประมาณนี้
ในระหว่างการทดสอบเราจะเห็น log ต่างๆที่เราทำการตั้งไว้ด้วยหล่ะ ถ้ามีตรงไหน crash เราสามารถแก้โค้ด แล้วเซฟ และทดสอบใหม่ได้เลยไม่ต้อง deploy ใหม่เลย
สามารถเข้าไปอ่าน document เพิ่มเติมได้ด้านล่างจ้า
พวก key ต่างๆที่เราใช้และอยากเก็บให้เป็นระเบียบ สามารถดูได้ที่นี่นะ (เพราะมันใช้ Environment configuration ไม่ได้อ่ะ แงงง)
ทำการ deploy จริง และนำ webhook เพื่อให้ LINE Chatbot ใช้งานได้
ถ้าอยากใช้งานจริงๆ อย่าลืม upgrade project ของ Firebase ให้เป็น Blaze Plan เสียก่อน เพราะตัว Cloud Functions for Firebase มันเชื่อมต่อ service ฝั่ง Google Cloud ด้วย
Firebase มี 2 plans คือ Spark Plan อันนี้ฟรี และ Blaze Plan เป็น Pay as you go เนอะ
สุดท้ายใช้คำสั่งนี้บน command เพื่อ deploy จริง
firebase deploy --only functions:lineBot
และเราก็ได้เส้น webhook มาใช้จริงได้เลย หน้าตาจะเป็นดังนี้
https://{region}-{project_id}.cloudfunctions.net/lineBot
การเก็บ key ต่างๆบน production สามารถดูได้ที่นี่ได้เลยจ้า
สุดท้าย ถ้าอยากรู้ว่า Firebase Emulator Suite ทำอะไรได้บ้าง มาดูในนี้ได้เลย
หรือถ้าอยากไปลองเล่นก็เล่น codelab ได้เน้อ
download แอพอ่านบล็อกใหม่ของเราได้ที่นี่
ติดตามข่าวสารและบทความใหม่ๆได้ที่
และช่องทางใหม่ใน Twiter จ้า