ใน Codelab นี้คุณจะได้เรียนรู้การสร้าง LINE Chatbot เที่ยวทิพย์ที่ช่วยตัดรูปคนออกจาก background พื้นหลัง พร้อมทั้งนำรูปภาพใหม่เข้ามาแทน Background พื้นหลังเดิมแบบอัตโนมัติ ให้เราสามารถมีรูปไปท่องเที่ยวได้ทั่วโลก
จุดเริ่มต้นของการพัฒนา LINE Chatbot คือคุณจะต้องสร้าง LINE OA(LINE Official Account) และเปิดใช้งาน Messaging API
หลังจากที่เรามี LINE OA เรียบร้อยแล้ว ขั้นตอนนี้จะพาทุกคนไปเพิ่มความสามารถให้ LINE OA ของเรากลายเป็น LINE Chatbot ได้
ขั้นตอนนี้เราจะเข้าไปใช้งาน LINE Developers Console ซึ่งเป็นเว็บไซต์สำหรับการบริหารจัดการ LINE Chatbot(LINE OA ที่เปิดใช้งาน Messaging API แล้ว) ในส่วนของนักพัฒนา
เบื้องหลังความสามารถเที่ยวทิพย์ในการตัดภาพ background มาจาก Remove bg ที่เป็นเว็บไซต์ที่ให้บริการตัดฉากหลัง (Background) ออกจากตัวแบบ ซึ่งเราสามารถอัพโหลดรูปบุคคล, สัตว์ หรือสิ่งของก็ได้ ตัวระบบสามารถตัดได้อย่างสวยงาม
เบื้องหลังของ Chatbot ตัวนี้ เราจะใช้บริการ Cloud Functions for Firebaseในการจัดการ Webhook และฝากรูปทั้งหมดไว้ที่ Cloud Storage for Firebase
เนื่องจาก Cloud Functions for Firebase มีเงื่อนไขว่า หากต้องการไป request ตัว APIs ที่อยู่ภายนอก Google คุณจำเป็นจะต้องใช้ Blaze plan(เราจะต้องไปเรียก Messaging API ของ LINE)
Firebase CLI เป็นเครื่องมือที่จำเป็นสำหรับการ deploy ตัวฟังก์ชันที่เราพัฒนาขึ้น อีกทั้งยังสามารถจำลองการทำงานฟังก์ชัน(Emulate) ภายในเครื่องที่เราพัฒนาอยู่(Locally) ได้
npm install -g firebase-tools
firebase --version
firebase login
mkdir removebgbot
cd removebgbot
firebase init functions
package.json
ขี้นมา"dependencies": {
"firebase-admin": "^9.9.0",
"firebase-functions": "^3.14.1",
"axios": "^0.21.1",
"uuid-v4": "^0.1.0"
}
functions
จากนั้นสั่ง Install ตัว dependencies ที่เพิ่มเข้ามาใหม่ใน terminal ด้วยคำสั่งnpm install
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");
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)];
}
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}`;
};
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;
};
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]
})
});
};
firebase deploy --only functions
ยินดีด้วย! ถึงตรงนี้คุณก็มี LINE Chatbot เที่ยวทิพย์เป็นของคุณเองแล้ว!!!