GoogleドライブへのファイルアップロードをGASで監視してDiscordに通知してみた

# はじめに
社内業務でGoogleドライブにExcelファイルをアップロードしてもらい、その中身を確認するという作業が時々発生します。
発生が時々のため、Googleドライブの中をちょこちょこ確認する必要があり手間なのでGASで検知するようにしました。
~~（本番では検知後slackに通知しているのですが、本記事では検知まで）~~
検知までの記事にしようと思っていましたが、Discordに通知するところまで書きました。

# 設計
* 指定フォルダで1分以内に作成されたファイルを特定する
* GASのトリガーで上記のスクリプトを毎分実行させる

`1分以内に作成されたファイルを特定するスクリプト`を`毎分実行`＝新規に作成されたファイルを検出

# GAS実装
### 指定フォルダのファイル一覧を取得する
まず監視対象のフォルダからファイル一覧（FileIterator）を取得します。

```Javascript
function getFileList() {
  // Google Driveの対象フォルダIDを指定
  // フォルダIDはGoogleドライブのURLの 「folders/」 以降の長いランダム英数
  const targetFolder = DriveApp.getFolderById('フォルダID');
  // .getFiles() で対象フォルダ内のファイル一覧を取得
  const files = targetFolder.getFiles();

  // FileIterator
  return files;
}
```

これでフォルダ内のファイル一覧が取得できました。

### ファイル一覧からファイルを取得する
次に取得したファイル一覧（files）からファイルを取り出してみます。
引数に上記で取得したファイル一覧（files）を渡します。

```Javascript
function getFile(files) {
  // files(FileIterator)を最後までループする
  while(files.hasNext()) {
    // files(FileIterator)からファイルを順番に取り出す
    let file = files.next();
    // 一覧から取り出したファイルをログに出力
    Logger.log(file);
  }
}
```
上記を実行すると指定フォルダ内の全ファイル名がログに出力されます。

上記では取り出した `file` オブジェクト自体をログに出力し、結果ファイル名が表示されていましたが、丁寧にプロパティを取得するには以下のようにします。

```Javascript
// ファイル名を取得
file.getName()

// ファイル作成時間を取得
file.getDateCreated()
```

### 1分以内に作成（アップロード）されたファイルを特定する
フォルダ内のファイルは取得できるようになりました。
しかしこれでは昨日作成（アップロード）したファイルも、一昨日作成したファイルも全部出てきてしまいます。
そのため新規で作られた（アップロードされた）ファイルに限定します。
今回は `新規で作られた` 判断を `1分以内に作られた` とすることにしました。
先程作ったファイル一覧からファイルを取得する関数 `getFile(files)` を改造します。

```Javascript
function getFile(files) {                         // 既存コード
  // 1分以内の判定用に今の日時を取得
  const now = new Date();

  // files(FileIterator)の最後までループする          // 既存コード
  while(files.hasNext()) {                        // 既存コード
    // files(FileIterator)からファイルを順番に取り出す   // 既存コード
    let file = files.next();                      // 既存コード
    // ファイルの作成（アップロード）時間を取得
    let createDate = file.getDateCreated();
    // 差分を計算（1分経過していたら1.0）
    const timeDiff = (now.getTime() - createDate.getTime()) / (60 * 1000);
    // timeDiffが1以下 ＝ 1分以内
    if (1 >= timeDiff) {
      // 1分以内に作成（アップロード）されたファイル名をログに出力
      // 今回はログ出力していますが、ここでSlack等に投げる処理を書きます
      Logger.log(file.getName());
    }
  }                                               // 既存コード
}                                                 // 既存コード
```

これで1分以内に作成（アップロード）されたファイル名がログに出力されます。

#### 今までのコードをまとめると

```Javascript
function myFunction() {
  // 指定フォルダのファイル一覧を取得する
  const files = getFileList()
  // ファイル一覧からファイルを取得する
  getFile(files)
}

// 指定フォルダのファイル一覧を取得する
function getFileList() {
  // 対象のGoogle Driveの対象フォルダIDを指定
  // フォルダIDはGoogleドライブのURLの 「folders/」 以降の乱数
  const targetFolder = DriveApp.getFolderById('フォルダID');
  // 対象フォルダ内のファイル一覧を取得
  const files = targetFolder.getFiles();

  return files;
}

// ファイル一覧からファイルを取得する
function getFile(files) {
  // 1分以内の判定用に今の日時を取得
  const now = new Date();

  // files(FileIterator)の最後までループする
  while(files.hasNext()) {
    // files(FileIterator)からファイルを順番に取り出す
    let file = files.next();
    // ファイルの作成（アップロード）時間を取得
    let createDate = file.getDateCreated();
    // 差分を計算（1分経過していたら1.0）
    const timeDiff = (now.getTime() - createDate.getTime()) / (60 * 1000);
    if (1 >= timeDiff) {
      // 1分以内に作成（アップロード）されたファイル名をログに出力
      Logger.log(file.getName());
    }
  }
}
```

## GASトリガー設定
ファイル作成（アップロード）の検知はできるようになりました。
しかし今の状態だとGASを実行した過去1分間を監視するだけなので、自動実行するようにGASのトリガーを設定しましょう。
トリガーの設定は

* 実行する関数を選択: myFunction
* イベントのソースを選択: 時間主導型（初期値）
* 時間ベースのトリガーのタイプを選択: 分ベースのタイマー
* 時間の間隔を選択（分）: 1分おき

これで過去1分間に作られたファイルの監視をするスクリプトが、1分おきに動くようになりました。

## Discordに通知する
本番ではSlackに通知していますが、Discordのwebhookを使ったことがなかったので試しにDiscordに通知してみました。

### Discordのwebhook設定
1. サーバー設定 > 連携サービス > `ウェブフックを作成`ボタンをクリックする
1. ウェブフック画面で、`任意のNAME`を入力し、対象の`チャンネル`を選択する
1. `ウェブフックURLをコピー`ボタンをクリックする
ウェブフック画面右下の`変更を保存する`ボタンをクリックする

これでDiscord側の設定は完了です。
試しにcurlでメッセージを投げてみましょう。
（Qiita記事の[DiscordにWebhookでいろいろ投稿する](https://qiita.com/Eai/items/1165d08dce9f183eac74)を参考にしました:pray:）

```
% curl -X POST -H "Content-Type: application/json" -d '{"content": "テストメッセージ"}' 取得したウェブフックURL
```

![スクリーンショット 2021-04-24 21.29.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/710015/8a72b313-a759-2fcd-f753-cba2cdd3d666.png)
きた！:grinning:

Discordの投稿者の名前は、Discordのウェブフックで設定した`NAME`が使われるようです。
`username`を渡すことで投稿者名を上書きできるようです。

```
% curl -X POST -H "Content-Type: application/json" -d '{"username": "GENZ", "content": "名前を変えたよ"}' 取得したウェブフックURL
```

![スクリーンショット 2021-04-24 21.36.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/710015/92535d56-7c16-ccb5-cf8b-b683eb4d417b.png)

### GASからDiscord送信する

ではGASからDiscordに送信するようにしましょう。

```Javascript
function sendDiscord(sendContent) {
  const webhookUrl = '取得したウェブフックURL';

  const username = 'GENZ';
  const jsonData = {
    "username": username,
    "content": sendContent
  };

  const payload = JSON.stringify(jsonData);
  const options = {
    "method" : "post",
    "contentType" : "application/json",
    "payload" : payload
  };

  UrlFetchApp.fetch(webhookUrl, options);
}
```

このコードを `function getFile(files)` 内の最後の`// 1分以内に作成（アップロード）されたファイル名をログに出力`の箇所で `file.getName()` を引数にして呼んであげれば、Discordにファイル名が通知されます。
ファイル名だけDiscordで受け取っても微妙なので 

```
sendDiscord('`' + file.getName() + '` がアップロードされました。') 
```

とか分かりやすいメッセージにしておけば良いかと思います。 

# まとめ
今回は `file.getDateCreated()` でファイル作成時間を取得し処理を行いましたが、 `file.setTrashed(true)` を使うとファイルの削除ができるので、一定時間経過したファイルは削除というような応用もできると思います。

# We look forward to your visit.
株式会社GENZではソフトウェアのテストサービス業務を行っています。
キャリア採用も行っておりますので、ご興味がある方は以下をご確認ください！
[株式会社GENZ - Webテスト自動化]
(https://www.genz.jp/test-service-menu/web-test-automation/?qi=20210426)
[株式会社GENZ - 採用情報]
(https://www.genz.jp/recruit/?qirecruit=2021426)