ใน Codelab นี้คุณจะได้เรียนรู้การพัฒนาระบบย่อรูปผ่าน LINE Chatbot ด้วย Firebase Extenstions ผู้ช่วยในการย่อรูป ที่สายย่อต้องชอบ เพราะคุณไม่จำเป็นต้อง Setup ตัว Storage และไม่ต้องเขียนโปรแกรมในส่วนของการย่อรูปเอง
จุดเริ่มต้นของการพัฒนา LINE Chatbot คือคุณจะต้องสร้าง LINE OA(LINE Official Account) และเปิดใช้งาน Messaging API
หลังจากที่เรามี LINE OA เรียบร้อยแล้ว ขั้นตอนนี้จะพาทุกคนไปเพิ่มความสามารถให้ LINE OA ของเรากลายเป็น LINE Chatbot ได้
ขั้นตอนนี้เราจะเข้าไปใช้งาน LINE Developers Console ซึ่งเป็นเว็บไซต์สำหรับการบริหารจัดการ LINE Chatbot(LINE OA ที่เปิดใช้งาน Messaging API แล้ว) ในส่วนของนักพัฒนา
เบื้องหลังของ Chatbot ตัวนี้ เราจะใช้บริการใน Firebase อย่าง Cloud Functions for Firebase, Cloud Storage for Firebase และ Firebase Extensions ดังนั้นขั้นตอนนี้เราจะมาสร้างโปรเจค 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 bot
cd bot
firebase init functions
functions
ให้เปิดไฟล์ package.json
ขี้นมาaxios
และ cheerio
ลงไปใน dependencies"dependencies": {
"firebase-admin": "^9.7.0",
"firebase-functions": "^3.13.2",
"axios": "^0.21.1",
"uuid-v4": "^0.1.0"
}
functions
จากนั้น install ตัว dependencies ที่เพิ่มเข้ามาใหม่ด้วยคำสั่ง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");
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"
};
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();
});
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]
})
})
};
upload()
ที่บรรทัดล่างสุดของไฟล์ index.js
const upload = async(event) => {
};
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"
});
const filename = `${event.timestamp}.jpg`;
const tempLocalFile = path.join(os.tmpdir(), filename);
await fs.writeFileSync(tempLocalFile, buffer.data);
const uuid = UUID()
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
}
}
})
fs.unlinkSync(tempLocalFile)
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}`
functions/
จากนั้นพิมพ์คำสั่งด้านล่างนี้firebase deploy --only functions
และหากเราไปเปิดเมนูชื่อ Storage ใน Firebase console เราก็จะเจอไฟล์ที่อัพโหลดไป
Firebase Extensions เป็นบริการ Plug & Play ที่ช่วยลดเวลาในการพัฒนาฟังก์ชันต่างๆ ที่เรามักจะต้องพัฒนาขึ้นมาใช้เองใน Firebase โดยนักพัฒนาเพียงแค่ติดตั้ง extension ที่ต้องการ ก็จะสามารถใช้งานได้ทันที
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}`
};
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();
});
functions/
จากนั้นพิมพ์คำสั่งเพื่อ deployfirebase deploy --only functions
ยินดีด้วยครับ ถึงตรงนี้คุณก็มี LINE Chatbot สำหรับการย่อรูปเป็นของคุณเองแล้ว!!!