質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
LINE Messaging API

LINE Messaging APIは、メッセージの送信・返信ができるAPIです。Web APIを経由しアプリケーションサーバとLINEのAPIでやり取りが可能。複数のメッセージタイプや分かりやすいAPIリファレンスを持ち、グループチャットにも対応しています。

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

2429閲覧

【GAS】linebotでカルーセルを使用したい

SN____R

総合スコア8

LINE Messaging API

LINE Messaging APIは、メッセージの送信・返信ができるAPIです。Web APIを経由しアプリケーションサーバとLINEのAPIでやり取りが可能。複数のメッセージタイプや分かりやすいAPIリファレンスを持ち、グループチャットにも対応しています。

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2021/07/12 22:13

編集2021/07/14 03:09

前書き

初学者です。似たような質問を複数回投稿しており、気を悪くされた方がいらっしゃいましたら心よりお詫び申し上げます。

本題

さて、本題なのですが、lineで使用するためのbotを現在作成しております。
botの内容としては、if文にて特定の文字を入力すると特定の文字列が返され、そこに数値を入力すると計算結果が返ってくるというものになります。
実際に完成し、問題なく動作はしているのですが、最初の特定の文字を入力するという部分が、特定の文字を覚えないといけないということが煩わしく、その他という文字を入力するとカルーセルテンプレートを表示させ、入力できないかと考えました
しかし、カルーセルテンプレートを呼び出すコードと元のコードを上手く合わせることが出来ず、文字を認識してくれなくなったり、関数が長くなりすぎることで動作しなくなってしまったりという現象が起こってしまいました。
うまく動作させるためにはどのようにして組み合わせればいいのか、ご教授のほどよろしくお願いいたします。

現在のbotのソースコード

Javascript

1var ACCESS_TOKEN = "XXXXXXXXXXX"; 2var URL = "https://api.line.me/v2/bot/message/reply"; 3var SS_ID = "XXXXXXXXXXX"; //スプレッドシートのID 4var PUSH = "https://api.line.me/v2/bot/message/push"; 5var REPLY = "https://api.line.me/v2/bot/message/reply"; 6var line_endpoint = 'https://api.line.me/v2/bot/message/reply'; 7var ID = 'XXXXXXXXXXX'; 8var HistorySheet = SpreadsheetApp.openById(SS_ID).getSheets()[0]; 9 10function doPost(e) { 11 var contents = e.postData.contents; 12 var obj = JSON.parse(contents); 13 var events = obj["events"]; 14 for (var i = 0; i < events.length; i++) { 15 if (events[i].type == "message" && events[i].message.type == "text") { 16 reply_message(events[i]); 17 } 18 } 19} 20 21function reply_message(e) { 22 var input_text = e.message.text; 23 var reply_text = ""; 24 const userId = e.source.userId; 25 const history = getLastUserHistory(userId); 26 27 if (history && history.continuous) { 28 if (history.text == "70%*2") { 29 reply_text = input_text + " の 軽減率70%×70%での実質HPは\n約 " + Math.floor(input_text / 0.09) + "です"; 30 } else if (history.text == "50%*2") { 31 reply_text = input_text + " の 軽減率50%×50%での実質HPは\n約 " + Math.floor(input_text / 0.25) + "です"; 32 } else if (history.text == "70%*50%") { 33 reply_text = input_text + " の 軽減率70%×50%での実質HPは\n約 " + Math.floor(input_text / 0.15) + "です"; 34 } else if (history.text == "25%*2") { 35 reply_text = input_text + " の 軽減率25%×25%での実質HPは\n約 " + Math.floor(input_text / 0.5625) + "です"; 36 } else if (history.text == "75%*2") { 37 reply_text = input_text + " の 軽減率75%×75%での実質HPは\n約 " + Math.floor(input_text / 0.0625) + "です"; 38 } else if (history.text == "50%*2+30%*2") { 39 reply_text = input_text + " の 軽減率50%×50%×30%×30%での実質HPは\n約 " + Math.floor(input_text / 0.1225) + "です"; 40 } else if (history.text == "50%*2+25%") { 41 reply_text = input_text + " の 軽減率50%×50%×25%での実質HPは\n約 " + Math.floor(input_text / 0.1875) + "です"; 42 } else if (history.text == "25%+50%*2") { 43 reply_text = input_text + " の 軽減率25%×50%×50%での実質HPは\n約 " + Math.floor(input_text / 0.1875) + "です"; 44 } else if (history.text == "35%*2") { 45 reply_text = input_text + " の 軽減率35%×35%の実質HPは\n約 " + Math.floor(input_text / 0.4225) + "です"; 46 } else if (history.text == "35%*25%") { 47 reply_text = input_text + " の 軽減率35%×25%の実質HPは\n約 " + Math.floor(input_text / 0.4875) + "です"; 48 } else if (history.text == "25%*35%") { 49 reply_text = input_text + " の 軽減率25%×35%の実質HPは\n約 " + Math.floor(input_text / 0.4875) + "です"; 50 } 51 setLastUserHistory(userId, input_text, false); 52 } 53 else { 54 if (input_text == "70%*2") { 55 reply_text = "軽減率70%×70%で計算します。\n計算したいパーティーの最大HPを、半角英数字でカンマ区切りをせずに入力してください。"; 56 } else if (input_text == "50%*2") { 57 reply_text = "軽減率50%×50%で計算します。\n計算したいパーティーの最大HPを、半角英数字でカンマ区切りをせずに入力してください。"; 58 } else if (input_text == "70%*50%") { 59 reply_text = "軽減率70%×50%で計算します。\n計算したいパーティーの最大HPを、半角英数字でカンマ区切りをせずに入力してください。"; 60 } else if (input_text == "25%*2") { 61 reply_text = "軽減率25%×25%で計算します。\n計算したいパーティーの最大HPを、半角英数字でカンマ区切りをせずに入力してください。"; 62 }else if (input_text == "75%*2") { 63 reply_text = "軽減率75%×75%で計算します。\n計算したいパーティーの最大HPを、半角英数字でカンマ区切りをせずに入力してください。"; 64 }else if (input_text == "50%*2+30%*2") { 65 reply_text = "軽減率50%×50%×30%×30%で計算します。\n計算したいパーティーの最大HPを、半角英数字でカンマ区切りをせずに入力してください。"; 66 } else if (input_text == "35%*2") { 67 reply_text = "軽減率35%×35%で計算します。\n計算したいパーティーの最大HPを、半角英数字でカンマ区切りをせずに入力してください。"; 68 }else if (input_text == "50%*2+25%") { 69 reply_text = "軽減率50%×50%×25%で計算します。\n計算したいパーティーの最大HPを、半角英数字でカンマ区切りをせずに入力してください。"; 70 }else if (input_text == "25%+50%*2") { 71 reply_text = "軽減率50%×50%×25%で計算します。\n計算したいパーティーの最大HPを、半角英数字でカンマ区切りをせずに入力してください。"; 72 }else if (input_text == "25%*35%") { 73 reply_text = "軽減率25%×35%で計算します。\n計算したいパーティーの最大HPを、半角英数字でカンマ区切りをせずに入力してください。"; 74 }else if (input_text == "35%*25%") { 75 reply_text = "軽減率35%×25%で計算します。\n計算したいパーティーの最大HPを、半角英数字でカンマ区切りをせずに入力してください。"; 76 } 77 if (reply_text) { 78 setLastUserHistory(userId, input_text, true); 79 } 80 else { 81 setLastUserHistory(userId, input_text, false); 82 } 83 } 84 85 var postData = { 86 "replyToken": e.replyToken, 87 "messages": [{ 88 "type": "text", 89 "text": reply_text 90 }] 91 }; 92 93 var options = { 94 "method": "post", 95 "headers": { 96 "Content-Type": "application/json", 97 "Authorization": "Bearer " + ACCESS_TOKEN 98 }, 99 "payload": JSON.stringify(postData) 100 }; 101 const response = UrlFetchApp.fetch(URL, options); 102 if (response.getResponseCode() !== 200) { 103 throw new Error(response.getContentText()); 104 } 105} 106 107function getLastUserHistory(userId) { 108 const userHistory = HistorySheet.getDataRange().getValues(); 109 for (let i = userHistory.length - 1; 0 < i; i--) { 110 if (userHistory[i][1] === userId) { 111 return { 112 date: userHistory[i][0], 113 userId: userHistory[i][1], 114 text: userHistory[i][2], 115 continuous: userHistory[i][3] 116 }; 117 } 118 } 119 return undefined; 120} 121 122function setLastUserHistory(userId, text, continuous) { 123 const values = [new Date(), userId, text, continuous]; 124 HistorySheet.appendRow(values); 125}

カルーセルテンプレートのソースコード

Javascript

1var ACCESS_TOKEN = "XXXXXXXXXXXXX"; 2 3function doPost(e) { 4 var contents = e.postData.contents; 5 var obj = JSON.parse(contents); 6 var events = obj["events"]; 7 for (var i = 0; i < events.length; i++) { 8 if (events[i].type == "message") { 9 reply_message(events[i]); 10 } else if (events[i].type == "postback") { 11 post_back(events[i]); 12 } 13 } 14} 15 16function reply_message(e) { 17 var input_text = e.message.text; 18 if (input_text == "その他") { 19 var postData = { 20 "replyToken": e.replyToken, 21 "messages": [{ 22 "type": "template", 23 "altText": "カルーセルテンプレート", 24 "template": { 25 "type": "carousel", 26 "columns": [{ 27 "title": "タイトル1", 28 "text": "テキスト", 29 "actions": [{ 30 "type": "message", 31 "label": "A", 32 "text": "テスト1" 33 }, 34 { 35 "type": "message", 36 "label": "B", 37 "text": "テスト2" 38 }, 39 { 40 "type": "message", 41 "label": "C", 42 "text": "テスト3" 43 } 44 ] 45 }, 46 { 47 "title": "タイトル2", 48 "text": "テキスト", 49 "actions": [{ 50 "type": "message", 51 "label": "A", 52 "text": "テスト1" 53 }, 54 { 55 "type": "message", 56 "label": "B", 57 "text": "テスト2" 58 }, 59 { 60 "type": "message", 61 "label": "C", 62 "text": "テスト3" 63 } 64 ] 65 } 66 ] 67 } 68 }] 69 }; 70 } 71 fetch_data(postData); 72} 73 74function fetch_data(postData) { 75 var options = { 76 "method": "post", 77 "headers": { 78 "Content-Type": "application/json", 79 "Authorization": "Bearer " + ACCESS_TOKEN 80 }, 81 "payload": JSON.stringify(postData) 82 }; 83 UrlFetchApp.fetch("https://api.line.me/v2/bot/message/reply", options); 84}

追記:$を認識できていないのか、うまく作動しませんでした。
バッククォートのままだと返信が来ない、
バッククォートをダブルクォートにすると返信は返ってくる。といった形でした。

Javascript

1// 計算の答えを返す。input_rate:軽減率。input_hp:HP。 2function buildAnswerReply(input_rate, input_hp){ 3 const damage = DAMAGES[input_rate] 4 if(damage == null) return "ERROR"; 5 const detail = damage.detail; 6 const rate = damage.rate; 7 return "${input_hp} の 軽減率${detail} での実質HPは\n約${Math.floor(input_hp / rate)}です"; 8} 9 10 11// HP入力を促すダイアログを返す。input_rate:軽減率。 12function buildRequestReply(input_rate){ 13 const damage = DAMAGES[input_rate] 14 if(damage == null) return "ERROR"; 15 const detail = damage.detail; 16 return "軽減率${detail}で計算します。\n計算したいパーティーの最大HPを、半角英数字でカンマ区切りをせずに入力してください。"; 17}

イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

K_3578

2021/07/14 03:06

「Java」タグ>質問内容からは関係あるように見えません。 関係が無いのでしたらJavaタグの質問を閲覧しているユーザーのノイズになるので外してください。 JavaScriptと混同しているなら全く別の言語です。 初学者です。> こういうのはタイトルに初心者マーク付けるだけに留めてください。 それだけで判断できるので本文には質問の内容を書いてください。 似たような質問を複数回投稿しており> であれば、一つの質問で済むようにしましょう。 同様の質問を投稿するのは非推奨ですが、内容の異なる質問であれば投稿するのは問題ないです。
SN____R

2021/07/14 03:13

Javaタグに関してはJavaScriptと混同しておりました。申し訳ございません。 初学者なので右も左も分からず、気を悪くされる方が居る可能性がある様に感じたので付けさせて頂きました。 前置きという部分に記載しているので、この一言が無いよりはある方が良いように思えますがどうでしょうか。 似たような質問だが、同じ質問はしていないという話をしたかっただけですので、新しく質問を立てさせて頂きました。
K_3578

2021/07/14 03:20

JavaとJavaScriptに関してはメロンとメロンパンぐらい違います。背景関係が知りたければ ググれば出てきます。 ヘルプの「質問するときのヒント」というページはご覧になられたでしょうか。 読んでなければ一読を。質問する上では非常に参考になる内容です。 https://teratail.com/help/question-tips 基本的にエンジニアは無駄な事を嫌うので、気を使って書いているのはわかりますが、 書かなくていいかと。特に「初学者です。」の部分は。
guest

回答1

0

ベストアンサー

カルーセルは各カラム同数でないとエラーになるようです。

軽減率データをオブジェクトにして、繰り返しの記述を減らしてみました。

GAS

1// 軽減率。キー:入力用省略文字列、 detail:詳細文字列、 rate:計算用軽減率。 2// 個数は、3の倍数とする。(各カラム同数でないとカルーセル構築時にエラーになるため) 3const DAMAGES = { 4 "70%*2": { "detail": "70%×70%", "rate": 0.09 }, 5 "50%*2": { "detail": "50%×50%", "rate": 0.25 }, 6 "70%*50%": { "detail": "70%×50%", "rate": 0.15 }, 7 "25%*2": { "detail": "25%×25%", "rate": 0.5625 }, 8 "75%*2": { "detail": "75%×75%", "rate": 0.0625 }, 9 "50%*2+30%*2": { "detail": "50%×50%×30%×30%", "rate": 0.1225 }, 10 "50%*2+25%": { "detail": "50%×50%×25%", "rate": 0.1875 }, 11 "25%+50%*2": { "detail": "25%×50%×50%", "rate": 0.1875 }, 12 "35%*2": { "detail": "35%×35%", "rate": 0.4225 }, 13 "35%*25%": { "detail": "35%×25%", "rate": 0.4875 }, 14 "25%*35%": { "detail": "25%×35%", "rate": 0.4875 }, 15 "---": { "detail": "---", "rate": 0 }, // 3の倍数に揃えるためのプレースホルダ 16 //"----":{ "detail": "---", "rate": 0 } // 2個不足する場合の2個めのプレースホルダ 17} 18 19 20function doPost(e) { 21 var contents = e.postData.contents; 22 var obj = JSON.parse(contents); 23 var events = obj["events"]; 24 for (var i = 0; i < events.length; i++) { 25 if (events[i].type == "message" && events[i].message.type == "text") { 26 reply_message(events[i]); 27 } 28 } 29} 30 31 32function normalReply(e, reply_text){ 33 return { 34 "replyToken": e.replyToken, 35 "messages": [{ 36 "type": "text", 37 "text": reply_text 38 }] 39 } 40} 41 42 43// 計算の答えを返す。input_rate:軽減率。input_hp:HP。 44function buildAnswerReply(input_rate, input_hp){ 45 const damage = DAMAGES[input_rate] 46 if(damage == null) return "ERROR"; 47 const detail = damage.detail; 48 const rate = damage.rate; 49 return `${input_hp} の 軽減率${detail} での実質HPは\n約${Math.floor(input_hp / rate)}です`; 50} 51 52 53// HP入力を促すダイアログを返す。input_rate:軽減率。 54function buildRequestReply(input_rate){ 55 const damage = DAMAGES[input_rate] 56 if(damage == null) return "ERROR"; 57 const detail = damage.detail; 58 return `軽減率${detail}で計算します。\n計算したいパーティーの最大HPを、半角英数字でカンマ区切りをせずに入力してください。`; 59} 60 61 62function reply_message(e) { 63 const input_text = e.message.text; 64 let reply_text = ""; 65 let postData = null; 66 const userId = e.source.userId; 67 const history = getLastUserHistory(userId); 68 // 入力の続き 69 if (history && history.continuous) { 70 reply_text = buildAnswerReply(history.text, input_text); 71 if (reply_text == "ERROR") return; 72 postData = normalReply(e, reply_text) 73 setLastUserHistory(userId, input_text, false); 74 } else { // 新規入力 75 if (input_text == "その他") { 76 // カルーセルを表示 77 postData = createCarousel(e) 78 } else if (input_text.startsWith("---")) { 79 // プレースホルダーをタップしたとき 80 return; 81 } else { 82 reply_text = buildRequestReply(input_text) 83 if (reply_text == "ERROR") return; 84 postData = normalReply(e, reply_text) 85 } 86 if (reply_text) 87 setLastUserHistory(userId, input_text, true); 88 else 89 setLastUserHistory(userId, input_text, false); 90 } 91 92 93 const options = { 94 "method": "post", 95 "headers": { 96 "Content-Type": "application/json", 97 "Authorization": "Bearer " + ACCESS_TOKEN 98 }, 99 "payload": JSON.stringify(postData) 100 }; 101 const response = UrlFetchApp.fetch(URL, options); 102 103 if (response.getResponseCode() !== 200) { 104 values = response.getContentText() 105 HistorySheet.appendRow(values); 106 throw new Error(response.getContentText()); 107 } 108} 109 110 111function getLastUserHistory(userId) { 112 const userHistory = HistorySheet.getDataRange().getValues(); 113 for (let i = userHistory.length - 1; 0 < i; i--) { 114 if (userHistory[i][1] === userId) { 115 return { 116 date: userHistory[i][0], 117 userId: userHistory[i][1], 118 text: userHistory[i][2], 119 continuous: userHistory[i][3] 120 }; 121 } 122 } 123 return undefined; 124} 125 126 127function setLastUserHistory(userId, text, continuous) { 128 const values = [new Date(), userId, text, continuous]; 129 HistorySheet.appendRow(values); 130} 131 132 133// カルーセルを構築する。 134function createCarousel(e){ 135 const dmgarray = Object.keys(DAMAGES); 136 const dmgitems = dmgarray.map((k)=> {return {"type":"message", "label":k, "text":k}}) 137 const itemsPerPage = 3; //1つのカラム内のアイテムの数。最大3(LINE APIの仕様)各カラム同数でないとエラー。 138 const pages = Math.ceil(dmgarray.length / itemsPerPage); // カラムの数 139 const columns = [] 140 // dmgitemsを3つずつ区切ってcolumnsに格納。 141 for(let page = 0; page < pages; page++){ 142 columns.push({ 143 "title": " ", 144 "text": page+1, 145 "actions": dmgitems.slice(page * itemsPerPage, (page+1) * itemsPerPage) 146 }); 147 } 148 return { 149 "replyToken": e.replyToken, 150 "messages": [{ 151 "type": "template", 152 "altText": "軽減率計算", 153 "template": { 154 "type": "carousel", 155 "columns": columns 156 } 157 }] 158 }; 159}

投稿2021/07/13 16:46

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

SN____R

2021/07/13 22:13

回答ありがとうございます。試してみましたところ、うまく作動しませんでしたので追記を行いました。ご確認のほどよろしくお願いします。
退会済みユーザー

退会済みユーザー

2021/07/14 02:54

こちら手元のスマホでは、画像の事象は再現しなかった(正常に動いている)ので、原因は不明ですが、 バッククオートと$ではなく、文字列と変数をプラス記号で連結するように変えた場合はどうでしょうか? あと質問の本旨であるカルーセルの表示自体はうまく行っているという理解でいいですか?
SN____R

2021/07/14 05:13

文字列とプラス記号...試してみます。 カルーセルの表示自体もその他と入力しても返信がない状態でした。
SN____R

2021/07/14 08:55

>文字列と変数をプラス記号で連結するように変えた場合は 試してみたところ正常に動作するようになりました。本当に助かりました... 二つほど伺いたいのですが、軽減率データを追加したいときは const DAMAGESの部分に "XXXXX": { "detail": "XXXXX", "rate": },のように追加するという方法で合っていますでしょうか? また、もし可能であればなのですが、カルーセルの文字を変更することなどは出来ますでしょうか?
退会済みユーザー

退会済みユーザー

2021/07/14 15:02 編集

軽減率データを追加するときは、const DAMAGESの部分に"XXXXX": { "detail": "XXXXX", "rate": },のように追加してください。 ただし、個数合計が3の倍数になっていないとエラーでカルーセルが表示されませんので注意してください。 「カルーセルの文字を変更する」とはどういう意味でしょうか? 文字の字体を変更したいという意味ならば、それはできないようです。 (それともカルーセルの各カラムのタイトルを変更するという意味でしょうか?)
SN____R

2021/07/14 14:59

承知致しました。 カルーセルの文字というのはタイトル(押すボタンの部分ですね)を変更できないか、ということになります。
退会済みユーザー

退会済みユーザー

2021/07/14 15:10

変更可能です。
SN____R

2021/07/15 00:35

本当ですか...! さらに具体的に説明すると、Aというタイトルを押したら70%*2という文字列を送信する。といった形になるのですがどの様に書き加えるのでしょうか...?
退会済みユーザー

退会済みユーザー

2021/07/15 12:45 編集

createCarousel()関数の中で const dmgitems = dmgarray.map((k)=> {return {"type":"message", "label":k, "text":k}}) という行がありますが、 このうち、"label"がカルーセルのカラムに表示される文字列、"text"がタップしたときにメッセージとして入力される文字列に該当します。 したがって、"label"に「a」 "text"に"70%*2"という文字列を設定すればよいことになります。 (現状はどちらも 「k」になっている=DAMAGEのプロパティ(例:"70%*2"等)の同じ文字列が設定される)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問