ใน Codelab นี้คุณจะได้เรียนรู้การพัฒนาระบบย่อรูปผ่าน LINE Chatbot ด้วย Firebase Extenstions ผู้ช่วยในการย่อรูป ที่สายย่อต้องชอบ เพราะคุณไม่จำเป็นต้อง Setup ตัว Storage และไม่ต้องเขียนโปรแกรมในส่วนของการย่อรูปเอง


สิ่งที่คุณจะได้เรียนรู้


สิ่งที่คุณต้องเตรียมพร้อมก่อนเริ่ม Codelab

สร้าง LINE Official Account

จุดเริ่มต้นของการพัฒนา LINE Chatbot คือคุณจะต้องสร้าง LINE OA(LINE Official Account) และเปิดใช้งาน Messaging API

  1. เข้าไปที่ https://manager.line.biz แล้วเลือก Log in with LINE account(สีเขียว) เพื่อเข้าสู่ระบบ

  1. เข้าสู่ระบบด้วยบัญชี LINE ของคุณให้เรียบร้อย
  2. กดสร้าง LINE OA จากปุ่ม Create LINE official account สำหรับผู้ทีสร้าง LINE OA ครั้งแรก หรือกด Create new ทางด้านซ้ายสำหรับผู้ที่เคยสร้าง LINE OA แล้ว

  1. ให้ระบุข้อมูลต่างๆลงไปในฟอร์ม แล้วกด ตกลง

  1. จากนั้นให้ยืนยันรายละเอียดในการสร้าง LINE OA เป็นอันเสร็จสิ้น


เปิดใช้งาน Messaging API

หลังจากที่เรามี LINE OA เรียบร้อยแล้ว ขั้นตอนนี้จะพาทุกคนไปเพิ่มความสามารถให้ LINE OA ของเรากลายเป็น LINE Chatbot ได้

  1. เข้าไปที่ https://manager.line.biz ในกรณีที่เรามีบัญชี LINE OA ที่สร้างไว้แล้ว หน้านี้จะแสดงบัญชี LINE OA ต่างๆที่เรามี ก็ให้เรากดเลือกบัญชี LINE OA ที่เราต้องการ

  1. ให้เราไปทีเมนู Settings > Messaging API แล้วให้กดปุ่ม Enable Messaging API

  1. หากเป็นการ Enable Messaging API ครั้งแรกของบัญชี LINE Business ID จะเจอหน้าให้ลงทะเบียน Developer info ก็ให้กรอก ชื่อ และ อีเมล

  1. จากนั้นให้สร้าง Provider ใหม่ หรือเลือก Provider เดิมกรณีที่เคยสร้างไปแล้ว

  1. ระบุ URL ของ Privacy Policy และ Terms of Use (ถ้ามี) หากยังไม่มีก็สามารถกดปุ่ม ok ข้ามไปได้

  1. ยืนยันการเปิดใช้งาน Messaging API ด้วยการกด Ok

  1. เมื่อเจอหน้านี้ ก็แปลว่าคุณได้เปิดใช้งาน Messaging API ให้กับบัญชี LINE OA เรียบร้อยแล้ว

เพิ่ม Chatbot เป็นเพื่อนและตั้งค่า Channel

ขั้นตอนนี้เราจะเข้าไปใช้งาน LINE Developers Console ซึ่งเป็นเว็บไซต์สำหรับการบริหารจัดการ LINE Chatbot(LINE OA ที่เปิดใช้งาน Messaging API แล้ว) ในส่วนของนักพัฒนา

  1. เข้าไปที่ https://developers.line.biz/console/
  2. ให้กดเลือก Provider ที่ต้องการ

  1. เราจะพบกับบัญชี LINE OA ที่เราได้เปิดใช้งาน Messaging API ไว้ ซึ่งในที่นี้เราจะเรียกมันว่า Channel(Channel จะเปรียบเสมือน Chatbot หรือ App) ก็ให้กดเลือก Channel ที่ต้องการ

  1. ให้ไปที่ Tab ชื่อ Messaging API และทำการแสกน QR code ด้วยแอป LINE เพื่อเพิ่ม Chatbot เป็นเพื่อน

  1. ให้ปิด Auto-reply messages เนื่องจากฟีเจอร์นี้จะเป็น default การตอบกลับของ Chatbot ซึ่งไม่จำเป็นต้องใช้ฟีเจอร์นี้

  1. กลับมาที่ Channel ที่เราสร้างใน Tab ชื่อ Messaging API ตรงส่วนของ Channel access token ให้กดปุ่ม Issue

เบื้องหลังของ Chatbot ตัวนี้ เราจะใช้บริการใน Firebase อย่าง Cloud Functions for Firebase, Cloud Storage for Firebase และ Firebase Extensions ดังนั้นขั้นตอนนี้เราจะมาสร้างโปรเจค Firebase เพื่อใช้งานกัน

สร้างโปรเจคใน Firebase

  1. ให้ Sign in ใน Firebase console ด้วย Google account
  2. ในหน้า Firebase console ให้คลิก Add project จากนั้นตั้งชื่อโปรเจคตามต้องการ

  1. เมื่อกด Continue แล้วให้ข้ามการตั้งค่า Google Analytics ไป เพราะคุณจะไม่ได้ใช้มันในโปรเจคนี้


เปลี่ยนแพลนจาก Spark ไปเป็น Blaze (Pay as you go)

เนื่องจาก Cloud Functions for Firebase มีเงื่อนไขว่า หากต้องการไป request ตัว APIs ที่อยู่ภายนอก Google คุณจำเป็นจะต้องใช้ Blaze plan(เราจะต้องไปเรียก Messaging API ของ LINE)


เปิดใช้งาน Cloud Storage for Firebase

  1. ในหน้าโปรเจคที่เราสร้างจะเลือกเมนูชื่อ Storage ที่อยู่ทางซ้ายมือ
  2. ในหน้า Cloud Storage for Firebase ให้คลิก Get started

  1. Popup ของ Default ของ Security Rules จะแสดงขึ้นมา แล้วก็ให้เรากด Next ไป

  1. เลือก Location ของ Cloud Storage ที่ต้องการ แล้วกด Done

การติดตั้ง Firebase CLI

Firebase CLI เป็นเครื่องมือที่จำเป็นสำหรับการ deploy ตัวฟังก์ชันที่เราพัฒนาขึ้น อีกทั้งยังสามารถจำลองการทำงานฟังก์ชัน(Emulate) ภายในเครื่องที่เราพัฒนาอยู่(Locally) ได้

  1. เปิด Terminal ขึ้นมาแล้ว run คำสั่ง
npm install -g firebase-tools
  1. ตรวจสอบว่า Firebase CLI ได้ติดตั้งเรียบร้อยแล้วโดย run คำสั่ง (หากสำเร็จจะเห็นเลขเวอร์ชัน)
firebase --version


Initialize โปรเจค

  1. รันคำสั่งนี้ จากนั้นตัว browser จะเปิดขึ้นมาให้เราเข้าสู้ระบบด้วย Google account เดียวกันกับที่สร้างโปรดจคใน Firebase
firebase login
  1. สร้างโฟลเดอร์เปล่า(ตัวอย่างโฟลเดอร์ชื่อ bot) แล้วให้ shell เข้าไปในนั้น
mkdir bot
cd bot
  1. เมื่อเข้ามาในโฟลเดอร์แล้ว ให้ Initial โปรเจคด้วยคำสั่ง
firebase init functions
  1. เลือก Use an existing project จากนั้นจะเห็นเชื่อโปรเจคที่เราสร้างไว้ ก็กด enter ต่อไป

  1. ถัดไปจะมีตัวเลือกภาษา 2 ตัวคือ JavaScript และ TypeScript โดยตัวอย่างนี้ให้เลือก JavaScript
  2. จากนั้นมันจะถามว่าจะให้ติดตั้ง ESLint ไหม ตรงนี้แนะนำให้ตอบ N ไปก่อน(สำหรับมือใหม่)
  3. สุดท้ายมันจะถามว่าจะให้ติดตั้ง dependencies เลยไหมก็ให้ตอบว่า Y ไป

เพิ่ม Dependencies ที่จำเป็นสำหรับโปรเจคนี้

  1. ในโฟลเดอร์ functions ให้เปิดไฟล์ package.json ขี้นมา
  2. เพิ่ม axios และ cheerio ลงไปใน dependencies
"dependencies": {
  "firebase-admin": "^9.7.0",
  "firebase-functions": "^3.13.2",
  "axios": "^0.21.1",
  "uuid-v4": "^0.1.0"
}
  1. ใน Terminal ให้เรา shell เข้าไปในโฟลเดอร์ชื่อ functions จากนั้น install ตัว dependencies ที่เพิ่มเข้ามาใหม่ด้วยคำสั่ง
npm install
  1. เปิดไฟล์ index.js แล้วเริ่มจากการ import ตัว dependencies ต่างๆเข้ามา
const functions = require('firebase-functions')

// สำหรับการเข้าถึง Cloud Storage
const admin = require("firebase-admin");
admin.initializeApp();

// สำหรับ network requests
const axios = require('axios');

// สำหรับสร้าง public url ใน Cloud Storage
const UUID = require("uuid-v4");

// สำหรับจัดการไฟล์
const path = require("path");
const os = require("os");
const fs = require("fs");


สร้างฟังก์ชันสำหรับรับ Webhook Events จาก LINE

  1. หลังจากเพิ่ม dependencies ในไฟล์ index.js แล้ว ให้ประกาศตัวแปรที่จำเป็นลงไปดังนี้
const LINE_MESSAGING_API = "https://api.line.me/v2/bot";
const LINE_CONTENT_API = "https://api-data.line.me/v2/bot/message";
const LINE_HEADER = {
  "Content-Type": "application/json",
  Authorization: "Bearer xxxxx"
};
  1. ถัดไปให้สร้างฟังก์ชันชื่อ uploadPhoto ในไฟล์ index.js โดยภายในฟังก์ชันให้เขียนเงื่อนไขเพื่อรับ Webhook event จาก LINE ซึ่งเราจะสนใจเฉพาะกรณีที่ผู้ใช้อัพโหลดรูปเข้ามา
exports.uploadPhoto = functions.https.onRequest(async(req, res) => {
  const event = req.body.events[0];
  if (event.type === 'message' && event.message.type === 'image') {
    // เรียกฟังก์ชัน upload เมื่อเข้าเงื่อนไข
    const urls = await upload(event);
    
    // reply ตัว URL ที่ได้กลับไปยังห้องแชท
    await reply(event.replyToken, { type: "text", text: urls });
  }
  return res.end();
});
  1. ที่บรรทัดสุดท้ายของไฟล์ index.js ให้เพิ่มฟังก์ชัน reply() เพื่อส่ง URL ที่ได้จากการอัพโหลดกลับไปยังห้องแชท
const reply = (replyToken, payload) => {
  axios({
    method: "post",
    url: `${LINE_MESSAGING_API}/message/reply`,
    headers: LINE_HEADER,
    data: JSON.stringify({
      replyToken: replyToken,
      messages: [payload]
    })
  })
};
  1. ให้เพิ่มฟังก์ชัน upload() ที่บรรทัดล่างสุดของไฟล์ index.js
const upload = async(event) => {
};
  1. ภายในฟังก์ชัน upload() ให้เพิ่มโค้ดสำหรับการดาวน์โหลด binary ของไฟล์ที่ผู้ใช้ส่งผ่าน LINE เข้ามา
const url = `${LINE_CONTENT_API}/${event.message.id}/content`;
const buffer = await axios({
  method: "get",
  headers: LINE_HEADER,
  url: url,
  responseType: "arraybuffer"
});
  1. ถัดจากโค้ดการดาวน์โหลด binary ให้เพิ่มโค้ดสำหรับสร้างไฟล์ temp ใน local โดยใช้ timestamp ที่ได้จาก Webhook event เป็นชื่อไฟล์
const filename = `${event.timestamp}.jpg`;
const tempLocalFile = path.join(os.tmpdir(), filename);
await fs.writeFileSync(tempLocalFile, buffer.data);
  1. ต่อจากโค้ดด้านบนให้เเพิ่มโค้ดเพื่อ generate ค่า UUID มาเก็บไว้
const uuid = UUID()
  1. ต่อจากโค้ดด้านบน ให้เพิ่มโค้ดเพื่ออัพโหลดไฟล์ขึ้น Cloud Storage for Firebase
const bucket = admin.storage().bucket()
const file = await bucket.upload(tempLocalFile, {
  // กำหนด path ในการเก็บไฟล์แยกเป็นแต่ละ userId
  destination: `photos/${event.source.userId}/${filename}`,
  metadata: {
    cacheControl: 'no-cache',
    metadata: {
      firebaseStorageDownloadTokens: uuid
    }
  }
})
  1. ต่อจากโค้ดด้านบน ให้เพิ่มคำสั่งลบไฟล์ temp เมื่ออัพโหลดเรียบร้อย
fs.unlinkSync(tempLocalFile)
  1. ท้ายสุดในฟังก์ชัน upload() ให้สร้าง download url ขึ้นมา แล้ว return ออกไป
const prefix = `https://firebasestorage.googleapis.com/v0/b/${bucket.name}/o`
const suffix = `alt=media&token=${uuid}`
return `${prefix}/${encodeURIComponent(file[0].name)}?${suffix}`

สร้าง Deploy ฟังก์ชัน

  1. เปิด Terminal ขึ้นมาแล้ว shell ไปที่โฟลเดอร์ functions/ จากนั้นพิมพ์คำสั่งด้านล่างนี้
firebase deploy --only functions
  1. หาก deploy สำเร็จ เราจะเห็นฟังก์ชันของเราแสดงอยู่ที่เมนู Functions ใน Firebase console โดยในหน้านี้จะมี Webhook URL ปรากฎอยู่ที่ column ชื่อ Trigger

เชื่อมต่อ Webhook URL เข้ากับ Messaging API Channel

  1. คัดลอก Webhook URL แล้วไปอัพเดทใน Messaging API Channel ที่สร้างไว้ในขั้นตอนที่ 2(สร้าง Provider และ Channel) พร้อมเปิด toggle ที่ชื่อ Use webhook

  1. ทดสอบส่งรูปผ่าน LINE Chatbot ได้เลย โดยผลลัพธ์ที่ได้ก็จะมีหน้าตาประมาณนี้


และหากเราไปเปิดเมนูชื่อ Storage ใน Firebase console เราก็จะเจอไฟล์ที่อัพโหลดไป

Firebase Extensions เป็นบริการ Plug & Play ที่ช่วยลดเวลาในการพัฒนาฟังก์ชันต่างๆ ที่เรามักจะต้องพัฒนาขึ้นมาใช้เองใน Firebase โดยนักพัฒนาเพียงแค่ติดตั้ง extension ที่ต้องการ ก็จะสามารถใช้งานได้ทันที

  1. เริ่มจากเข้าไปที่ หน้ารวม Extensions แล้วเลือก Resize Images

  1. กดปุ่ม Install in console แล้ว ระบบจะพาไปหน้าที่ให้เลือกโปรเจค ก็ให้เราเลือกโปรเจคเดียวกันกับที่สร้างไว้ในขั้นตอนที่ 3(สร้างและตั้งค่าโปรเจคใน Firebase) จากนั้นจะเข้าสู่หน้ารายละเอียดการติดตั้ง

  1. กด Next ไปเรื่อยๆจนเจอส่วนที่ให้เราตั้งค่า

  1. เมื่อตั้งค่าเรียบร้อยแล้ว ก็กด Install extension ได้เลย
  1. ที่ฟังก์ชัน upload() ในไฟล์ index.js ให้แก้โค้ดเพื่อเปลี่ยนการ return จาก url รูปต้นฉบับ ไปเป็น object ที่ประกอบไปด้วย url ของทั้งรูปต้นฉบับและรูปที่ถูกย่อ
let prefix = `https://firebasestorage.googleapis.com/v0/b/${bucket.name}/o`;
let suffix = `alt=media&token=${uuid}`;
return {
  original: `${prefix}/${encodeURIComponent(file[0].name)}?${suffix}`,
  thumb: `${prefix}/photos${encodeURIComponent(`/${event.source.userId}/thumbs/${event.timestamp}_200x200.jpg`)}?${suffix}`
};
  1. เปลี่ยนการส่ง Text ธรรมดาให้กลายเป็น Flex Message ในฟังก์ชัน uploadPhoto
exports.uploadPhoto = functions.https.onRequest(async (req, res) => {
  const event = req.body.events[0];
  if (event.type === 'message' && event.message.type === 'image') {
    // เรียกฟังก์ชัน upload เมื่อเข้าเงื่อนไข
    const urls = await upload(event);
    
    // reply ตัว URL ที่ได้กลับไปยังห้องแชท
    await reply(
      event.replyToken,
      {
        "type": "flex",
        "altText": "Flex Message",
        "contents": {
          "type": "bubble",
          "hero": {
            "type": "image",
            "url": urls.original,
            "size": "full",
            "aspectRatio": "1:1",
            "aspectMode": "cover"
          },
          "footer": {
            "type": "box",
            "layout": "horizontal",
            "spacing": "md",
            "contents": [
              {
                "type": "button",
                "action": {
                  "type": "uri",
                  "label": "Original",
                  "uri": urls.original
                },
                "style": "secondary"
              },
              {
                "type": "button",
                "action": {
                  "type": "uri",
                  "label": "Thumb",
                  "uri": urls.thumb
                },
                "style": "primary"
              }
            ]
          }
        }
      }
    );
  }
  return res.end();
});
  1. เปิด Terminal ขึ้นมาแล้ว shell ไปที่โฟลเดอร์ functions/ จากนั้นพิมพ์คำสั่งเพื่อ deploy
firebase deploy --only functions
  1. เมื่อ deploy เสร็จแล้ว ก็ลองมาทดสอบส่งรูปผ่าน LINE Chatbot อีกที ผลลัพธ์ที่ได้ก็จะมีหน้าตาประมาณนี้แล้วครับ

ยินดีด้วยครับ ถึงตรงนี้คุณก็มี LINE Chatbot สำหรับการย่อรูปเป็นของคุณเองแล้ว!!!


สิ่งที่คุณได้เรียนรู้ใน Codelab นี้


เรียนรู้เพิ่มเติม

Reference docs


บอกเราหน่อยว่า Codelab ชุดนี้เป็นอย่างไรบ้าง