GASでDiscordにGoogle Driveの変更通知をしたかったけど、かゆいところに手が届かなかったのでメモ。


コード

しくみ

sequenceDiagram autonumber actor User participant GAS participant GDrive participant Discord GAS->>GDrive:watch activate GDrive GDrive-->>GAS:sync message User->>GDrive:add file GDrive->>GAS:add message GAS->>Discord:notificate GAS->>GDrive:watch deactivate GDrive
  1. GASでDrive APIのChanges: watch1を叩く。すると、channelが作成され、Driveに変更が加わると事前に渡しているaddressに通知が飛ぶ。
  2. 成功すると200 OKが返る。
  3. Driveに変更を加える。閲覧だけでも変更とみなされてしまう(後述)
  4. 変更を通知する。
  5. DiscordのWebhook URLに通知を飛ばす。
  6. channelの有効期限が切れる前にwatchする必要がある。これはGASで定期実行しておく。

諸々の設定とか

前提

以下を使う

  • clasp: CLI上でコードが書けるようになる
  • typescript: clasp>=1.5.0ならpush時に自動でjsにトランスパイルされる

初期設定

npm init -y
npm i @google/clasp -g
npm i @types/google-apps-scrips prettier --save-dev
clasp login
# Google Apps Script APIをONに変更する(ブラウザで)
clasp create # プロジェクト作成

tsconfig追加

typescript有効化のためにtsconfig.jsonを追加する

tsconfig.json
{
  "compilerOptions": {
    "lib": ["esnext"],
    "experimentalDecorators": true
  }
}

claspあれこれ

よく使うコマンド

clasp push # GASのプロジェクトに現在のコードをpush
clasp pull # pushの逆

引用:https://github.com/google/clasp/blob/master/docs/typescript.md

Drive API追加

Driveの変更をwatchするのに必要なので、Drive API2を追加する。

appsscript.json
{
  "timeZone": "Asia/Tokyo",
  "dependencies": {
    "enabledAdvancedServices": [
      {
        "userSymbol": "Drive",
        "version": "v2",
        "serviceId": "drive"
      }
    ]
  },
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "webapp": {
    "executeAs": "USER_DEPLOYING",
    "access": "ANYONE_ANONYMOUS"
  }
}

環境変数を使いたい

WebhookのURL等はベタ書きするわけにはいかない。 GASにはスクリプトプロパティというkey-valueを対で管理する仕組みが最近できたのでこれを使う。(GitHub ActionsのSecretsみたいなやつ)

使用例
const DiscordWebhookURL = PropertiesService.getScriptProperties().getProperty("DISCORD_WEBHOOK_URL")

一度購読したチャンネルを破棄するためにstop3というAPIが用意されているが、これを利用するにはwatchが成功したときのレスポンスに含まれる、resourceIdchannelIdを保存しておく必要がある。 これらの値の保存にもスクリプトプロパティが使える。

以前は永続化にスプレッドシートを使う必要があったので、GAS単体で完結できるようになっていて嬉しい。

デプロイ

GASでWebアプリをデプロイするとき、新しいデプロイを選択すると毎回URLが変わってしまう。 そうすると、watchAPIを叩くときに渡す通知先アドレスも毎回変更しなくてはならず面倒。

そのため、デプロイの際は「デプロイの管理」→「✏」→「新バージョン」を選択する。

ロギング

GASはデフォルトでWebアプリのログが見れない。Google Cloud Platformプロジェクトの項目内の「プロジェクトを変更」を選択し、GCP紐付けてやる必要がある。 そうすると、GCP側のログエクスプローラからログが見れる。

webアプリのログはLogger.log()では見れないので、console.log()を使う。

失敗

変更の種別を受け取るにはDrive APIの通知のcustome header4を見る必要があるのだが、 なんとGASのdoPost()ではリクエストヘッダが見れない5ことがわかった。

変更通知のペイロード内に変更種別を判定できる情報がないか探したが、できるのはファイル削除の判別くらいで、他は無理そうだった(現状ファイルを開くだけで通知が飛んでしまう)。

そもそもこれをGASでやるのが間違いだったようだ。

おわりに

動き出す前にちゃんとしらべよう(自戒)。

参考文献