From b9184255546f0bae16d23bc98494c70e1d210276 Mon Sep 17 00:00:00 2001 From: sc.surasity@gmail.com Date: Thu, 4 Nov 2021 14:45:29 +0700 Subject: [PATCH] update Code Rewebhook --- config.json | 2 +- index dynamic.js | 326 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ index.js | 366 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------------------------------ services/LineService.js | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------- test.json | 59 ++++++++++++++++++++++++++++++++++------------------------- 5 files changed, 634 insertions(+), 193 deletions(-) create mode 100644 index dynamic.js diff --git a/config.json b/config.json index 5a71e84..5e9e046 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,5 @@ { - "port" : "3000", + "port" : "4000", "channelAccessToken": "be/XHjQ+gMoypZE78Us7hk0h6PA04TyfpQciMOq+B/OVPmumozdhGzYUwopDgsOMCM7RymTK8m++q20GSj3c6B7gZkgEmuGYEYPvc6j+4as6X5bu7tEg+KAZKMfBVDnk+ekpAorC7FMwVPyt2frGRQdB04t89/1O/w1cDnyilFU=", "channelSecret": "df6ab26f85f5130ebf5b546f3a5a4b7f" } \ No newline at end of file diff --git a/index dynamic.js b/index dynamic.js new file mode 100644 index 0000000..4b6c2ce --- /dev/null +++ b/index dynamic.js @@ -0,0 +1,326 @@ +"use strict"; + +const line = require("@line/bot-sdk"); +const express = require("express"); +const config = require("./config.json"); +const bodyParser = require("body-parser"); +const axios = require("axios"); +const fs = require("fs"); +const request = require("request"); +const moment = require("moment"); +const flexMsg = require("./flexMsg"); +const ContentService = require("./services/ContentService"); +const LineService = require("./services/LineService"); + +// create LINE SDK client +const client = new line.Client(config); + +const app = express(); + +const configLine = [ + { + lineOA_name: "น้องปุ้ย OA", + webhook: "/webhook1", + lineOA: { + channelAccessToken: + "be/XHjQ+gMoypZE78Us7hk0h6PA04TyfpQciMOq+B/OVPmumozdhGzYUwopDgsOMCM7RymTK8m++q20GSj3c6B7gZkgEmuGYEYPvc6j+4as6X5bu7tEg+KAZKMfBVDnk+ekpAorC7FMwVPyt2frGRQdB04t89/1O/w1cDnyilFU=", + channelSecret: "df6ab26f85f5130ebf5b546f3a5a4b7f", + }, + }, + { + lineOA_name: "ปุ้ย OA2", + webhook: "/webhook2", + lineOA: { + channelAccessToken: + "NYy1k9OM7e/T7PgabnjafdqcxbRc58m7/K4A3kuNCVkoPxhBoZ/Jvg9gaarsqySG2BtvsoQcVgE4yulPaT1WJ7qTUyBaqegae+r8uh0oWrZO93zXIUILn3bOGUFtLmVFcTuBfRKgFHCaJhi1+wFQ6QdB04t89/1O/w1cDnyilFU=", + channelSecret: "58690bf9c646cb2535894e07c387c0aa", + }, + }, + { + lineOA_name: "น้องปุ้ย OA3", + lineOA: { + channelAccessToken: + "4GuNsc3jKQuohzyD81dWtHu2CB3Lay1WJjfS8ZRvODbTmnK0W9U5Elra2JEFbHeHp+X58qfXlEAMbHLlI9ACklppS06qp3Kd8/y6SyYrQVsyIozyG8Taj/OZtvPrlz9I1NoSnTxRh+4iuntIB9792QdB04t89/1O/w1cDnyilFU=", + channelSecret: "44332b03dee7c5631c7954b2e5e64f84", + }, + webhook: "/webhook3", + }, +]; + +for (let cl of configLine) { + app.post(cl.webhook,line.middleware(cl.lineOA), (req, res) => { + const clientx = new line.Client(cl.lineOA); + if (!Array.isArray(req.body.events)) { + return res.status(500).end(); + } + console.log('to lineOA :',cl.lineOA_name); + console.log('-------------------------------') + console.log("req.body.events !", req.body.events); + + Promise.all( + req.body.events.map((event) => { + console.log("event", event); + // check verify webhook event + if ( + event.replyToken === "00000000000000000000000000000000" || + event.replyToken === "ffffffffffffffffffffffffffffffff" + ) { + return; + } + return replyHookBasic(event,clientx); + }) + ) + .then(() => res.end()) + .catch((err) => { + console.error(err); + res.status(500).end(); + }); + + }); +} + +// webhook callback +let debugMode = false; +app.use("/webhook", line.middleware(config)); +app.post("/webhook", (req, res) => { + // req.body.events should be an array of events + if (!Array.isArray(req.body.events)) { + return res.status(500).end(); + } + console.log("req.body.events !", req.body.events); + // handle events separately + Promise.all( + req.body.events.map((event) => { + console.log("event", event); + // check verify webhook event + if ( + event.replyToken === "00000000000000000000000000000000" || + event.replyToken === "ffffffffffffffffffffffffffffffff" + ) { + return; + } + return handleEvent(event); + }) + ) + .then(() => res.end()) + .catch((err) => { + console.error(err); + res.status(500).end(); + }); +}); + +const handleEvent = (event) => { + let payload = { + type: "text", + text: "Hello From PUI", + }; + + if (event.type == "message" && event.message.type == "text") { + let selecttext = String(event.message.text).toLowerCase(); + + let get_text = ContentService.mockText()[selecttext]; + if (get_text) { + payload = get_text; + } + console.log("event.message.text ::", event.message.text); + if (event.message.text == "แจ้งซ่อม") { + const user_id = event.source.userId; + let sendNotiData = { + body: `ทำรายการแจ้งซ่อม`, + title: "SmartRMS For Condo", + }; + let messx = { + type: "flex", + altText: "GenContentFlex!0", + contents: { + type: "bubble", + body: { + type: "box", + layout: "vertical", + spacing: "sm", + contents: [ + { + type: "text", + text: sendNotiData.body, + weight: "bold", + size: "xl", + wrap: true, + contents: [], + }, + { + type: "box", + layout: "baseline", + contents: [ + { + type: "text", + text: sendNotiData.title, + weight: "bold", + size: "xl", + flex: 0, + wrap: true, + contents: [], + }, + ], + }, + ], + }, + footer: { + type: "box", + layout: "vertical", + spacing: "sm", + contents: [ + { + type: "button", + action: { + type: "uri", + label: "ไปแจ้งซ่อมได้ที่นี่", + uri: `http://localhost:4200/fix-alert/${user_id}`, + }, + style: "primary", + }, + ], + }, + }, + }; + + payload = messx; + } + } else { + payload.text = "Other Message =>>>" + JSON.stringify(event); + } + + console.log("SEND TO ==> " + JSON.stringify(payload)); + + return client.replyMessage(event.replyToken, payload); +}; + +app.use(bodyParser.json()); +app.get("/", (req, res) => { + res.json({ line: "ok" }); +}); + +app.post("/push", (req, res) => { + let body = req.body; + let { user_id, message } = body; + console.log("push =>> body ::", body); + if (!message) { + message = { + type: "text", + text: `Push Message! to ${user_id}`, + }; + } + client.pushMessage(user_id, message); + res.json(message); +}); + +app.post("/multicast", (req, res) => { + let body = req.body; + let { user_ids } = body; + console.log("body ::", body); + let message = [ + { + type: "text", + text: `use multicast Message1! to ${JSON.stringify(user_ids)}`, + }, + { + type: "text", + text: `use multicast Message2! to ${JSON.stringify(user_ids)}`, + }, + ]; + + client.multicast(user_ids, message); + res.json(message); +}); + +app.post("/multicast", (req, res) => { + let body = req.body; + let { user_ids } = body; + console.log("body ::", body); + let message = [ + { + type: "text", + text: `use multicast Message1! to ${JSON.stringify(user_ids)}`, + }, + { + type: "text", + text: `use multicast Message2! to ${JSON.stringify(user_ids)}`, + }, + ]; + + client.multicast(user_ids, message); + res.json(message); +}); + +app.post("/broadcast", async (req, res) => { + let body = req.body; + let { messages } = body; + console.log("body ::", body); + + let resx = await LineService.Message.Broadcast(messages); + console.log("resx.data", resx.data); + + res.json({ message: "OK" }); +}); + +app.post("/save", bodyParser.json(), async (req, res) => { + console.log("saveFile!"); + try { + const downloadFile = function (uri, filename, callback) { + request.head(uri, function (err, res, body) { + console.log("content-type:", res.headers["content-type"]); + console.log("content-length:", res.headers["content-length"]); + console.log("Content-Type:", res.headers["Content-Type"]); + console.log("res.headers ::", res.headers); + + request(uri, { + headers: { + Authorization: + "Bearer be/XHjQ+gMoypZE78Us7hk0h6PA04TyfpQciMOq+B/OVPmumozdhGzYUwopDgsOMCM7RymTK8m++q20GSj3c6B7gZkgEmuGYEYPvc6j+4as6X5bu7tEg+KAZKMfBVDnk+ekpAorC7FMwVPyt2frGRQdB04t89/1O/w1cDnyilFU=", + }, + }) + .pipe(fs.createWriteStream(filename)) + .on("close", callback); + }); + }; + + let unquie_file = moment().format("YYYY-MM-DD_HHmmssss"); + let file_name = `filesave_${unquie_file}`; + let message_id = req.body.message_id; + let URI = `https://api-data.line.me/v2/bot/message/${message_id}/content`; + console.log("message_id ::", message_id); + console.log("file_name ::", file_name); + + axios + .get(URI, { + headers: { + Authorization: + "Bearer be/XHjQ+gMoypZE78Us7hk0h6PA04TyfpQciMOq+B/OVPmumozdhGzYUwopDgsOMCM7RymTK8m++q20GSj3c6B7gZkgEmuGYEYPvc6j+4as6X5bu7tEg+KAZKMfBVDnk+ekpAorC7FMwVPyt2frGRQdB04t89/1O/w1cDnyilFU=", + }, + }) + .then(function (response) { + // handle success + console.log("axios =>>", response.headers["content-type"]); + let sp = response.headers["content-type"].split("/"); + let type = sp[sp.length - 1]; + let full_file_name = file_name + "." + type; + console.log("full_file_name =", full_file_name); + downloadFile(URI, full_file_name, function () { + console.log("done!"); + res.json({ LoadFlieName: "Success" }); + }); + }) + .catch(function (error) { + // handle error + console.log(error); + console.error("errorxx ::", error); + res.json({ error: "error" }); + }); + } catch (error) { + console.error("errorxx ::", error); + } +}); + +const port = config.port; +app.listen(port, () => { + console.log(`listening on ${port}`); +}); diff --git a/index.js b/index.js index 30d9a78..a7e1bfa 100644 --- a/index.js +++ b/index.js @@ -1,32 +1,33 @@ -"use strict"; - -const line = require("@line/bot-sdk"); +const https = require("https"); const express = require("express"); -const config = require("./config.json"); -const bodyParser = require("body-parser"); -const axios = require("axios"); -const fs = require("fs"); -const request = require("request"); -const moment = require("moment"); +const app = express(); +const PORT = process.env.PORT || 3000; +const axios = require("axios").default; +const TOKEN = + "NYy1k9OM7e/T7PgabnjafdqcxbRc58m7/K4A3kuNCVkoPxhBoZ/Jvg9gaarsqySG2BtvsoQcVgE4yulPaT1WJ7qTUyBaqegae+r8uh0oWrZO93zXIUILn3bOGUFtLmVFcTuBfRKgFHCaJhi1+wFQ6QdB04t89/1O/w1cDnyilFU="; + +app.use(express.json()); +app.use( + express.urlencoded({ + extended: true, + }) +); + const flexMsg = require("./flexMsg"); -const ContentService = require("./services/ContentService"); -const LineService = require("./services/LineService"); -// create LINE SDK client -const client = new line.Client(config); +app.get("/", (req, res) => { + res.sendStatus(200); +}); + +app.post("/Rewebhook", async (req, res) => { + console.log("Rewebhook ::", req.body); -const app = express(); + res.json({ OK: req.body }); -// webhook callback -let debugMode = false; -app.use("/webhook", line.middleware(config)); -app.post("/webhook", (req, res) => { - // req.body.events should be an array of events if (!Array.isArray(req.body.events)) { return res.status(500).end(); } - console.log('req.body.events !',req.body.events) - // handle events separately + Promise.all( req.body.events.map((event) => { console.log("event", event); @@ -37,7 +38,7 @@ app.post("/webhook", (req, res) => { ) { return; } - return handleEvent(event); + return handleEvent(event, req.body); }) ) .then(() => res.end()) @@ -47,154 +48,219 @@ app.post("/webhook", (req, res) => { }); }); -const handleEvent = (event) => { - let payload = { - type: "text", - text: "Hello From PUI", +const handleEvent = async (event, body) => { + const { Channel_access_token } = body; + const { replyToken } = event; + const _headers = { + Authorization: "Bearer " + Channel_access_token, + "Content-Type": "application/json", }; + const messages = [ + { + type: "text", + text: "Hello, user", + }, + { + type: "text", + text: "May I help you?", + }, + ]; + + let template_text = null; - if (event.type == "message" && event.message.type == "text") { - let selecttext = String(event.message.text).toLowerCase(); - let get_text = ContentService.mockText()[selecttext]; + if (event.message.type == "text") { + let selecttext = tolow(event.message.text); + let get_text = mockText()[selecttext]; if (get_text) { - payload = get_text; + template_text = get_text; } } else { - payload.text = "Other Message =>>>" + JSON.stringify(event); + template_text.text = "Other Message =>>>" + JSON.stringify(event); } - console.log("SEND TO ==> " + JSON.stringify(payload)); + console.log("template_text", template_text); - return client.replyMessage(event.replyToken, payload); + let URL = "https://api.line.me/v2/bot/message/reply"; + let payload = { + replyToken: replyToken, + messages: template_text ? [template_text] : messages, + }; + console.log("payload ::", payload); + try { + let res = await axios.post(URL, payload, { + headers: _headers, + }); + console.log("res ::", res.data); + } catch (e) { + console.log("Error Reply::", e); + } }; -app.use(bodyParser.json()); -app.get("/", (req, res) => { - res.json({ line: "ok" }); -}); +const tolow = (str) => { + return str.toLowerCase(); +}; -app.post("/push", (req, res) => { - let body = req.body; - let { user_id, message } = body; - console.log("push =>> body ::", body); - if (!message) { - message = { - type: "text", - text: `Push Message! to ${user_id}`, +app.post("/webhook", function (req, res) { + res.send("HTTP POST request sent to the webhook URL!"); + // If the user sends a message to your bot, send a reply message + console.log("req.body.events ::", req.body.events); + + if (req.body.events[0].type === "message") { + // Message data, must be stringified + const dataString = JSON.stringify({ + replyToken: req.body.events[0].replyToken, + messages: [ + { + type: "text", + text: "Hello, user", + }, + { + type: "text", + text: "May I help you?", + }, + ], + }); + + // Request header + const headers = { + "Content-Type": "application/json", + Authorization: "Bearer " + TOKEN, }; - } - client.pushMessage(user_id, message); - res.json(message); -}); -app.post("/multicast", (req, res) => { - let body = req.body; - let { user_ids } = body; - console.log("body ::", body); - let message = [ - { - type: "text", - text: `use multicast Message1! to ${JSON.stringify(user_ids)}`, - }, - { - type: "text", - text: `use multicast Message2! to ${JSON.stringify(user_ids)}`, - }, - ]; + // Options to pass into the request + const webhookOptions = { + hostname: "api.line.me", + path: "/v2/bot/message/reply", + method: "POST", + headers: headers, + body: dataString, + }; - client.multicast(user_ids, message); - res.json(message); -}); + // Define request + const request = https.request(webhookOptions, (res) => { + res.on("data", (d) => { + process.stdout.write(d); + }); + }); -app.post("/multicast", (req, res) => { - let body = req.body; - let { user_ids } = body; - console.log("body ::", body); - let message = [ - { - type: "text", - text: `use multicast Message1! to ${JSON.stringify(user_ids)}`, - }, - { - type: "text", - text: `use multicast Message2! to ${JSON.stringify(user_ids)}`, - }, - ]; + // Handle error + request.on("error", (err) => { + console.error(err); + }); - client.multicast(user_ids, message); - res.json(message); + // Send data + request.write(dataString); + request.end(); + } }); -app.post("/broadcast", async (req, res) => { - let body = req.body; - let { messages } = body; - console.log("body ::", body); +app.listen(PORT, () => { + console.log(`Example app listening at http://localhost:${PORT}`); +}); - let resx = await LineService.Broadcast(messages); - console.log("resx", resx); +const flexs = flexMsg.flexs; - res.json({ message: "OK" }); -}); +const mockText = () => { + return { + flex0: flexs.flex0, + flex1: flexs.flex1, -app.post("/save", bodyParser.json(), async (req, res) => { - console.log("saveFile!"); - try { - const downloadFile = function (uri, filename, callback) { - request.head(uri, function (err, res, body) { - console.log("content-type:", res.headers["content-type"]); - console.log("content-length:", res.headers["content-length"]); - console.log("Content-Type:", res.headers["Content-Type"]); - console.log("res.headers ::", res.headers); - - request(uri, { - headers: { - Authorization: - "Bearer be/XHjQ+gMoypZE78Us7hk0h6PA04TyfpQciMOq+B/OVPmumozdhGzYUwopDgsOMCM7RymTK8m++q20GSj3c6B7gZkgEmuGYEYPvc6j+4as6X5bu7tEg+KAZKMfBVDnk+ekpAorC7FMwVPyt2frGRQdB04t89/1O/w1cDnyilFU=", + bub1: flexs.bub1, + bub2: flexs.bub2, + bub3: flexs.bub3, + bub4: flexs.bub4, + bub5: flexs.bub5, + bub6: flexs.bub6, + confirm: { + type: "template", + altText: "this is a confirm template", + template: { + type: "confirm", + text: "Are you sure?", + actions: [ + { + type: "message", + label: "Yes", + text: "yes", }, - }) - .pipe(fs.createWriteStream(filename)) - .on("close", callback); - }); - }; - - let unquie_file = moment().format("YYYY-MM-DD_HHmmssss"); - let file_name = `filesave_${unquie_file}`; - let message_id = req.body.message_id; - let URI = `https://api-data.line.me/v2/bot/message/${message_id}/content`; - console.log("message_id ::", message_id); - console.log("file_name ::", file_name); - - axios - .get(URI, { - headers: { - Authorization: - "Bearer be/XHjQ+gMoypZE78Us7hk0h6PA04TyfpQciMOq+B/OVPmumozdhGzYUwopDgsOMCM7RymTK8m++q20GSj3c6B7gZkgEmuGYEYPvc6j+4as6X5bu7tEg+KAZKMfBVDnk+ekpAorC7FMwVPyt2frGRQdB04t89/1O/w1cDnyilFU=", - }, - }) - .then(function (response) { - // handle success - console.log("axios =>>", response.headers["content-type"]); - let sp = response.headers["content-type"].split("/"); - let type = sp[sp.length - 1]; - let full_file_name = file_name + "." + type; - console.log("full_file_name =", full_file_name); - downloadFile(URI, full_file_name, function () { - console.log("done!"); - res.json({ LoadFlieName: "Success" }); - }); - }) - .catch(function (error) { - // handle error - console.log(error); - console.error("errorxx ::", error); - res.json({ error: "error" }); - }); - } catch (error) { - console.error("errorxx ::", error); - } -}); + { + type: "message", + label: "No", + text: "no", + }, + ], + }, + }, + location: { + type: "text", // ① + text: "Select your favorite food category or send me your location!", + quickReply: { + // ② + items: [ + { + type: "action", // ③ + imageUrl: "https://example.com/sushi.png", + action: { + type: "message", + label: "Sushi", + text: "Sushi", + }, + }, + { + type: "action", + imageUrl: "https://example.com/tempura.png", + action: { + type: "message", + label: "Tempura", + text: "Tempura", + }, + }, + { + type: "action", // ④ + action: { + type: "location", + label: "Send location", + }, + }, + ], + }, + }, -const port = config.port; -app.listen(port, () => { - console.log(`listening on ${port}`); -}); + image_carousel: { + type: "template", + altText: "this is a image carousel template", + template: { + type: "image_carousel", + columns: [ + { + imageUrl: + "https://image.sistacafe.com/images/uploads/summary/image/26097/cat-200313-020.jpg", + action: { + type: "postback", + label: "Buy", + data: "action=buy&itemid=111", + }, + }, + { + imageUrl: + "https://image.sistacafe.com/images/uploads/summary/image/26097/cat-200313-020.jpg", + action: { + type: "message", + label: "Yes", + text: "yes", + }, + }, + { + imageUrl: + "https://image.sistacafe.com/images/uploads/summary/image/26097/cat-200313-020.jpg", + action: { + type: "uri", + label: "View detail", + uri: "http://example.com/page/222", + }, + }, + ], + }, + }, + }; +}; diff --git a/services/LineService.js b/services/LineService.js index e1c2545..a59f37a 100644 --- a/services/LineService.js +++ b/services/LineService.js @@ -6,23 +6,63 @@ const headers = { }; const LineService = { - Broadcast: async (messages) => { - console.log(""); - return new Promise(async (resolve, reject) => { - let URL = "https://api.line.me/v2/bot/message/broadcast"; - let payload = { messages: messages }; - try { - let res = await axios.post(URL, payload, { - headers: headers, - }); - // console.log("res ::", res); - resolve(res); - } catch (e) { - console.log("bbb!"); - console.log("Error ::", e); - reject(null); - } - }); + Message: { + Broadcast: async (messages) => { + console.log(""); + return new Promise(async (resolve, reject) => { + let URL = "https://api.line.me/v2/bot/message/broadcast"; + let payload = { messages: messages }; + try { + let res = await axios.post(URL, payload, { + headers: headers, + }); + // console.log("res ::", res); + resolve(res); + } catch (e) { + console.log("bbb!"); + console.log("Error ::", e); + reject(null); + } + }); + }, + Narrowcast: async (messages) => { + console.log(""); + return new Promise(async (resolve, reject) => { + let URL = "https://api.line.me/v2/bot/message/narrowcast"; + let payload = { messages: messages }; + try { + let res = await axios.post(URL, payload, { + headers: headers, + }); + // console.log("res ::", res); + resolve(res); + } catch (e) { + console.log("bbb!"); + console.log("Error ::", e); + reject(null); + } + }); + }, + }, + ManagingAudience: { + createAudience: async (messages) => { + console.log(""); + return new Promise(async (resolve, reject) => { + let URL = "https://api.line.me/v2/bot/audienceGroup/upload"; + let payload = { messages: messages }; + try { + let res = await axios.post(URL, payload, { + headers: headers, + }); + // console.log("res ::", res); + resolve(res); + } catch (e) { + console.log("bbb!"); + console.log("Error ::", e); + reject(null); + } + }); + } }, }; diff --git a/test.json b/test.json index b3fea6d..be30812 100644 --- a/test.json +++ b/test.json @@ -3,49 +3,58 @@ "altText": "GenContentFlex!0", "contents": { "type": "bubble", - "direction": "ltr", - "header": { - "type": "box", - "layout": "vertical", - "contents": [ - { - "type": "text", - "text": "Header", - "align": "center", - "contents": [] - } - ] - }, - "hero": { - "type": "image", - "url": "https://1417094351.rsc.cdn77.org/articles/1439/1438984/thumbnail/small.gif?3", - "size": "full", - "aspectRatio": "1.51:1", - "aspectMode": "fit" - }, "body": { "type": "box", "layout": "vertical", + "spacing": "sm", "contents": [ { "type": "text", - "text": "Body", - "align": "center", + "text": "Arm Chair, White", + "weight": "bold", + "size": "xl", + "wrap": true, "contents": [] + }, + { + "type": "box", + "layout": "baseline", + "contents": [ + { + "type": "text", + "text": "$49", + "weight": "bold", + "size": "xl", + "flex": 0, + "wrap": true, + "contents": [] + }, + { + "type": "text", + "text": ".99", + "weight": "bold", + "size": "sm", + "flex": 0, + "wrap": true, + "contents": [] + } + ] } ] }, "footer": { "type": "box", - "layout": "horizontal", + "layout": "vertical", + "spacing": "sm", "contents": [ { "type": "button", "action": { "type": "uri", - "label": "Button", + "label": "Add to Cart", "uri": "https://linecorp.com" - } + }, + "style": "primary" } ] } -- libgit2 0.21.2