[CodeLab] Line bot translation

วันนี้เจมส์ได้นั่งดู Live codelab ในช่องทาง Facebook ในช่องทางกลุ่ม LINE Developers Thailand สนุกและได้ความรู้มากครับ ซึ่งมีกิจกรรมให้ร่วมสนุก คือ ผู้ร่วมกิจกรรม 10 คนแรกที่ดู LIVE และทำ Codelabs เสร็จอย่างถูกต้อง รับสติ๊กเกอร์ LINE DEV สุด Cool! ปิดรับถึงเวลา 21:00 แล้ว ซึ่งตอนนี้น่าจะเลยเวลาที่ปิดรับแล้ว (เจมส์ลองทำและส่งไปแล้ว ส่งไปก่อน 21:00 แล้วครับ เราเน้นเข้าร่วม ไม่เน้นเข้ารอบ ฮ่าาาา) เลยอยากมาเขียนบทความเก็บไว้

เพื่อน ๆ สามารถดูย้อนหลัง Live ได้ที่นี่เลยเน้อคับ

Sunday Codelabs

มาดูตัวอย่างถ้าทำเรียบร้อยแล้ว ผลลัพธ์จะเป็นยังไงกันดีกว่าครับ อันนี้คืออันที่เจมส์สร้างและ Submit ไปเรียบร้อยแล้วครับ

ตัวอย่าง Codelab ที่ส่งไปครับ

โจทย์หลัก ๆ ของ Codelab นี้มี 3 ข้อหลัก ๆ คือ

  1. เอา Linebot ไป join group ได้ คือเอาไป join ใน Group ไหน ก็ให้มันแปลเป็นภาษา ญี่ปุ่นให้ถ้าข้อมูลที่ส่งมาเป็น Text message
  2. ต้องมีสัญลักษณ์ ธงแสดงไว้ในส่วนที่แสดง Message ที่แปลข้อความด้วย
  3. Message จาก Linebot ต้องเป็น Flex Message

ถ้าเพื่อน ๆ อยากลองทำก่อนก็เข้าไปที่ https://codelab.line.me/ แล้วเลือกในส่วนของ Easily create a Translation Chatbot with Firebase Extensions

แต่ถ้าเพื่อน ๆ อยากดูจากบทความนี้ก็ เลื่อนลงมาอ่านได้เลยครับ ^^

อันนี้เป็น Flow ทั้งหมดที่เราจะทำเน้อครับ

Flow ทั้งหมดเป็นประมาณนี้เน้อครับ

เราจะมาไล่ทำกันไปทีละส่วนเน้อครับ

สร้าง Provider และ Channel

ขั้นแรกเรามาสร้าง Provider และ Channel กันก่อนเน้อครับ จริง ๆ แต่ถ้าเพื่อน ๆ อยากใช้ Provider เดิมก็ได้เน้อครับ เพียงแต่ต้องสร้าง Channel ใหม่ เข้าไปที่ ในที่นี้เจมส์จะอธิบายตั้งแต่สร้าง Provider เลยเน้อครับ แต่อาจจะอธิบายแบบเร็ว ๆ
คือเข้าไปที่ https://developers.line.biz/console ในส่วน Provider ให้คลิกที่ Create จะมี Dialog ขึ้นมา ประมาณนี้ครับ

สร้าง Provider name ขึ้นมา

เมื่อสร้าง Provider เรียบร้อยแล้ว ให้เลือกที่ Create a Messaging API channel

เลือ Create a Messaging API channel

จากนั้นก็กรอกข้อมูลเกี่ยวกับ Channel ต่าง ๆ ลงไป ในส่วนของ Category หรือ Subcategory สามารถเลือกอะไรก็ได้ครับ เมื่อเลือกเสร็จแล้ว ในส่วนของ Terms เราต้องติ๊ก Checkbox ด้วยเน้อ ไม่งั้นจะกด Create ไม่ได้

เมื่อสร้างเรียบร้อยแล้วให้เลือก Tab มาที่ Messaging API จากนั้นเลื่อนลงมา ที่ตรง Allow bot to join group chats ให้คลิกที่ Edit ซึ่งมันจะเปิดหน้าใหม่ขึ้นมา

คลิกที่ Edit แล้วมันจะเปิดหน้าต่างใหม่ขึ้นมา

ในหน้าใหม่ ให้เลื่อนลงมาด้านล่างในส่วน Toggle features ในส่วน Group and multi-person chats ให้เลือกมาที่ Allow account to join groups and multi-person chats

เมื่อกดที่ Allow มันจะแสดง Dialog ขึ้นมาให้ยืนยัน ให้กดยืนยัน

เมื่อกดยืนยันเรียบร้อยแล้ว ให้เลือก Menu Response settings จากนั้นในส่วน Detailed Settings ในส่วน Webhooks ให้เลือก Enabled
และ Auto-response เป็น Disabled

เลือก Webhooks ไปที่ Enabled และ Auto-response เลือก Disabled

จากนั้น ย้อนกลับมาหน้า Channel หลัก (ก่อนที่จะกด Edit ในตอนแรก) ในส่วน Channel access token ให้คลิกที่ปุ่ม Issue ก็จะเห็น Token แสดงขึ้นมา ให้จำเอาไว้ในใจก่อน เอ้ย! ให้ Copy เก็บ เอาไว้ก่อน เพราะเราจะใช้ในอนาคตครับ

ตั้งค่า Firebase

ในส่วนถัดไป เราจะมาสร้างเกี่ยวกับ Firebase กันครับ ขั้นแรกให้คลิกเข้าไปที่ https://firebase.google.com/products/extensions

จากนั้นเลื่อนลงมาด้านล่างจะเห็น Extension ชื่อ Translate Text ให้เราคลิกที่ปุ่ม Install

คลิกที่ Install เพื่อ Install extension

มันจะเปิดหน้าสร้าง Project ของ Firebase ขึ้นมา ให้กดไปที่ Add project จากนั้นก็กรอกชื่อ project

ตัวอย่างสร้าง Project firebase
ในส่วนนี้เรายังไม่ต้องใช้ Googla Analytics ก็ให้เลือกเอา Google Analytics ออก

เมื่อคลิกที่ Create project มันจะสร้าง Project ขึ้นมา จากนั้น จะแสดงหน้าให้ Install Translate Text เพิ่มขึ้นมา จากนั้นก็กด Next ไปตาม Step

ใน Step 2 เราต้องผูกบัตร แต่มันยังไม่ตัดครับ ถ้าหากเราใช้ไม่เกิน Limit ของ Free

จากนั้นก็ Next ถัดมา จน Step สุดท้ายเราจะเลือก Cloud function location เป็น Tokyo (asia-norteast1) ที่เลือกตรงนี้เพราะ Line ตั้งอยู่ที่ Japan เพื่อลด Latency ในการคุยกับ Line ในส่วนของ target เราจะใส่เป็น ja,th คือจะแปลเป็น Thai และ Japan

เราตั้งค่าประมาณนี้

collections คือ ในส่วนของ Collection path เราใส่เป็น translations อยู่ คือ collections ที่เราจะใช้กับ extension นี้

Input field name คือ ถ้า field มีการเปลี่ยนแปลงหรือสร้าง จะเรียก extension นี้

Translations output field name คือ field ที่ extension จะสร้างขึ้นมาใน firestore เป็น field translated

พูดรวม ๆ คือ เมื่อมี collections > doc > field Input มีการเปลี่ยนแปลง จะสร้าง field ชื่อ translated ขึ้นมามี value เป็น map ที่มีค่า th ที่แปลค่าจาก input เป็นภาษาไทย และมี ja ที่แปลค่าจาก input เป็นภาษาญี่ปุ่น

เมื่อเราคลิกที่ Install extension จะใช้เวลาสักพักนึง (ประมาณ 3–5 นาที) ก็เสร็จในส่วน install extension แล้ว

ในระหว่างที่ Install ให้เราเลือกไปที่ Menu firestore Database ให้คลิกที่ Create database จะแสดง Dialog ขึ้นมา ให้เลือกเป็น production mode ได้เลย เพราะ cloud function เราเป็นสิทธิ์ Admin สามารถ write ได้ จากนั้นกดที่ Next

เลือก production mode
ในส่วนนี้ให้เลือก asia-northeast1 (Tokyo)

จากนั้นให้เลือก asia-northeast1 คือ Tokyo ที่เลือกอันนี้เพื่อลด latency เหมือนเดิมครับก็คือให้มันคุยที่ Tokyo ให้เรียบร้อยเลย ^^ จากนั้นให้กดที่ Enable

ถ้าย้อนกลับไปดู Flow ข้างต้น เราจะทำเสร็จไป 3ส่วนคือ
1. ในส่วนของ Line
2. ในส่วนของ extension ใน firebase cloud function
3. ในส่วนของ Firebase Firestore

ในตอนนี้น่าจะ Install extension เรียบร้อยแล้ว (เพื่อน ๆ สามารถกดไปที่ menu extension ได้ ถ้าแสดงดังรูป แสดงว่าติดตั้งเรียบร้อยแล้ว)

ตัวอย่างเมื่อลง extension เรียบร้อยแล้ว

เพื่อน ๆ สามารถลองทดสอบว่า Extension ทำงานได้หรือไม่ โดยเข้าไปที่ Menu Firestore database คลิกที่ start collections จากนั้นลองเพิ่ม collection ชื่อว่า translations ขึ้นมา แล้วกด next

document ID ในอนาคตมันจะเป็น groupID ของ Line ที่ join เข้ากลุ่ม แต่ในที่นี้เราจะลองใส่ว่า demo ไปก่อน

field เราจะใส่ว่า input และเลือก Type เป็น String โดย value เราจะลองใส่คำว่า สวัสดี จากนั้นกด Save

ทดสอบใส่ข้อมูลลงใน firestore เพื่อทดสอบ Translate Text extension

เมื่อเรา Save ไปแล้ว จะเห็นว่าข้อมูลตอนแรกก็มีแค่ field ที่เราใส่ แต่สักพักจะมี field เพิ่มขึ้นมาอีกหนึ่ง field คือ translated และมี map value เป็น ja กับ th ดังรูปด้านล่าง

ตัวอย่างเมื่อกด save และ Translate Text extension ทำงาน

ติดตั้ง Firebase และ set up firebase cloud function

ส่วนถัดไปที่เราจะมาทำต่อก็คือ ติดตั้ง firebase ให้เพื่อน ๆ เปิด Terminal ขึ้นมา แล้วพิมพ์คำสั่ง

npm install -g firebase-tools

หากติด permission ให้เติม sudo เข้าไปด้านหน้า จะเป็น sudo npm install -g firebase-tools

ในระหว่าง install เพื่อน ๆ ต้องใจเย็นนิดนึงนะครับ บางจังหวะอาจจะมีค้างบ้าง รอให้มัน install จนเสร็จเรียบร้อย

จากนั้นเราลองเช็ค version ของ firebase ที่เรา install ไป ด้วยคำสั่ง

firebase --version

ถ้าแสดง version ของ firebase ขึ้นมาแสดงว่า install สำเร็จไม่มีปัญหาอะไรครับ

หลังจากที่เรา install firebase เรียบร้อยแล้ว เราจะ login firebaseให้ใช้คำสั่ง

firebase login

เมื่อใช้คำสั่งนี้เรียบร้อย ถ้าหากเรายังไม่ได้ login ใน terminal จะแสดงลิงค์ให้ Login แต่ถ้าหากเรา Login เรียบร้อยแล้ว มันจะแสดงประมาณว่า Already logged in as xxxx@xxx.com

ถ้าหากต้องการ logout จาก firebase เดิมที่ login อยู่ ให้ใช้คำสั่ง

firebase logout

มีทริคเล็ก ๆ ที่ได้มาจาก Firebase live เมื่อวันเสาร์ คือ ถ้าหาก ไม่ต้องการ Logout ออก แต่ต้องการ Login อีก Account นึง เราสามารถใช้คำสั่ง firebase login:add

Set up firebase cloud function

ขั้นแรกเราจะสร้าง project ขึ้นมา แล้ว initial firebase cloud function ในที่นี้เจมส์จะสร้าง project ชื่อ ex-translation (แค่สร้าง directory)

mkdir ex-translation

จากนั้นให้เข้าไปใน directory ด้วยคำสั่ง

cd ex-translation

ในขั้นถัดไปเราจะ initial firebase cloud function เข้ามาด้วยคำสั่ง

firebase init functions
เลือก Use an existing project

เลือก Use an existing project จากนั้นก็กด enter มันก็จะให้เลือก firebase project ให้เราเลือก firebase project ที่เราสร้างในตอนต้น

ในตอนต้นเจมส์ตั้ง firebase ว่า ex-translation จากนั้นก็กด enter

จากนั้นจะมีให้เราเลือกว่าจะใช้ภาษาอะไรเขียน cloud functions จะมี JavaScript และ TypeScript ในที่นี้เจมส์เลือก JavaScript จากนั้นก็กด enter

จากนั้นระบบจะถามว่าจะใช้ ESLint ไหมในการจัด Style ประมาณว่าถ้า style ไม่ถูกต้องก็จะ deploy ไม่ได้ ในที่นี้เจมส์จะพิมพ์ N แล้วกด enter คือ ไม่ใช้งาน ESLint

ระบบก็จะถามอีกว่าต้องการ install dependencies ตอนนี้เลยไหม ก็ตอบ Y แล้วก็ enter ได้เลยครับ

ตั้งค่า firebase ใน project ครับ

เมื่อเราดูใน project เราจะเห็น directory ชื่อ functions เพิ่มขึ้นมา ถัดมาเราจะ install axios เข้าไปใน firebase cloud function โดยให้ cd เข้าไปใน functions

cd functions

แล้วพิมพ์คำสั่ง

npm install --save axios

จะเห็นว่าใน package.json จะมี axios แสดงขึ้นมา

ตรงนี้ใน Live พี่ตี๋จะมีเทคนิค upgrade version เป็น lastest ด้วยคำสั่ง

ncu -u

ถ้าหากเพื่อน ๆ ลองใช้แล้วเจอว่า command not found: ncu
เพื่อน ๆ จะต้อง install npm-check-updates ก่อน ด้วยคำสั่ง
npm install -g npm-check-updates
จากนั้นเพื่อน ๆ จึงจะสามารถใช้ ncu -u ได้

จะบอกว่ามี dependencies ไหนอัพเดท version

จากนั้นให้ใช้คำสั่ง npm install อีกรอบเพื่อ install version ใหม่

npm install

ก่อนจะเริ่มเขียน Code เดี๋ยวเจมส์เอาตัวอย่าง JSON ที่ Webhook ของ Line ส่งมาเมื่อเกิด Event ต่าง ๆ ที่เราจะต้องเจอใน codelab นี้กันก่อนครับ

ตัวอย่าง Webhook ส่งมาเมื่อดึง Chat bot เข้า Group

ตัวอย่าง Webhook ส่งมาเมื่อเอา Chat bot ไป Join Group

ตัวอย่าง Webhook ส่งมาเมื่อดึง Chat bot เข้าห้อง (ไม่ใช่แบบสร้างกลุ่ม)

ตัวอย่าง Webhook ส่งมาเมื่อเอา Chat bot ไป Join Room

เวลาพิมพ์ Message ภาษาไทยเข้ามา

ตัวอย่าง Webhook ส่งมาเมื่อมีการส่ง Message ภาษาไทย ใน Group ที่มี Chat bot อยู่

เวลาพิมพ์ Message ภาษาญี่ปุ่นเข้ามา

ตัวอย่าง Webhook ส่งมาเมื่อมีการส่ง Message ภาษาญี่ปุ่น ใน Group ที่มี Chat bot อยู่

หากเพื่อน ๆ ต้องการต่อยอดเพิ่มเติม สามารถดู Webhook Event ต่าง ๆ ได้ที่นี่

ในตอนนี้เรารู้แล้วว่า Webhook ส่งมาเป็นแบบไหน จากนั้นเราจะมาเขียน firebase cloud function กันครับ

เราจะแก้ไขไฟล์ index.js ใน functions/index.js เป็นแบบนี้ครับ

อธิบายโค้ด

โค้ดบรรทัด 1– 4 เรากำหนดตัวแปรต่าง ๆ และ initial firebase admin

โค้ดบรรทัด 7–28 คือเราสร้าง cloud function ไว้รับ webhook ของ Line มา เราจะดู เฉพาะ event ในการส่ง Text Message อย่างเดียว คือ event.type เป็น message และ event.message.type เป็น text คือถ้า Message ส่งมา เราจะไป set input ใน collection translations ใน doc ที่เป็น groupId หรือ roomId ที่ได้รับจาก webhook มา เมื่อเรา set input ใน firestore ตัว extension ที่เป็น Translate Text ก็จะทำงานแบบเดิม เหมือนตอนที่เราเพิ่ม Manual ไปในตอนต้น ที่ doc เป็น demo ในส่วนของ cloud function อันนี้ทำแค่ส่วนนี้

โค้ดบรรทัด 30 — 44 จะคอยดู firestore ใน collections translations ว่ามีอะไรเปลี่ยนแปลงหรือเปล่า ถ้าหากมีการเปลี่ยนแปลงใน doc ไหน มันก็จะ trigger function นี้ ซึ่งการทำงานคือ ดูว่า field input นั้นเป็นภาษา ญี่ปุ่นหรือเปล่า เช็คจาก regular expression ถ้าเป็น ภาษาญี่ปุ่น จะเรียก function push และโดยส่ง GROUP_OR_ROOM_ID ไปและ message ที่เป็นคำแปลที่เป็นภาษาไทยไป (latest.translated.th) ก็คือไปดึงจากใน field translated ใน key th ใน firestore ถ้า message ที่ส่งมาเป็นภาษาไทย ก็จะไปดึงภาษาญี่ปุ่นมาแทน (latest.translated.ja)

โค้ดบรรทัด 46–87 คือ function ที่ใช้สำหรับเรียก API สำหรับส่ง Notification ไปที่ Line เพื่อน ๆ ต้องเปลี่ยน XXXXXXXXXXXX ในบรรทัด 52 ให้เป็น Token ที่เรากดปุ่ม issue ที่ทำตอนสร้าง Channel ใน LINE ตอนต้นของบทความอ่ะครับ ที่เจมส์ให้จำไว้ในใจ

ใน Live พี่แทนมีเทคนิคแนะนำ คือใส่ .region(‘asia-northeast1’) ต่อท้าย functions เพื่อระบุให้ deploy cloud function ไปที่ asia-northeast1 เพราะมันจะได้คุยที่ Tokyo (asia-northeast1) ให้เรียบร้อยให้หมด

จากนั้นเราจะ deploy ขึ้น firebase cloud function ก่อนอื่นให้ cd กลับไปที่ root project ของเราก่อน

cd ..

จากนั้นให้ใช้คำสั่ง

firebase deploy --only functions
เมื่อ deploy เรียบร้อยจะแสดงดังรูป

เมื่อเรา Deploy เรียบร้อยแล้ว ลองเข้า console firebase เลือกไปที่ Project ของเรา แล้วเลือกไปที่ functions จะเห็น functions ที่เรา deploy ขึ้นมาแล้ว

Deploy เรียบร้อยแล้วจะแสดงดังนี้

ให้เรา Copy Link ของ function lineWebhook ไว้เช่น https://xxxxxxxxx/lineWebhook

จากนั้นเข้าไปที่ Line console แล้วเลือก channel ที่เราสร้างไว้ในตอนต้น เลือกไปที่ Tab Messaging API แล้วเลื่อนลงมาที่ Webhook URL กดที่ปุ่ม Edit และวาง Link ของ function lineWebhook ที่เรา Copy ไว้ลงไป แล้วกด Update

จากนั้นตรง Use webhook ถ้ามันปิดอยู่ ให้กด switch ให้มันเปิดใช้งาน

ถ้าเพื่อน ๆ กดปุ่ม Verify แล้วมันแสดง Error ก็ไม่ต้องตกใจเน้อครับ เพราะในส่วนนี้เราไม่ได้เช็คไว้ เพื่อน ๆ สามารถไปแก้ไขโค้ดให้เช็คว่าถ้า event[0] เป็น undefined ให้ response 200 กลับไปก็ได้โดยไม่ต้องทำอะไร แต่โค้ดในที่นี้ก็ใช้งานได้แล้วครับ

วิธีทดสอบก็คือ ให้เพื่อนแอด Friend ของ Channel ที่เราสร้างขึ้นมา
เมื่อเป็น Friend กับ channel เราแล้ว ก็ให้เราลองสร้าง Group ขึ้นมา แล้ว Invite Channel เราเข้าไป แล้วลองพิมพ์ Text ภาษาไทยไป ตัว Channel เราแปลเป็นภาษาญี่ปุ่นตอบกลับมา

หากบทความนี้มีส่วนไหนผิดพลาดประการใด ก็ขออภัยมา ณ ที่นี้ด้วยเน้อครับ หรือเพื่อน ๆ มีคำถามอะไรสงสัย สามารถพิมพ์ทิ้งไว้ในคอมเม้นท์ได้เลยเน้อครับ หากตอบได้ผมจะเข้ามาตอบ หากตอบไม่ได้ผมจะพยายามไปหาข้อมูลมาตอบให้ครับ ^^

ข้อมูลเพิ่มเติมที่ใช้ในการทำ

--

--

ถึงเขียนน้อย แต่เขียนนะ บทความส่วนใหญ่จะเป็นสิ่งที่ได้เรียนรู้ และลองทำมาครับ ^^