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

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

新規登録して質問してみよう
ただいま回答率
85.49%
Google スプレッドシート

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

Gmail

GmailとはGoogleによって提供されているウェブメールのサービスのことです。

Google Apps Script

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

Q&A

解決済

1回答

996閲覧

【GAS】Gmailスプシ転記、時間主導トリガーのみエラー(length' of undefined)

cardamon

総合スコア19

Google スプレッドシート

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

Gmail

GmailとはGoogleによって提供されているウェブメールのサービスのことです。

Google Apps Script

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

0グッド

0クリップ

投稿2022/06/01 22:35

編集2022/06/01 23:21

前提

こちらでアドバイス頂きながら、Gmailに届く注文メールをスプレッドシートに転記する仕組みを作りました。(個人飲食店で、プログラミング初学者です)

具体的には、最新500件のメールの中で、件名に「新規注文番号」が含まれ、ラベル「転記済」がついていないメールのスレッドを探し、メール本文の必要情報を取得し、スプレッドシートの「予約表」シートに転記するという内容です。

イメージ説明

イメージ説明

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

この転記作業が、手動実行orスプレッドシート編集時トリガーに設定すると正常に動くのですが、時間主導トリガーに設定するとエラーが出ます。

エラーメッセージ TypeError: Cannot read property 'length' of undefined at tenkiMessage(splitコード:35:53)

該当のソースコード

コードは、以下です。

GoogleAppsScript

1 2const tenkiMessage = () => { 3const condition = 'subject:(新規注文番号) -label:(転記済)'; 4 5const start = 0; const max = 500; 6const threads = GmailApp.search(condition, start, max); 7 8//検索条件に該当するスレッド一覧を取得 9const messages = GmailApp.getMessagesForThreads(threads); 10 11const label = GmailApp.getUserLabelByName('転記済'); 12 13const data = messages.map(message => { 14const body = message[0].getPlainBody(); 15 16 const date = message[0].getDate(); 17 const subject = message[0].getSubject().replace('新規注文番号', ''); 18 const goukei = body.split('合計:')[1].split('\n')[0]; 19 20 // \t=タブ文字削除、\n=改行削除、\s=空白、g=2個目以降もの意 21 const test = body.split('日)')[1].replace(/[\s\t\n]/g,"") 22.split('==========')[0].trim(); 23 const basyo = body.split('1)場所を選ぶ↓:')[1].split('\n')[0]; 24 const hiduke = body.split('2)日付を選ぶ↓')[1].split('\n')[0]; 25 const jikan = body.split('3)時間を選ぶ↓')[1].split('\n')[0]; 26 27const datum = [date, subject, goukei, test, basyo, hiduke, jikan]; 28return datum; 29}); 30const ss = SpreadsheetApp.getActiveSpreadsheet(); 31const sheet = ss.getSheetByName('予約表'); 32 33// 最終行を取得 34const lastRow = sheet.getLastRow() + 1; 35 36sheet.getRange(lastRow, 1, messages.length, data[0].length).setValues(data); 37 38threads.forEach(thread => thread.addLabel(label)); 39}

参考にしたページ

https://teratail.com/questions/109959

初歩的なつまずきかもしれず恐縮ですが、参考サイトや、なにかヒントになるアドバイスなど頂けましたら幸いです。

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

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

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

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

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

cardamon

2022/06/01 23:28

メールや予約表は、イメージサンプルです。(実際のものとは異なります)
guest

回答1

0

ベストアンサー

エラーの原因

「TypeError: Cannot read property 'length' of undefined at tenkiMessage(splitコード:35:53)」というエラーは

sheet.getRange(lastRow, 1, messages.length, data[0].length).setValues(data);

の data[0].length の部分で発生しているものと思われます。


//検索条件に該当するスレッド一覧を取得 const messages = GmailApp.getMessagesForThreads(threads);

の部分でメールを取得していますが、検索条件に合致するメールがない場合、messagesは空の配列([])になります。

messagesが空の配列のとき、const data = messages.map(message => {}); の部分でdataも空の配列([])になります。

空の配列dataに対して、data[0] とすると、これは undefined となります。
そしてdata[0].length を評価した時点で 「'length' of undefined」 エラーが発生します。


解決案

対策として、検索条件に当てはまるメールが存在しない場合は、returnして処理をスキップするようにします。

diff

1const tenkiMessage = () => { 2const condition = 'subject:(新規注文番号) -label:(転記済)'; 3 4const start = 0; const max = 500; 5const threads = GmailApp.search(condition, start, max); 6 7//検索条件に該当するスレッド一覧を取得 8const messages = GmailApp.getMessagesForThreads(threads); 9+ if (messages.length === 0) { 10+ console.log('検索条件に合致するメールが見つかりませんでした。'); 11+ return; 12+} 13const label = GmailApp.getUserLabelByName('転記済') ?? GmailApp.createLabel('転記済'); 14 15const data = messages.map(message => { 16const body = message[0].getPlainBody(); 17 18 const date = message[0].getDate(); 19 const subject = message[0].getSubject().replace('新規注文番号', ''); 20 const goukei = body.split('合計:')[1].split('\n')[0]; 21 22 // \t=タブ文字削除、\n=改行削除、\s=空白、g=2個目以降もの意 23 const test = body.split('日)')[1].replace(/[\s\t\n]/g,"") 24.split('==========')[0].trim(); 25 const basyo = body.split('1)場所を選ぶ↓:')[1].split('\n')[0]; 26 const hiduke = body.split('2)日付を選ぶ↓')[1].split('\n')[0]; 27 const jikan = body.split('3)時間を選ぶ↓')[1].split('\n')[0]; 28 29const datum = [date, subject, goukei, test, basyo, hiduke, jikan]; 30return datum; 31}); 32const ss = SpreadsheetApp.getActiveSpreadsheet(); 33const sheet = ss.getSheetByName('予約表'); 34 35// 最終行を取得 36const lastRow = sheet.getLastRow() + 1; 37 38sheet.getRange(lastRow, 1, messages.length, data[0].length).setValues(data); 39 40threads.forEach(thread => thread.addLabel(label)); 41}

投稿2022/06/01 23:38

編集2022/06/02 02:14
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

cardamon

2022/06/01 23:55

コードの回答、また詳しいプロセスまで易しく解説ありがとうございます。 試しに頂いたコードをそのまま貼ってみたところ、 ```ここに言語を入力 SyntaxError: Unexpected token 'if'(行 9、ファイル「splitコード.gs」) ``` というエラーが出ました。 同じエラーを探したところ、JavaScript?の記事で「予約語は使えない」というものが出てきて(リンク1番目の記事)GASでも同様のことがあるのかな?と想像しました。(リンク2番め記事) https://www.javadrive.jp/javascript/ini/index5.html https://excel-ubara.com/apps_script1/GAS016.html (もし、そういうことでなく、単に私の貼り方の問題や、操作ミスであれば申し訳ありません…) しかし、エラーの意味がわかっただけでも、手がかりが頂けて大変たすかりました。本当にありがとうございます…! この半年間、時間トリガーが使えないことで、毎朝スプレッドシートを開いて、空白を一文字入力する(編集トリガーを起動させる)という地味作業にとらわれていたので、頂いたアドバイスを元に、また閉店後に調べてみます。
cardamon

2022/06/01 23:57

自レスですみません。 + if (messages.length === 0) { + console.log('検索条件に合致するメールが見つかりませんでした。'); + return; +} のプラスをつけたまま、コピペしてしまっていました。今からお店に出なければいけないので、また閉店後、プラスマークを消して再度うごかしてみます。取り急ぎ、お礼とお詫びまで…!
退会済みユーザー

退会済みユーザー

2022/06/02 00:01

先頭のプラス記号は消してください(追記部分を色分け表示するために付けたものです)こちらの説明不足ですみませんでした。
cardamon

2022/06/02 01:32

プラス記号の件、入れ違いで自レスしておりました、初歩的な誤りにも関わらず、やさしく補足ありがとうございます。 プラス記号を省いて貼ったところ、 Exception: 無効な引数: label(行 52、ファイル「splitコード」) という新たなエラーが出ましたので、もしかすると当初(35行目)のエラーが解消され、次のエラーに行き当たったのかな?と想像していますが、また貼り付けミスなど、もしお気づきの点があれば、ご指摘いただければ幸いです。 (まだエラーの読み解き方があまり解っていないので、また自分でも調べて勉強します。) このたびはありがとうございます!
退会済みユーザー

退会済みユーザー

2022/06/02 02:15 編集

そのGMailアカウントに「転記済」というラベルが存在しないため、 const label = GmailApp.getUserLabelByName('転記済'); のときにlabelが null または undefined になっている可能性があります。 null または undefined のラベルを設定しようとしたため「Exception: 無効な引数: label」というエラーが発生しているものと思われます。 したがって、この部分は null合体演算子 ?? を使用し、 const label = GmailApp.getUserLabelByName('転記済') ?? GmailApp.createLabel('転記済'); というようにして、「転記済」というラベルが存在しない場合は「転記済」ラベルを作成して返すようにしてください。 これは let label = GmailApp.getUserLabelByName('転記済'); if (label == null) label = GmailApp.createLabel('転記済'); の2行と同じ意味です。
cardamon

2022/06/02 04:51 編集

たびたびご教授ありがとうございます。まさにご指摘の通りでした。教えていただいた null合体演算子 ?? を書き加えたところ、完璧に動きました。 自分でも気づいていなかったのですが、今朝2つのアカウントでGoogleにログインしていたことで「現在、ファイルを開くことができません」とスクリプトが開けなくなり一度すべてログアウトし入り直したため、Gmailアカウントも別アカウントに切り替わり、ずっとラベルの存在しないメールボックスを探していたようでした。 (スプレッドシート自体は権限を持つ2つのアカウントで共有・編集していたため気づかず、自分の頭ではGmailラベルは作成済なのに…という思い込みでいたため全くの盲点でした) また今回、コードだけでなくプロセスも教えていただけ、本当に勉強になりました。ありがとうございます。 ・空の配列([])に、data[0] → undefined→data[0].●●→ 「'●●' of undefined」 ・ null または undefined →Exception: 無効な引数: label は、いずれもデータがなかった場合…というエラーだったので、今後エラーが出た時は、エラーをコピペで検索するだけでなく、データが存在しない場合の処理も疑うようにします。 と同時に、見よう見まねのコードを切り貼りするだけでなく、きちんと言語自体も読める(?)よう、今回いただいた回答を軸に言葉を調べ、勉強していきます。 改めて、何度もアドバイスいただきありがとうございます。 (以下、似たようなお困りでここにたどり着く方がいるかもしれないので、重リンクも貼っておきます) 無効な引数 エラーがでたキッカケ https://stafftaion.dcf7.com/438/ (「現在、ファイルを開くことができません」と出てスクリプトエディタが開かない)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問