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

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

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

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

Google スプレッドシート

Google スプレッドシートは、フリーで利用できる表計算ソフト。Webアプリのためインターネットに接続することで利用できます。チャートやグラフの作成のほか、シートを他のユーザーと共有したり、同時に作業を進めることも可能です。

Google Apps Script

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

Q&A

解決済

1回答

950閲覧

【GAS】LINE Messaging APIとの連携において、応答botを機能させたい(家計簿ツール)

Tokihiro

総合スコア1

LINE Messaging API

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

Google スプレッドシート

Google スプレッドシートは、フリーで利用できる表計算ソフト。Webアプリのためインターネットに接続することで利用できます。チャートやグラフの作成のほか、シートを他のユーザーと共有したり、同時に作業を進めることも可能です。

Google Apps Script

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

1グッド

0クリップ

投稿2022/11/23 15:02

前提

GAS、Messaging API共に初心者です。ぜひお力添えいただければ幸いです。
LINEのMessaging APIとGASを連携させた、家計簿のようなシステムを作っています。(個人利用レベル)

実現したいこと

  1. LINEで購入額やカテゴリーを送信する
  2. 送信データがスプレッドシートへ反映される
  3. 残り使用できる金額等をスプレッドシートの関数で計算の上、応答用botで自動返信する

つまずいているポイント

①LINEで送ったメッセージのタイムスタンプをスプシへ反映
→ソースコード上の48〜50行目で実現させたいができず。
②LINEで送ったメッセージのデータを集計し、その結果をLINEで応答
→ソースコード上の54〜56行目で実現させたいができず。
③コーディング初心者のため、コードの正確性が分からない

発生している問題・エラーメッセージ

TypeError: Cannot read property 'postData' of undefined at doPost(lineBotApp:9:35)
ReferenceError: userMessage is not defined (匿名) @ lineBotAppTest.gs:16

該当のソースコード

// LINE developersのメッセージ送受信設定に記載のアクセストークン(Messaging API設定の一番下のアクセストークン) const LINE_TOKEN = '該当のトークン'; const LINE_URL = 'https://api.line.me/v2/bot/message/reply'; //postリクエストを受取ったときに発火する関数 function doPostTest(e) { // 応答用Tokenを取得 const replyToken = JSON.parse(e.postData.contents).events[0].replyToken; // メッセージを取得 const userMessage = JSON.parse(e.postData.contents).events[0].message.text; } //メッセージを改行ごとに分割 const all_msg = userMessage.split("\n"); const msg_num = all_msg.length; // *************************** // スプレットシートからデータを抽出 // *************************** // 1. 今開いている(紐付いている)スプレッドシートを定義 const sheet = SpreadsheetApp.getActiveSpreadsheet(); // 2. ここでは、デフォルトの「シート1」の名前が書かれているシートを呼び出し const listSheet = sheet.getSheetByName("シート1"); // 3. 最終列の列番号を取得 const numColumn = listSheet.getLastColumn(); // 4. 最終行の行番号を取得 const numRow = listSheet.getLastRow()-1; // 5. 範囲を指定(上、左、右、下) const topRange = listSheet.getRange(1, 1, 1, numColumn); // 一番上のオレンジ色の部分の範囲を指定 const dataRange = listSheet.getRange(2, 1, numRow, numColumn); // データの部分の範囲を指定 // 6. 値を取得 const topData = topRange.getValues(); // 一番上のオレンジ色の部分の範囲の値を取得 const data = dataRange.getValues(); // データの部分の範囲の値を取得 const dataNum = data.length +2; // 新しくデータを入れたいセルの列の番号を取得 // 7. 日付データを取得 const Day = new Date(); const Year = Day.getFullYear(); const Month = Day.getMonth()+1; // *************************** // スプレッドシートにデータを入力 // *************************** // 最終列の番号まで順番にスプレッドシートの左からデータを新しく入力し、さらに5〜7行目に日付データを入力 for (let i = 0; i < msg_num; i++) { sheet.getRange(dataNum, i+1).setValue(all_msg[i]); sheet.getRange(numRow+1,5).setValue(Day); sheet.getRange(numRow+1,6).setValue(Year); sheet.getRange(numRow+1,7).setValue(Month); } // 自動返信を送信する var autoReplySheet = sheet.getSheetByName("ReplyText"); var wordList = autoReplySheet.getRange(1,2).getValues(); messages.push(wordList); //lineで返答する UrlFetchApp.fetch(LINE_URL, { 'headers': { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': `Bearer ${LINE_TOKEN}`, }, 'method': 'post', 'payload': JSON.stringify({ 'replyToken': replyToken, 'messages': messages, }), }); ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);

スプシ画像

シート1
イメージ説明
シート2
イメージ説明
シート3
イメージ説明

補足情報

応答用Botでは、以下のようなメッセージを自動送信させたいです。

="【今月の残高】 ・食費 :"&sum!J4&"円 ("&TEXT(sum!K4,"0%")&") ・外食費:"&sum!J5&"円 ("&TEXT(sum!K5,"0%")&") ・雑費 :"&sum!J6&"円 ("&TEXT(sum!K6,"0%")&") ・娯楽 :"&sum!J7&"円 ("&TEXT(sum!K7,"0%")&")"
Cocode👍を押しています

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

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

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

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

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

Cocode

2022/11/23 19:12

閉じカッコの位置がおかしいのですが、 // メッセージを取得 const userMessage = JSON.parse(e.postData.contents).events[0].message.text; } ←この閉じカッコを 提示されているコードの一番下に移動させたらエラー直りませんか?
Tokihiro

2022/11/23 21:27

コメントありがとうございます! 上記の閉じカッコの位置を変更しましたが、2つ目のエラーメッセージは消え、依然として1つ目のエラーメッセージが表示された状態です。 ・依然として表示されているエラーメッセージ↓ ーーーーーーーーーーーーーーーーーーーーーーーーーーーー TypeError: Cannot read property 'postData' of undefined doPostTest @ lineBotAppTest.gs:10 ーーーーーーーーーーーーーーーーーーーーーーーーーーーー 引き続き対処法をご存知でしたら、お力添えいただけますと幸いです><
YAmaGNZ

2022/11/23 21:38

エディタで実行していませんか?
Tokihiro

2022/11/23 22:29

コメントありがとうございます! エディタで実行というのは、プロジェクト画面で『実行』ボタンを押していないかということでしょうか? 実際の動作時は『実行』ボタンではなく、LINEでメッセージが届いた際に関数が実行されるようになっております。しかし、GAS上でプロジェクトの内容を変更した後に、デバッグで確認を行うと上記のエラーメッセージが表示され、変更内容がスプシに反映されないという状態となっているため、困っている次第です><
YAmaGNZ

2022/11/24 01:10

>GAS上でプロジェクトの内容を変更した後に、デバッグで確認を行うと この動作もLINEでメッセージを送ってみての動作なのですか? 通常postを受け取る場合、doPostという関数を用意します。 ですが、提示されているソースですとdoPostTestとなっているため、実際のpostから呼び出された結果エラーとなっているのかエディタ上で実行してエラーとなっているのかが分かりません。 (doPostを用意しても同じ話ではありますがdoPostTestという関数のためpostで呼び出されているとは思えない。提示されているソースが全文ではないのかもしれませんがこちらには提示がないのでわからない) またLINEでメッセージを送る際に既読にはなりますか?
Cocode

2022/11/24 05:47

> デバッグで確認を行うと ↑どういう意味ですか?エディタの「実行▶︎」ボタンを押しての確認でしたら、エラーになりますよ。 デバッグの際も必ずLINEからメッセージを送信してください。 --- またGASには何か特定の動作をしたときに自動的に実行される関数名というものが最初から用意されています。 たとえばスプレッドシートなどが編集されたら自動的に動く「onEdit()」関数です。 これを「onEditTest()」と名前を変えると、GASが用意した関数ではなく、自分でつくったただの関数になるので、自動で実行されなくなります。 「doPost()」も同じです。「doPostTest()」としたらダメです。
Tokihiro

2022/11/24 10:40

お二方ともご意見をいただきありがとうございます! ご指摘の通り、「doPostTest()」を→「doPost()」へ変更いたしました。 また、確認の際も必ずLINEからメッセージを送信することで確認するようにいたします。 >またLINEでメッセージを送る際に既読にはなりますか? 既読になります! そして、LINEのテキストもしっかりスプシへ反映されております!(「スプレッドシートにデータを入力」の"setValue(all_msg[i])"の箇所) しかし、その下のDay,Year,Monthがスプシへ入力されません。 また、自動送信もLINEに届かない状況です。(「/ 自動返信を送信する」以降) 知識に乏しく、コミュニケーションコストがかかり申し訳ないです。 何卒よろしくお願いします、!
Cocode

2022/11/25 03:22 編集

エディタの左メニューに「三」みたいなアイコンがあり、そこに実行ログが残っています。 失敗している場合赤文字で「Failed」と書いてあり、該当するログをクリックするとエラーの内容がかいてあります。 Failedになっていますか?またその場合エラー文のコピペをお願いします!
Tokihiro

2022/11/24 11:59

>failureになっていますか? 失敗となっていました!(同義ですかね) 失敗のログが2パターンありました。 ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー ①導入:バージョン1 関数:doPost 種類:ウェブアプリ 期間:0.316秒 ②導入:Head    関数:doPost 種類:エディタ   期間:1.032秒 ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー ②にのみエラーメッセージがあったため、以下送信いたします。 TypeError: Cannot read property 'postData' of undefined at doPost(lineBotApp:10:37)
Cocode

2022/11/24 12:37

うーん。。。 https://auto-worker.com/blog/?p=5141 ↑このサイトにLINE Messaging APIの手順をかいていますけど確認していただきたいのは… ①TOKEN間違ってないですか? ②Webhookは設定されていますか?
Tokihiro

2022/11/24 12:43

①チャネルアクセストークンは合っております! 念の為トークンを再発行した上でGASに新しいトークンを反映し、その後にLINEでメッセージを送りましたが、スプシ上に反映されていたので問題ないかと、、! ②Webhookは設定されております! 検証し、『成功』というテキストは出てきております。
Tokihiro

2022/11/24 12:53

五月雨式にすみません! 以下のYouTubeで使用しているスプシを参考に少しコードを変えたところ、応答用機能が動かなくなった次第です、、 https://youtu.be/9K_EKn2QQUQ
Cocode

2022/11/24 14:12

動画の概要欄に貼られているQiitaを拝見しました。 そのそも今どういうエラーが起きているかというと、 const replyToken = JSON.parse(e.postData.contents).events[0].replyToken; ↑の部分で、「postData」なんてないから情報読めねーよっていうエラーなんですよね。 つまりdoPost(e)が発火した時点で、きちんとイベントオブジェクトが受け取れてないんです。 上記のコード以下のコードはおそらく何も関係ないです。 LINE Developers上の設定で何か不備があるのかなぁと推察していますが、 提示されているコードは(私がお願いした修正をした上で)問題ないように見えるので、原因がわかりかねます…
Cocode

2022/11/24 14:15

参考にされている動画が2年前のものなので、プログラミングの世界からいえばちょっと古い情報です。 https://auto-worker.com/blog/?p=5141 こちらの記事でしたら今年の情報なので、一度こちらで簡単なものからためしてみてはいかがでしょうか?
YAmaGNZ

2022/11/24 14:40

双方情報が錯綜しているように思えます。 現状は doPostに変更しLINEで送信した結果、「Cannot read property 'postData' of undefined」は発生していない そして、スプレッドシートのE~G列に日付、年、月が入らない でいいのですかね? 送信したメッセージはスプレッドシートに入るのですか?
Tokihiro

2022/11/24 22:29

@Cocode様 ありがとうございます! 2年前で古い情報となるのですね、、 URLの共有ありがとうございます! こちらを元に再度作成してみます!
Tokihiro

2022/11/24 22:33

@YAmaGNZ様 いえ、依然として以下のエラーメッセージは発生いたします、! TypeError: Cannot read property 'postData' of undefined at doPost(lineBotApp:10:37) ただ、スプシに送信データや日付等は入るようになりました! A〜C列に日付・年・月、D〜G列にLINEでの送信データを入れるようにしたところ、全てのデータが無事に入るようになりました! 残っている問題は、「特定のセルの値を、自動でリプライさせることができない」状態です。 シート3のB2セルのデータを自動で返信させることができるようにコードを変えたい次第です、!
Cocode

2022/11/25 03:33 編集

(コメント削除 原因わかったので解答欄にかきます)
YAmaGNZ

2022/11/25 07:34

>確認の際も必ずLINEからメッセージを送信することで確認するようにいたします と書いているので、こちらはそれ以降は「エディタで実行する」という行為自体していないと考えます。 ですから、「Cannot read property 'postData' of undefined」とエラーが出ると言われると LINEからメッセージを送った場合でも「Cannot read property 'postData' of undefined」が発生すると判断します。 そうなると貴方が言う >全てのデータが無事に入る ということがありえなくなります。 言っている意味が分かりますか?
Tokihiro

2022/11/25 08:21

@Cocode様 ご回答ありがとうございます! 全て変更してやってみます!
Tokihiro

2022/11/25 08:25

@YAmaGNZ様 ありがとうございます! 仰っていただいたことを踏まえ、再度同じシートで試したり、コピーを作って試しました。 ですが、やはりエラーは出ているものの、全てのデータが無事にスプレッドシートに入っている状況です、!
guest

回答1

0

ベストアンサー

不具合の原因

大まかに3つです。

  • doPostTest(e)と関数名を変更してしまっている
  • 関数の閉じ波括弧}が変な位置にある
  • 送信するmessageがまったくのデタラメ

原因① doPostTest(e)と関数名を変更してしまっている

(コメント欄より自分の発言を引用)

GASには何か特定の動作をしたときに自動的に実行される関数名というものが最初から用意されています。
たとえばスプレッドシートなどが編集されたら自動的に動く「onEdit()」関数です。
これを「onEditTest()」と名前を変えると、GASが用意した関数ではなく、自分でつくったただの関数になるので、自動で実行されなくなります。
「doPost()」も同じです。「doPostTest()」としたらダメです。

doPost(e)に直しましょう。また、これは今回の場合LINEからメッセージが送信されたときに(e)の部分でその情報を受け取っています。
エディタ画面から「実行▶︎」してしまうと何も情報を受け取っていないのでエラーとなります。
デバッグの際も必ずLINEでメッセージを送信するようにしましょう。

原因② 関数の閉じ波括弧}が変な位置にある。

変な位置に関数の閉じ波括弧がありますので、修正しましょう。

javascript

1//postリクエストを受取ったときに発火する関数 2function doPostTest(e) { 3 4 // 応答用Tokenを取得 5 const replyToken = JSON.parse(e.postData.contents).events[0].replyToken; 6 // メッセージを取得 7 const userMessage = JSON.parse(e.postData.contents).events[0].message.text; 8} ←この閉じ括弧を削除して

javascript

1 ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON); 2} ←ここに書く

原因③ 送信するmessageがまったくのデタラメ

  • 単一のセルから値を取得するときは🚫.getValues()ではなく→✅.getValue()です。
    • またB2セルは🚫(1,2)ではなく→✅(2,2)ではないでしょうか。
  • message.push(wordList)とありますが、事前にmessageという変数または定数を宣言していないので、事前に定義しましょう。
  • messageは配列で、そしてその中身は特定のプロパティをもったオブジェクトでなければいけません。
正しいmessage

javascript

1[{ 2 'type': 'text', 3 'text': 'ここに自動送信したい文章がはいります' 4}]

コード例

javascript

1// LINE developersのメッセージ送受信設定に記載のアクセストークン(Messaging API設定の一番下のアクセストークン) 2const LINE_TOKEN = '該当のトークン'; 3const LINE_URL = 'https://api.line.me/v2/bot/message/reply'; 4 5 6//postリクエストを受取ったときに発火する関数 7function doPost(e) { 8 9 // 応答用Tokenを取得 10 const replyToken = JSON.parse(e.postData.contents).events[0].replyToken; 11 // メッセージを取得 12 const userMessage = JSON.parse(e.postData.contents).events[0].message.text; 13 14 //メッセージを改行ごとに分割 15 const all_msg = userMessage.split("\n"); 16 const msg_num = all_msg.length; 17 18 // *************************** 19 // スプレットシートからデータを抽出 20 // *************************** 21 // 1. 今開いている(紐付いている)スプレッドシートを定義 22 const sheet = SpreadsheetApp.getActiveSpreadsheet(); 23 // 2. ここでは、デフォルトの「シート1」の名前が書かれているシートを呼び出し 24 const listSheet = sheet.getSheetByName("シート1"); 25 // 3. 最終列の列番号を取得 26 const numColumn = listSheet.getLastColumn(); 27 // 4. 最終行の行番号を取得 28 const numRow = listSheet.getLastRow() - 1; 29 // 5. 範囲を指定(上、左、右、下) 30 const topRange = listSheet.getRange(1, 1, 1, numColumn); // 一番上のオレンジ色の部分の範囲を指定 31 const dataRange = listSheet.getRange(2, 1, numRow, numColumn); // データの部分の範囲を指定 32 // 6. 値を取得 33 const topData = topRange.getValues(); // 一番上のオレンジ色の部分の範囲の値を取得 34 const data = dataRange.getValues(); // データの部分の範囲の値を取得 35 const dataNum = data.length + 2; // 新しくデータを入れたいセルの列の番号を取得 36 // 7. 日付データを取得 37 const Day = new Date(); 38 const Year = Day.getFullYear(); 39 const Month = Day.getMonth() + 1; 40 41 // *************************** 42 // スプレッドシートにデータを入力 43 // *************************** 44 // 最終列の番号まで順番にスプレッドシートの左からデータを新しく入力し、さらに5〜7行目に日付データを入力 45 for (let i = 0; i < msg_num; i++) { 46 sheet.getRange(dataNum, i + 1).setValue(all_msg[i]); 47 sheet.getRange(numRow + 1, 5).setValue(Day); 48 sheet.getRange(numRow + 1, 6).setValue(Year); 49 sheet.getRange(numRow + 1, 7).setValue(Month); 50 } 51 52 // 自動返信を送信する 53 const autoReplySheet = sheet.getSheetByName("ReplyText"); 54 const messageToSend = autoReplySheet.getRange(2, 2).getValue(); 55 const message = [{ 56 'type': 'text', 57 'text': messageToSend, 58 }]; 59 60 //lineで返答する 61 UrlFetchApp.fetch(LINE_URL, { 62 'headers': { 63 'Content-Type': 'application/json; charset=UTF-8', 64 'Authorization': `Bearer ${LINE_TOKEN}`, 65 }, 66 'method': 'post', 67 'payload': JSON.stringify({ 68 'replyToken': replyToken, 69 'messages': message, 70 }), 71 }); 72 73 ContentService.createTextOutput(JSON.stringify({ 74 'content': 'post ok' 75 })).setMimeType(ContentService.MimeType.JSON); 76}

お試しください。

投稿2022/11/25 04:41

編集2022/11/25 05:04
Cocode

総合スコア2314

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問