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

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

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

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

Google Apps Script

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

Q&A

1回答

312閲覧

何度も通知するコードについて

enjinia_shoshin

総合スコア1

Google スプレッドシート

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

Google Apps Script

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

0グッド

1クリップ

投稿2024/05/30 05:09

編集2024/05/30 05:13

実現したいこと

手動・自動で動かした際に、変更した内容のみの通知がGoogleチャットの指定したスペースにいくようにしたい

発生している問題・分からないこと

GASでChatGPTを元にコードを組んでいる。本来であれば送信スクリプト内のトリガで行うのを手動で行うよう変更したが、変えてない箇所まで通知が行ってしまう

該当のソースコード

var previousData = []; var changes = []; // 初期化関数 function initialize() { var spreadsheet = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d'); var sheet = spreadsheet.getSheetByName('出欠席'); previousData = sheet.getRange('A2:D100').getValues(); } // 変更の検出関数 function detectChanges() { var spreadsheet = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/'); var sheet = spreadsheet.getSheetByName('出欠席'); var data = sheet.getRange('A2:D100').getValues(); data.forEach(function(row, index) { var name = row[0]; var status = row[1]; var remarks = row[2]; var room = row[3]; var lastUpdate = new Date(); var previousRow = previousData[index] || []; var previousName = previousRow[0] || ''; var previousStatus = previousRow[1] || ''; var previousRemarks = previousRow[2] || ''; var previousRoom = previousRow[3] || ''; if (name !== previousName || status !== previousStatus || remarks !== previousRemarks || room !== previousRoom) { var changeMessage = name + " さんが「" + room + "」で変更を行いました。\n"; if (status !== previousStatus) { changeMessage += "変更前の状況: " + (previousStatus || "空欄") + " ➔ 変更後の状況: " + status + "\n"; } if (remarks !== previousRemarks) { changeMessage += "変更前の備考: " + (previousRemarks || "空欄") + " ➔ 変更後の備考: " + remarks + "\n"; } changeMessage += "変更日時: " + Utilities.formatDate(lastUpdate, Session.getScriptTimeZone(), 'MM-dd HH:mm'); changes.push({ room: room, message: changeMessage }); } }); previousData = data; } // 送信ボタンから手動で通知を行うための関数 function sendNotificationFromButton() { detectChanges(); if (changes.length > 0) { var response = Browser.msgBox('変更内容を通知しますか?', Browser.Buttons.YES_NO); if (response == 'yes') { changes.forEach(function(change) { sendChangeNotification(change.room, change.message); }); Browser.msgBox('変更内容を通知しました'); changes = []; } else { Browser.msgBox('通知をキャンセルしました'); } } else { Browser.msgBox('変更がありません'); } } // 変更通知の送信 function sendChangeNotification(room, changeMessage) { var webhookUrls = { 'ルームA': 'https://chat.googleapis.com/v1/spaces', 'ルームB': 'https://chat.googleapis.com/v1/spaces/AAAArUzE9Y0', }; var webhookUrl = webhookUrls[room]; if (!webhookUrl) { Logger.log('Invalid room: ' + room); return; } var payload = { 'text': changeMessage }; var options = { 'method': 'post', 'contentType': 'application/json', 'payload': JSON.stringify(payload) }; try { var response = UrlFetchApp.fetch(webhookUrl, options); Logger.log('Response from ' + room + ': ' + response.getContentText()); } catch (error) { Logger.log('Error sending message to ' + room + ': ' + error.message); } } function clearAttendanceSheet() { var response = Browser.msgBox('記載内容を消去します。よろしいですか。', Browser.Buttons.YES_NO); if (response == 'yes') { try { var spreadsheet = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/'); var sheet = spreadsheet.getSheetByName('出欠席'); var backupSheet = spreadsheet.getSheetByName('バックアップ') || spreadsheet.insertSheet('バックアップ'); var lastRow = sheet.getLastRow(); var range = sheet.getRange('A2:E' + lastRow); var data = range.getValues(); var backupLastRow = backupSheet.getLastRow(); backupSheet.getRange(backupLastRow + 1, 1, data.length, data[0].length).setValues(data); range.clearContent(); Browser.msgBox('シート名「バックアップ」にてバックアップを取り、消去しました'); } catch (error) { Logger.log('Error clearing sheet: ' + error.message); Browser.msgBox('エラーが発生しました: ' + error.message); } } else { Browser.msgBox('操作を中止しました'); } } function setupTriggers() { ScriptApp.newTrigger('sendNotificationFromButton') .timeBased() .atHour(9) .everyDays(1) .create(); ScriptApp.newTrigger('clearAttendanceSheet') .timeBased() .atHour(16) .nearMinute(0) .everyDays(1) .create(); } // 初期化関数を呼び出す function onOpen() { var ui = SpreadsheetApp.getUi(); ui.createMenu('カスタムメニュー') .addItem('初期化', 'initialize') .addItem('変更内容を通知', 'sendNotificationFromButton') .addToUi(); }

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

シート「出欠席」で1~2名記載内容を変更して手動で起動したら
山田大輔 さんが「ルームA」で変更を行いました。
変更前の状況: 空欄 ➔ 変更後の状況: 出席
変更前の送信先: 空欄 ➔ 変更後の送信先: ルームA
変更日時: 05-30 12:08
青空美穂 さんが「ルームA」で変更を行いました。
変更前の状況: 空欄 ➔ 変更後の状況: 出席
変更前の送信先: 空欄 ➔ 変更後の送信先: ルームA
変更日時: 05-30 12:08
上記のように変えたメンバー、変えた箇所だけで良いのに

スプレッドシート出欠席に書いたメンバーが8人いたとしたら.
残りの6人分も何もしていないのに、変更したことになって通知が行くようになっている。

補足

特になし

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

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

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

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

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

YAmaGNZ

2024/05/30 05:30

何を比較して変更されたと判断しているかをログ等で確認しましたか?
enjinia_shoshin

2024/05/30 05:44

ログなどを確認しましたが、エラー表示等などは確認できませんでした。
YAmaGNZ

2024/05/30 05:49

違います。 変更を判断する箇所 if (name !== previousName || status !== previousStatus || remarks !== previousRemarks ~ のところでnameやpreviousNameの中身、何が同じで何が違うのかどうして変更したと判断されたのかを確認されましたか? とお聞きしています。 確認されていないのであれば、確認してみてください。
YellowGreen

2024/05/30 08:52 編集

function detectChanges() { の var previousRow = previousData[index] || []; var previousName = previousRow[0] || ''; var previousStatus = previousRow[1] || ''; var previousRemarks = previousRow[2] || ''; var previousRoom = previousRow[3] || ''; の時点では、previouseDataは宣言時に初期化されたままの空の配列(※)なので、 previousRowも空の配列になっていると思います。 なので、previouseNameからprviouseRoomは値が取得できずに || '' の '' が入るので 変更前の値が全て空白になってしまっていて 変更していない分も通知されるのではないでしょうか。 トリガーで実行していた時は期待どおりに動作していたのなら、 手動実行に変更した内容をご確認されるといいのでは。 (※) function initialize()を手動で単独実行しても previousDataの内容はどこにも保存していないので、 実行終了時点で無くなってしまいます。 次回手動でfunction sendNotificationFromButton() を実行した時にpreviousDataは改めて空の配列に初期化されます。
enjinia_shoshin

2024/05/30 11:30

Checking row 1: 2024/05/30 17:32:28 情報 Name: 山田大輔 (Previous: , Initial: ) 2024/05/30 17:32:28 情報 Status: 欠席 (Previous: , Initial: ) 2024/05/30 17:32:28 情報 Remarks: (Previous: , Initial: ) 2024/05/30 17:32:28 情報 Room: ルームA (Previous: , Initial: ) のような状態です。previousを含めたDataの内容がないのですね…そこを保存できるようにしないといけないのでしょうか。
YellowGreen

2024/05/31 00:24

> previousを含めたDataの内容がないのですね…そこを保存できるようにしないといけないのでしょうか。 現在のセルの値が前回の値と比べて変化があったら通知したいのですよね。 保存しておけば比較できると思いますが。 保存しないで変更があったことを検知する方法をお探しでしたら、 「編集時」のトリガーによる方法になると思います。
guest

回答1

0

例えば、次のようなコードが参考になるでしょうか。
ご提示いただいたコードの変数の宣言やデータの取得、比較等を変更してあります。

次回のチェック用に現在のシートの内容をPREVIOUS_SHEETという名前のシートに保存しておき、
次回以降の起動時にPREVIOUS_SHEETの内容をpreviousDataに格納します。

なお、PREVIOUS_SHEETが存在しない時は、
初回の起動と判断してPREVIOUS_SHEETの作成と現在のデータの保存を自動で行います。

JavaScript

1// 変更の検出関数 2function detectChanges() { 3 const dataRange = 'A2:D100'; 4 const ss = SpreadsheetApp.openById(SS_ID); 5 const sheet = ss.getSheetByName('出欠席'); 6 const data = sheet.getRange(dataRange).getValues(); 7 let previousSheet = ss.getSheetByName('PREVIOUS_DATA'); 8 // 初回起動時(前回データのシートがないとき)は、次回用のデータを保存するだけで終了する 9 if (!previousSheet) { 10 previousSheet = ss.insertSheet('PREVIOUS_DATA'); 11 previousSheet.getRange(dataRange).setValues(data); 12 return [{ firstTime: true }]; 13 } 14 const previousData = previousSheet.getRange(dataRange).getValues(); // previousDataの宣言と初期化は、ここに移動 15 const changes = []; // changesの宣言と初期化もここに移動→関数の戻り値として呼び出し元に渡す 16 17 data.forEach(function (row, index) { 18 // 現在の値を取得 19 const [name, status, remarks, room] = row; 20 const lastUpdate = new Date(); 21 22 // 前回の値を取得 23 const previousRow = previousData[index]; 24 const [, previousStatus, previousRemarks,] = previousRow; 25 26 // 現在と前回を比較 27 if (row.toString() !== previousRow.toString()) { 28 let changeMessage = name + " さんが「" + room + "」で変更を行いました。\n"; 29 30 if (status !== previousStatus) { 31 changeMessage += "変更前の状況: " + (previousStatus || "空欄") + " ➔ 変更後の状況: " + status + "\n"; 32 } 33 34 if (remarks !== previousRemarks) { 35 changeMessage += "変更前の備考: " + (previousRemarks || "空欄") + " ➔ 変更後の備考: " + remarks + "\n"; 36 } 37 38 changeMessage += "変更日時: " + Utilities.formatDate(lastUpdate, Session.getScriptTimeZone(), 'MM-dd HH:mm'); 39 40 changes.push({ 41 room: room, 42 message: changeMessage 43 }); 44 } 45 }); 46 // 現在のデータを次回用に保存 47 previousSheet.getRange(dataRange).setValues(data); 48 49 // 変更の有無を返す 50 return changes; 51} 52 53// 送信ボタンから手動で通知を行うための関数 54function sendNotificationFromButton() { 55 const changes = detectChanges(); 56 if (changes.length > 0) { 57 if (changes[0].firstTime) { 58 Browser.msgBox('初回起動時は、次回データを保存するだけで終了します'); 59 } else { 60 const response = Browser.msgBox('変更内容を通知しますか?', Browser.Buttons.YES_NO); 61 if (response == 'yes') { 62 changes.forEach(function (change) { 63 sendChangeNotification(change.room, change.message); 64 }); 65 Browser.msgBox('変更内容を通知しました'); 66 } else { 67 Browser.msgBox('通知をキャンセルしました'); 68 } 69 } 70 } else { 71 Browser.msgBox('変更がありません'); 72 } 73}

投稿2024/05/31 02:36

YellowGreen

総合スコア861

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.31%

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

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

質問する

関連した質問