Skip to main content

มาลองเล่น LIFF และ Messaging API กัน

[ สามารถดู video ของหัวข้อนี้ก่อนได้ ดู video

** บทความนี้เดี๋ยวอาจจะกลับมาเรียบเรียงใหม่ เนื่องจากเป็นบทความที่ใช้ short note ร่วมกับคลิปดังกล่าว หากอ่านแล้วงงอย่างไร ลองดูจากคลิปกันก่อนได้นะครับ 🙏

รู้จักกันก่อนว่า LIFF คืออะไร ?

LIFF (LINE Frontend-end Framework) คือ WebView ที่สามารถรันเว็บไซต์อยู่ภายในแอป LINE ได้โดยตรง

  • มีความสามารถในการทำงานร่วมกับ LINE Login เพื่อทำการทำ Authentication คู่่กันกับ LINE ได้ = LINE auto login
  • สามารถเข้าถึง User's Profile (บางส่วน) ของ LINE ได้ เช่น ชื่อ, ภาพ
  • เนื่องจากทำงานอยู่บน LINE = สามารถใช้งานร่วมกับ Chatbot ของ LINE ได้ (เดี๋ยวเราจะมาดูกันว่า ปัจจุบัน developer ทำอะไรกัน LINE แล้วได้บ้าง)

จุดเด่นที่ LIFF พยายามนำเสนอ

  • ใช้งานร่วมกับ LINE = ไม่ต้องให้ลูกค้าจำ Login ตอนใช้งาน
  • ใช้งานร่วมกับ LINE OA ที่มีอยู่แล้ว ทำให้ลูกค้ารู้สึกมีประสบการณ์ใช้งานเหมือนกับ application ของร้านค้าเอง
  • ถ้ามี website อยู่แล้ว สามารถนำมาพัฒนาต่อยอดแล้วนำมาขึ้นบน LIFF ได้เลย

ปัจจุบัน Developer ทำอะไรกับ LINE ได้บ้าง

Ref: https://developers.line.biz/en/docs/

ปัจจุบันนั้น LINE มี service ที่ให้ developer มามีส่วนร่วมหลากหลาย service

เราจะเล่าเฉพาะ service ที่เกี่ยวข้องในวันนี้คือ

  • Messaging API = เกี่ยวข้องกับการทำ chatbot บน LINE
  • LINE Login = เกี่ยวข้องกับการทำเชื่อมต่อกับ LINE Account
  • LIFF (LINE Frontend-end Framework) = Webview ที่สามารถเชื่อมต่อกับ Account ของ LINE ได้

ซึ่งทั้ง 3 service นี้สามารถสื่อสารผ่านตัวกลางหนึ่งที่ชื่อ userId ได้

  • userId คือ unique key ที่เป็นตัวแทนของ user ในการสื่อสารกับ service provider ที่เราสร้างเป็น channel (ช่องทาง) ขึ้นมา
  • ทุก service ที่อยู่ภายใต้ provider เดียวกัน จะใช้ userId (หรือ uid) เดียวกัน ในการสื่อสาร เช่น
    • ถ้า LIFF service ได้ userID นี้มา
    • จะเท่ากับ หากเราส่ง Messaging API เข้า userID เดียวกันก็จะสามารถส่งข้อความไปยังคนเดียวกันได้

เดี๋ยวเราจะลองมาเล่นๆไปพร้อมๆกันใน Session นี้

เราจะมา code อะไรกันบ้าง

Session นี้สิ่งที่เราจะทำคือ

  1. เพิ่มหน้าเว็บสำหรับ LIFF และ LINE Login เข้ามา
  2. เมื่อนำ LIFF ไปเปิดบน LINE (หรือ LINE Login) จะได้ userId มา เราจะนำ userId นั้นมาเล่นกับ 2 feature ของ Messaging API คือ LINE Message กับ Rich menu กัน
  3. เราจะลองรับข้อมูลจาก webhook (ที่ได้ userId) มาเหมือนกัน แล้วส่งข้อความกลับไปยัง LINE ของคนๆนั้น

เพื่อมา confirm และผู้ผลลัพธ์ไปพร้อมกันว่า userID ตรงกันหรือไม่

Strucuture project

├── index.html --> สำหรับหน้าเว็บ LIFF
├── package.json
└── server.js --> nodejs (express)

Step แรก: สร้าง channel สำหรับการทำ LIFF และ Messaging API โดย

  1. เข้า https://developers.line.biz/ > Login LINE

  2. กดสร้างตรง Provider

  3. เสร็จแล้วเมื่อได้ provider มาให้สร้าง channel

  4. กดสร้าง LINE Login และ Messaiging API มาคู่กัน

index.html

Note

  • เพิ่ม liff login (โดยผูกกับ channel ที่สร้างไว้)
  • ทดสอบดึง profile โดยเมื่อดึง profile มาได้ = ให้แสดงออกมาที่หน้าเว็บ (แสดงภาพ, uid ออกมา)
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.message {
padding: 10px;
background: #ddd;
}
</style>
</head>
<body>
<div id="lineprofile" style="display: none;">
<img width="100px" id="profileImage" src="" />
<div>
Hello <b><span id="profileName">Name</span></b>
</div>
<div>UID <span id="profileUID">UID</span></div>
</div>
<script src="https://static.line-scdn.net/liff/edge/2/sdk.js"></script>
<script>
let userId = "";

const logOut = () => {
liff.logout();
window.location.reload();
};
const main = async () => {
await liff.init({ liffId: "2000719050-rGVOBePm" });
if (liff.isLoggedIn()) {
const profile = await liff.getProfile();

// update profile
profileImage.src = profile.pictureUrl;
profileName.innerText = profile.displayName;
profileUID.innerText = profile.userId;

// collect userId
userId = profile.userId;

// show profile
lineprofile.style.display = "block";
} else {
liff.login();
}
};
main();
</script>
</body>
</html>

ผลลัพธ์

server.js

Ref: https://developers.line.biz/en/reference/messaging-api/

Note

  • เพิิ่ม API สำหรับการส่ง LINE Message (เข้า Messaging API) ผ่าน userId (ที่ได้จาก LIFF)
const express = require("express");
const axios = require("axios");
const cors = require("cors");

require("dotenv").config();

const app = express();
app.use(cors());
const port = 8888;

const LINE_API_URL = "https://api.line.me/v2/bot/message/push";

app.use(express.json());

const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${LINE_ACCESS_TOKEN}`,
};

const sendMessage = async (userUid, message) => {
const body = {
to: userUid,
messages: [
{
type: "text",
text: message,
},
],
};
const response = await axios.post(LINE_API_URL, body, { headers });
return response;
};

app.post("/send-message", async (req, res) => {
const { userUid, message } = req.body;

try {
const response = await sendMessage(userUid, message);
console.log("=== LINE log", response.data);
res.json({
message: "Message OK",
});
} catch (error) {
console.log("error", error.response.data);
res.status(400).json({
error: error.response,
});
}
});

app.listen(port, async () => {
console.log(`Express app listening at http://localhost:${port}`);
});

ลองเพิ่มตัวส่งจาก html

<!-- เหมือนเดิม -->
<body>
<div id="lineprofile" style="display: none;">
<img width="100px" id="profileImage" src="" />
<div>
Hello <b><span id="profileName">Name</span></b>
</div>
<div>UID <span id="profileUID">UID</span></div>

<div class="message">
<div>Send message</div>
<input id="message" />
<button onclick="sendMessage()">Send</button>
</div>
<button onclick="logOut()">Logout</button>
</div>
<script src="https://static.line-scdn.net/liff/edge/2/sdk.js"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.6.1/axios.min.js"
integrity="sha512-m9S8W3a9hhBHPFAbEIaG7J9P92dzcAWwM42VvJp5n1/M599ldK6Z2st2SfJGsX0QR4LfCVr681vyU5vW8d218w=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
<script>
let userId = "";

const main = async () => {
await liff.init({ liffId: "2000719050-rGVOBePm" });
if (liff.isLoggedIn()) {
const profile = await liff.getProfile();

// update profile
profileImage.src = profile.pictureUrl;
profileName.innerText = profile.displayName;
profileUID.innerText = profile.userId;

// collect userId
userId = profile.userId;

// show profile
lineprofile.style.display = "block";
} else {
liff.login();
}
};

// เพิิ่ม sendMessage มายิงไปยังฝั่ง Backend
const sendMessage = async () => {
try {
if (!message.value) {
alert("message not found");
return false;
}

const data = {
userUid: userId,
message: message.value,
};
console.log("data", data);
const response = await axios.post("http://localhost:8888/send-message", data);
console.log("response", response.data);
} catch (error) {
console.log("error", error.message);
}
};
main();
</script>
</body>

Rich menu API

Ref: https://developers.line.biz/en/docs/messaging-api/switch-rich-menus/#richmenu-switch-07

Note

  • เราจะลองมาใช้ userID update richmenu กันบ้าง โดยยิงผ่าน Messaging API เพื่อไป update rich menu
  1. create rich menu
#!/bin/bash

curl -v -X POST https://api.line.me/v2/bot/richmenu \
-H 'Authorization: Bearer <access token>' \
-H 'Content-Type: application/json' \
-d \
'{
"size": {
"width": 800,
"height": 540
},
"selected": false,
"name": "Default rich",
"chatBarText": "Tap to open",
"areas": [
{
"bounds": {
"x": 0,
"y": 0,
"width": 800,
"height": 540
},
"action": {
"type": "richmenuswitch",
"richMenuAliasId": "al-menu2",
"data": "richmenu=al-menu2"
}
}
]
}'
  1. upload rich menu
#!/bin/bash

curl -X POST https://api-data.line.me/v2/bot/richmenu/richmenu-9f201bd36e1c32db506ad8c043583cbc/content \
-H "Authorization: Bearer <access token>" \
-H "Content-Type: image/png" \
-T ./images/800x540.png

เท่ากับเป็นการสร้าง rich menu เรียบร้อย

  1. set default rich menu
#!/bin/bash

curl -X POST 'https://api.line.me/v2/bot/user/all/richmenu/richmenu-00f38a8b2edf11419598cdddf9323cdd' -H 'Authorization: Bearer <access token>'
  1. set link rich menu (กรณีที่อยาก link richmenu ไปมา)
#!/bin/bash

curl -X POST https://api.line.me/v2/bot/richmenu/alias \
-H 'Authorization: Bearer <access token>' \
-H 'Content-Type: application/json' \
-d \
'{
"richMenuAliasId": "main-1",
"richMenuId": "richmenu-9f201bd36e1c32db506ad8c043583cbc"
}'
  1. force update rich menu from webhook (รับ userId จาก webhook และ force ยิงเปลี่ยน rich menu จากข้อความ)

ที่ server.js

  • เพิ่มคำสั่ง จัดการกับ rich menu ผ่าน webhook
const defaultRichmenu = async () => {
try {
const response = await axios.get(`${LINT_BOT_API_URL}/all/richmenu`, { headers });
return response;
} catch (error) {}
};

app.post("/webhook", async (req, res) => {
const { events } = req.body;

if (!events || events.length <= 0) {
console.log("error event not found");
res.json({
message: "event not found !",
});
return false;
}

try {
const lineEvent = events[0];
const lineUserID = lineEvent.source.userId;
let commandMessage = "";

if (lineEvent.type === "message") {
if (lineEvent.message.text === "อยากกลับบ้าน") {
commandMessage = "back";
}
}

// update richmenu ด้วย userId
if (commandMessage === "back") {
const richmenuResponse = await defaultRichmenu();
const response = await updateRichmenu(lineUserID, richmenuResponse.data.richMenuId);
console.log("=== LINE log", response.data);
}
} catch (error) {
console.log("error", error);
}
});

Flex message

  1. ลองส่งเป็น Flex message
  • ที่ server.js
// send Flex Message
const sendFlexMessage = async (userUid, message) => {
try {
let contents = Object.assign({}, templateJSON);
contents.body.contents[0].text = message;

const body = {
to: userUid,
messages: [
{
type: "flex",
altText: "this is a flex message",
contents,
},
],
};
console.log("body", JSON.stringify(body));
const response = await axios.post(LINE_API_URL, body, { headers });
return response;
} catch (error) {
// console.log(error.response)
throw new Error(error.message);
}
};

app.post("/send-message", async (req, res) => {
const { userUid, message } = req.body;

try {
// เปลี่ยนมายิงผ่าน flex message แทน
const response = await sendFlexMessage(userUid, message);
console.log("=== LINE log", response.data);
res.json({
message: "Message OK",
});
} catch (error) {
console.log("error", error.response.data);
res.status(400).json({
error: error.response,
});
}
});
  1. ลองนำ LIFF ขึ้นไปเล่นจริงบน LINE

อื่นๆเพิ่มเติม

LON (LINE Official Notification)

LINE shopping API (เชื่อม UID ไม่ได้)

Reference

Richmenu API

Github

https://github.com/mikelopster/liff-experiment