共有してるGoogleスプレッドシートに変更があったか1時間に1回確認してDiscordへ通知を送る

## やりたいこと

* 他人と共有してる**Googleスプレッドシート**に対して、誰かが編集したときにその変更通知を**Discord**で受け取りたい。
    * スプシを共有してるメンバーが居るDiscordサーバーのチャンネルに通知すれば、互いの変更を知れてハッピーになれる。
* 通知は**1時間に1回程度の頻度**にしたい。
    * 即時通知だとうるさい。逆に変更を躊躇してしまう。
* 特定のシートの変更内容だけ受け取りたい。
* どこが変更されたかをちょっとくらいは通知内容に載せたい。

## Discord側の設定

1. 通知を飛ばしたい先のチャンネルの :gear: 歯車アイコンを押す（あるいはサーバー設定）。
1. 「ウェブフックを作成」ボタンを押す。
1. 「ウェブフックURLをコピー」ボタンを押して、URLをコピーしておく。

Webhookの作成には相応の権限が必要なので注意。

## Googleスプレッドシート側の設定

### GASを開く

1. Google Driveの対象スプレッドシートを開く。
1. 上部メニューの「拡張機能」を選択
1. 「App Script」を選択
1. 別窓でApp Scriptの編集画面が開かれる。

### ソースコード

以下をコピペ。
加えて、下記のソースコード内に `★★★` が付いているコメント部分（**3箇所**）を編集する。

```js:コード.gs
////////////////////////////////////////////////////////////////////////////////
// 編集時に発行されるイベント
////////////////////////////////////////////////////////////////////////////////
function recordToProperty(e) {
  // 変更されたのが指定したシートの場合のみ有効
  // ★★★編集を監視したいシート名に置き換える
  if (e.source.getSheetName() != 'シート1') {
    return;
  }

  // 変更時刻
  const time = new Date().getTime();

  // メッセージに変更された範囲、新しい値を入れる
  const message = [e.range.getA1Notation(), e.value];

  // 変更があったことをスクリプトプロパティに追加。keyは変更時刻
  const script_property = PropertiesService.getScriptProperties();
  script_property.setProperty(time, JSON.stringify(message));
}

////////////////////////////////////////////////////////////////////////////////
// 定期実行されるイベント
////////////////////////////////////////////////////////////////////////////////
function notifyToDiscord(e) {
  // スクリプトプロパティの変更をチェック。
  const script_property = PropertiesService.getScriptProperties();
  const properties = script_property.getProperties();
  const array = Object.entries(properties);

  // スクリプトプロパティに1件もなければ、この期間に変更なしと見なす。
  if (array.length == 0) {
    return;
  }

  // プロパティがあったので変更あり。Webhookを送信する。
  const WEBHOOK_URL = 'https://discord.com/api/webhooks/xxxxxx/xxxxxx';  // ★★★DiscordのWebhook URL

  // メッセージを作成
  let message_body = 'シートに変更があったよ!!\n';
  let embeds = {
    fields: [],
  };

  array.forEach((prop) => {
    const key = prop[0];              // time
    const val = JSON.parse(prop[1]);  // message
    embeds.fields.push({
      name: 'セル: ' + val[0],
      value: '変更後の内容: ' + val[1],
    });
  });

  const payload = {
    'username': /* ★★★投稿時のユーザー名。指定しない（デフォのままで良い）場合は、行ごと削除する */,
    'content': message_body,
    'tts': false,
    'embeds': [embeds],
  }

  const param = {
    'method': 'POST',
    'headers': {
        'Content-type': 'application/json'
      },
    'payload': JSON.stringify(payload)
  }
  UrlFetchApp.fetch(WEBHOOK_URL, param);

  // 送信成功後、プロパティを全削除
  script_property.deleteAllProperties();
}
```

### トリガー設定1：編集した即時反応

編集するたびに実行される処理にトリガーを設定する。
こちらの関数では通知を行わず、スクリプトプロパティに貯めるだけ。

左メニューの時計アイコンを押して、「トリガーを追加」ボタンを押す。
以下のように設定する。

| 項目 | 設定値 |
|-|-|
| 実行する関数 | `recordToProperty` |
| 実行するデプロイ | `HEAD`（デフォルト） |
| イベントのソース | スプレッドシートから |
| イベントの種類 | 編集時 |
| エラー通知設定 | 任意 |

なお、操作中にアクセス権限を求める警告が出た場合は、適宜「**許可（Allow）**」する。

### トリガー設定2：定期実行

定期実行するトリガーを設定する。
スクリプトプロパティが1件でもあればDiscordに通知する。

上記と同様に、以下のように設定する。

| 項目 | 設定値 |
|-|-|
| 実行する関数 | `notifyToDiscord` |
| 実行するデプロイ | `HEAD`（デフォルト） |
| イベントのソース | 時間主導型 |
| 時間ベースのトリガーのタイプ | 時間ベースのタイマー |
| 時間の間隔 | 1時間おき |
| エラー通知設定 | 任意 |

通知頻度を変えたい場合は、時間の間隔をイイ感じに調整する。

## 動作確認

1. スプレッドシートを編集する
1. `notifyToDiscord` 関数を手動で「実行」する

これでDiscordの指定したチャンネルへBotの書き込みが投稿されれば :ok: 。

## 参考記事

* [【GASで監視】シートが編集されたことを任意のタイミングでメール通知する \- myfunc\.jp](https://myfunc.jp/items/00138/index.html)
    * ↑ 一番参考にしました！
* [Google Apps ScriptからDiscordにチャットを投稿する](https://zenn.dev/tamfoi/articles/18c4a815cc3449)
* [DiscordにWebhookでいろいろ投稿する \- Qiita](https://qiita.com/Eai/items/1165d08dce9f183eac74)
* [\#46 トリガーの「編集時」と「変更時」の違いは？｜ともかつのノート](https://note.com/tomo_saka/n/nc03145bb8b37)
* [Class Range  \|  Apps Script  \|  Google Developers](https://developers.google.com/apps-script/reference/spreadsheet/range?hl=ja)
* [Class Spreadsheet  \|  Apps Script  \|  Google Developers](https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet?hl=ja)

## License

MIT License