いつもお世話になっております。
本件ご教示頂けますでしょうか。
以下ご参照ください。
<現在>
formの回答をスプレットに集計
GASで上記スプレットの回答内容を自動送信
<改善したいこと>
おそらく最終行を取得し、本文に引用するコードになっているため
formに同時刻に回答があった場合にコンマ数秒差で後に回答した内容が2通送信される
例)Aさん→回答時刻00:00:01
Bさん→回答時刻00:00:03
上記2名のメール内容はBさんの回答内容になってしまう。
<試したこと>
lockサービス(うまくいきませんでしたが・・)
以下コードを入れてみました。
var lock = LockService.getScriptLock();
lock.waitLock(30000);
lock.releaseLock();//ロック解除
}
ご参考に以下が、現在組んでいるスクリプトです。
lockを入れるところや構文?が違うのか
そもそも最終行を取得し、本文に引用するコードになっているからダメなのか。。
乱文大変申し訳ございません。。
どうかよろしくお願いします。
【2/13追記】
waitlockでtry~cachの構文にしてみましたが、症状は改善されませんでした。
→同内容のメールが二通送信される。
function onFormSubmit() { Logger.log('sendMailGoogleForm() debug start'); } var lock = LockService.getDocumentLock();//ロック try { lock.waitLock(30000);//30秒待つ // メール送信先 var admin = "○○○○@○○○○.co.jp"; // 管理者(必須)→管理者アドレス var sendername = "管理者";//送信者名(必須) var cc = "○○○○@○○○○.co.jp"; // var bcc = admin; // Bcc: var reply = admin; // Reply-To: var to = ''; // To: (入力者のアドレスが自動で入ります) var d = new Date(); var day = Utilities.formatDate(d,'JST', 'M/dd'); // -------------------------------------- 定義---------------------- var Spreadsheet = SpreadsheetApp.openById("abcdefgh1234567890"); var sheet = Spreadsheet.getSheetByName("メールフォーム"); //スプレッドとシートの指定 var ss = Spreadsheet.getSheetByName("宛先リスト"); var atesaki = ss.getRange(3,2).getValue(); //宛先の指定 // var atesaki2 = ss.getRange(4,2).getValue(); var rows = sheet.getRange(1, 1).getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow(); //A列の最終行を取得する var cols = sheet.getLastColumn(); var rg = sheet.getDataRange(); Logger.log("rows="+rows+" cols="+cols); var tenpo = sheet.getRange(rows, 5).getValue(); //件名に店舗名 var name = sheet.getRange(rows, 6).getValue(); //件名に氏名 // ---------------------------------------------------------------- // 設定エリアここから //------------------------------------------------------------ + "------------------------------------------------------------\n"; // 件名、本文、フッター var subject = "【日報】【"+day+"】"+tenpo+"/"+name+""; var body = "※本メールは自動配信となります。\n\n各位\n\nお疲れ様です。\n本日の実績報告となります。\n\n"// var footer = "------------------------------------------------------------\n\n" + "以上、よろしくお願いいたします。";// // 入力カラム名の指定 var NAME_COL_NAME = '名前'; var MAIL_COL_NAME = 'メールアドレス'; //------------------------------------------------------------ // 設定エリアここまで //------------------------------------------------------------ try{ // スプレッドシートの操作 var sheet = Spreadsheet.getSheetByName("メールフォーム"); var rows = sheet.getRange(1, 1).getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow(); var cols = sheet.getLastColumn(); var rg = sheet.getDataRange(); Logger.log("rows="+rows+" cols="+cols); // メール件名・本文作成と送信先メールアドレス取得 for (var i = 1; i <= cols; i++ ) { var col_name = rg.getCell(1, i).getValue(); // カラム名 var col_value = rg.getCell(rows, i).getValue(); // 入力値 if (col_name === "タイムスタンプ"){ continue;} if (col_name === "日付"){ continue;} if (col_name === "(集計用)当月"){ continue;} if (col_name === "(内訳)購入サポートから"){ continue;} body += col_name + "\n"; body += col_value + "\n\n"; if ( col_name === NAME_COL_NAME ) { body = col_value+" 様\n\n"+body; } if ( col_name === MAIL_COL_NAME ) { to = col_value; } } body += footer; // 送信先オプション var options = {name: sendername}; if ( cc ) options.cc = cc; if ( bcc ) options.bcc = bcc; if ( reply ) options.replyTo = reply; // メール送信 if ( atesaki ) { MailApp.sendEmail(atesaki, subject, body, options); }else{ MailApp.sendEmail(admin, "【失敗】Googleフォームにメールアドレスが指定されていません", body); } }catch(e){ MailApp.sendEmail(admin, "【失敗】Googleフォームからメール送信中にエラーが発生", e.message); } } catch (e) { //ロック取得できなかった時の処理等を記述する var checkword = "ロックのタイムアウト: 別のプロセスがロックを保持している時間が長すぎました。"; //通常のエラーとロックエラーを区別する if(e.message == checkword){ //ロックエラーの場合 msg = "誰かまだ使ってるみたい"; }else{ //ソレ以外のエラーの場合 msg = e.message; } } finally { //ロックを開放する lock.releaseLock(); } コード
waitlockを使用するのであるならば、tryの中に記述しなければなりません。
waitlockはロックを取得できない場合、例外を発生させるためです。
下記の記事が参考になります。
https://officeforest.org/wp/2018/04/25/排他制御でgoogle-apps-scriptを安全に実行/
tryの範囲ですが、定義の中にも処理が入っているので、
var lock = LockService.getScriptLock();
上記以外のすべてを囲うようにしてください。
lockなんてする必要ありません。
フォーム側のスクリプトに書いて、フォーム送信のトリガでe.responseにて、フォーム送信項目を取得すればよいです。
スプレッドシートから取得するからだめなのです。
記述されている質問文を見ると、macaron_xxxさんの考えに至ることは当然と思います。
ただし、推察しかないのですが、この質問は下記の続きと思われます。
https://teratail.com/questions/239977
ここの記事には、複数のユーザが登録すると、1つのスプレッドシートに記述されていく
ようなことが記述されています。
たぶん、この構造は変えられないのではないかと推察していますが、
もちろん、macaron_xxxさんの言われているとおり、スプレッドシートを廃止できれば
それが望ましいと考えます。
lock.releaseLock()が2つあります。1つでOKのはずなのですが。
括弧の数も合っていません。
macaron_xxxさん、kikukikuさん、
大変わかりづらく申し訳ございません。。
kikukikuさんの推察の通り、この構造を変えることは現状難しく、できる限りこのまま行きたいと考えております・・
lock.releaseLock()と括弧の数修正しました。
もう一度試しましたが、同じ状態でした・・
kikukikuさん
というより、構造を変えないと解決しません。
なぜならば、このスクリプトでロックしたところで、データの書き込みを制御することはできないからです。
ロックが可能であるとしても300名の同時回答にロックしていては、タイムエラーがでるのは想像に難くないです。
スプレッドシートは、フォーム回答で自動で書き込まれるシートではないのですか?
であるならば、何の構造も変えることなくスクリプトを書き換えるだけで問題なく動くはずですが。
macaron_xxxさん
フォーム回答で自動で書き込まれるシートはあるのですが、
それをメールフォームというシートに転記して、そのシートを参照するようにしています。
理由は2点あって
①フォームの回答内容が追加や削除が発生した場合にメールの仕上がり?を編集するため
→例)新しい項目を追加→自動書き込みシートの一番右にくる(そうなるとメール本文ないでは一番下に位置されてしまう)
②フォームでは当日の実績を報告→メール本文に月間合計の記載が必要な
ため、この集計をメールフォームシート上で行っている。
このような場合でも、macaron_xxxさんがおっしゃるようなスクリプトの書き換えでも
問題はいでしょうか?
集計だけは単純な置き換えでは無理ですね。
代案として、最終行を取得するのではなく、メール送信行にチェックをつけていき、チェックのない行に対してメール送信する方法が考えられます。
なるほど!例えばそうした場合に
現状以下のようにほぼ同時に2件の回答があった場合、Bさんの回答内容が2通送られてしまいますが・・
Aさん→回答時刻00:00:01
Bさん→回答時刻00:00:03
でも回答内容が自動で書き込まれるスプレットにはきちんと2名分の回答内容が入っています。
代案では2名分にチェックが入って、メール内容を判別できるのでしょうか??