ใน Codelab นี้คุณจะได้เรียนรู้การสร้าง LINE Chatbot เที่ยวทิพย์ที่ช่วยตัดรูปคนออกจาก background พื้นหลัง พร้อมทั้งนำรูปภาพใหม่เข้ามาแทน Background พื้นหลังเดิมแบบอัตโนมัติ ให้เราสามารถมีรูปไปท่องเที่ยวได้ทั่วโลก

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


สิ่งที่คุณต้องเตรียมพร้อมก่อนเริ่ม 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

เบื้องหลังความสามารถเที่ยวทิพย์ในการตัดภาพ background มาจาก ​​Remove bg ที่เป็นเว็บไซต์ที่ให้บริการตัดฉากหลัง (Background) ออกจากตัวแบบ ซึ่งเราสามารถอัพโหลดรูปบุคคล, สัตว์ หรือสิ่งของก็ได้ ตัวระบบสามารถตัดได้อย่างสวยงาม

  1. ทำการ Sign up ได้ที่นี่โดยจะมีฟรีเครดิตให้สามารถเรียกใช้ API ได้ 50 ครั้งต่อเดือน
  2. เมื่อ Sign up เรียบร้อยแล้วให้ทำการสร้าง API Key เก็บไว้ เพื่อใช้ในการเรียก API


เบื้องหลังของ Chatbot ตัวนี้ เราจะใช้บริการ Cloud Functions for Firebaseในการจัดการ Webhook และฝากรูปทั้งหมดไว้ที่ Cloud Storage for 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

  1. ในหน้าโปรเจค Firebase ไปที่เมนูชื่อ Storage ที่อยู่ทางซ้ายมือและกดปุ่ม 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 removebgbot
cd removebgbot
  1. เมื่อเข้ามาในโฟลเดอร์แล้ว ให้ Initial โปรเจคด้วยคำสั่ง
firebase init functions
  1. เลือก Use an existing project จากนั้นจะเห็นเชื่อโปรเจคที่เราสร้างไว้ ให้เลือก RemoveBg-Chatbot และทำการกด enter

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

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

  1. เปิดไฟล์ package.json ขี้นมา
  2. เพิ่ม dependency ชื่อ axios และ uuid-v4 เข้าไปครับ
"dependencies": {
  "firebase-admin": "^9.9.0",
  "firebase-functions": "^3.14.1",
  "axios": "^0.21.1",
  "uuid-v4": "^0.1.0"
}
  1. ในโฟลเดอร์ที่เราสร้างมา ให้เรา shell เข้าไปต่อในโฟลเดอร์ชื่อ functions จากนั้นสั่ง Install ตัว dependencies ที่เพิ่มเข้ามาใหม่ใน terminal ด้วยคำสั่ง
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");
  1. ในไฟล์ index.js ให้เราสร้างฟังก์ชันใหม่ชื่อ LineWebhook() เพื่อรับ Webhook event ที่ถูกส่งออกมาจากไลน์ ซึ่งเราจะสนใจเฉพาะกรณีที่ผู้ใช้อัพโหลดรูปเข้ามาเท่านั้น
const LINE_MESSAGING_API = "https://api.line.me/v2/bot";
const LINE_CONTENT_API = "https://api-data.line.me/v2/bot/message";
const UNSPLASH_API = "https://source.unsplash.com/1600x900/?";
const REMOVE_BG_API = "https://api.remove.bg/v1.0/removebg";

// เมืองท่องเที่ยวชื่อดังที่เราอยากจะไปเที่ยวทิพย์
const CITIES = ["Paris", "London", "Newyork", "Tokyo", "Osaka", "Seoul", "Taipei", "Bangkok"]

const LINE_HEADER = {
  "Content-Type": "application/json",
  Authorization: "Bearer xxxxx"
};

exports.LineWebhook = functions.https.onRequest(async (req, res) =>{
  const event = req.body.events[0];
  if (event.type === 'message' && event.message.type === 'image') {
     const city = randomItem(CITIES);
     const url = await uploadAndProcessImg(event, city);
     await reply(event.replyToken, city,
        {
           type: "image",
           originalContentUrl: url,
           previewImageUrl: url
        });
  }
  return res.end();
});

function randomItem(items) {
  return items[Math.floor(Math.random() * items.length)];
}
  1. ต่อมาที่ไฟล์เดิม index.js ด้านล่างให้สร้างฟังก์ชัน uploadImage() โดยฟังก์ชันนี้จะทำหน้าที่ในการอัพโหลดรูปไปยัง Cloud Storage for Firebase บริการฝากไฟล์ที่ให้พื้นที่ฟรีๆถึง 5 GB
const uploadImage = async (event, imgLocalFile, filename) => {
  const uuid = UUID();
  const bucket = admin.storage().bucket();
  const file = await bucket.upload(imgLocalFile, {
     // กำหนด path ในการเก็บไฟล์แยกเป็นแต่ละ userId
     destination: `photos/${event.source.userId}/${filename}`,
     metadata: {
        cacheControl: 'no-cache',
        metadata: {
           firebaseStorageDownloadTokens: uuid
        }
     }
  });

const prefix = `https://firebasestorage.googleapis.com/v0/b/${bucket.name}/o`;
  const suffix = `alt=media&token=${uuid}`;
 
  // ส่งคืนค่า public url ของรูปออกไป  
  return `${prefix}/${encodeURIComponent(file[0].name)}?${suffix}`;
};
  1. ถัดไปให้เราสร้างฟังก์ชันชื่อ uploadAndProcessImg() เพื่อใช้ในการดึงรูปของผู้ใช้ที่ส่งมาจาก LINE, เรียก Remove bg และ Unsplash API รวมไปถึงจัดการอัพโหลดรูปไปยัง Cloud Storage ด้วย
const uploadAndProcessImg = async (event, city) => {
    // ดึงรูปของผู้ใช้ที่ส่งมาจาก LINE
    const url = `${LINE_CONTENT_API}/${event.message.id}/content`;
    const originalImage = await axios({
        method: "get",
        headers: LINE_HEADER,
        url: url,
        responseType: "arraybuffer"
    });

    // เซฟรูปผู้ใช้ลงเครื่องและทำการอัพโหลดไว้ที่ Cloud Storage
    const filename = event.timestamp;
    const originalLocalFile = path.join(os.tmpdir(), filename + ".jpg");
    fs.writeFileSync(originalLocalFile, originalImage.data);
    const originalImageUrl = await uploadImage(event, originalLocalFile, filename + ".jpg");

    // ดึงรูปสถานที่เที่ยวจาก Unsplash
    const backgroundImage = await axios({
        method: "get",
        url: UNSPLASH_API + city + ",landscape",
        responseType: "arraybuffer"
    });

    // เซฟรูปสถานที่เที่ยวลงเครื่องและทำการอัพโหลดไว้ที่ Cloud Storage
    const backgroundLocalFile = path.join(os.tmpdir(), filename + "-bg.jpg");
    fs.writeFileSync(backgroundLocalFile, backgroundImage.data);
    const backgroundImageUrl = await uploadImage(event, backgroundLocalFile, filename + "-bg.jpg");

    // เรียกใช้ Remove bg API โดยส่งรูปของผู้ใช้กับสถานที่เที่ยว
    const result = await axios({
        method: "post",
        headers: {
            "accept": "application/json",
            "Content-Type": "application/json",
            "X-API-Key": "your-removebg-api-key"
        },
        url: REMOVE_BG_API,
        data: JSON.stringify({
            size: "preview",
            image_file: "",
            semitransparency: "true",
            position: "original",
            bg_color: "",
            scale: "original",
            image_url: originalImageUrl,
            roi: "0% 0% 100% 100%",
            crop: "false",
            channels: "rgba",
            bg_image_url: backgroundImageUrl,
            format: "auto",
            type: "auto",
            crop_margin: "0",
            add_shadow: "false",
            type_level: 1

        })
    });
    // ได้ผลลัพธ์กลับมาเป็น base64 ต้องสร้าง buffer จาก base64
    const bufferResult = Buffer.from(result.data.data.result_b64, "base64");

    // เซฟรูปที่ได้จาก Remove bg ลงเครื่องและทำการอัพโหลดไว้ที่ Cloud Storage
    const resultLocalFile = path.join(os.tmpdir(), filename + "-result.jpg");
    fs.writeFileSync(resultLocalFile, bufferResult);
    const resultImageUrl = await uploadImage(event, resultLocalFile, filename + "-result.jpg");

    // ลบไฟล์ temp เมื่ออัพโหลดเรียบร้อย
    fs.unlinkSync(originalLocalFile);
    fs.unlinkSync(backgroundLocalFile);
    fs.unlinkSync(resultLocalFile);

    return resultImageUrl;
};
  1. สุดท้ายก็สร้างฟังก์ชัน reply() เพื่อส่งรูปเที่ยวทิพย์กลับไปยังห้องแชท (โค้ดของไฟล์ index.js ทั้งหมดสามารถดูได้ที่นี่)
const reply = async (replyToken, city, payload) => {
  axios({
     method: "post",
     url: `${LINE_MESSAGING_API}/message/reply`,
     headers: LINE_HEADER,
     data: JSON.stringify({
        replyToken: replyToken,
        messages: [{
           type: "text",
           text: "#เที่ยวทิพย์ Let's go to " + city
        }, payload]
     })
  });
};
  1. เมื่อทุกอย่างพร้อมแล้ว ก็ deploy ฟังก์ชันผ่าน terminal ด้วยคำสั่ง
firebase deploy --only functions

  1. ให้เอา URL /LineWebhook ไปใส่ใน LINE Developer Console

ผลลัพธ์

ยินดีด้วย! ถึงตรงนี้คุณก็มี LINE Chatbot เที่ยวทิพย์เป็นของคุณเองแล้ว!!!

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

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

Reference docs

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