Slack/SlashコマンドからGoogleリマインダーを設定したい
実現したいこと
今年に入って我が家にはGoogle Nest Hubが導入された。いろいろ便利に使っているが、そのうちの一つとしてGoogleリマインダーに設定されたリマインダーを指定時間になるとリマインド+内容を画面表示してくれる。狭い部屋にはこれがなかなか便利だ。
一方で、我が家にはかれこれ3年くらい運用している家庭内Slackがある。
そこで、Slack上のリマインダーとGoogle Nest Hubでのリマインダーが同期したら素晴らしいと思った。これを実現すべく、Slackリマインダー→Googleリマインダーの設定を調べ始めたら沼だった。
まずはいつもの自動化ツール達のコネクタを調査
この手のものは、まず定番自動化ツールのコネクタを調べるべし。
いつもの通り3兄弟の機能を調べる。→IFTTT, Zapier, Integromat
ここで最初の問題に直面
Googleリマインダーの機能がGoogleカレンダー/タスク/Keepのどれに属するかわからない
どうやら、各ツールにGoogleリマインダーというコネクターは無いようだ。確かにリマインダーをセットするときはGoogleカレンダーからセットする。
しかしGoogleカレンダーのコネクターにはリマインダーセット機能はない。少し調べると、GoogleタスクやGoogle keepとリンクした上での使い方もあるらしい。しかし、どのコネクターにもその機能はない。
ならば、Google Assistantか?と思ったものの、そもそもトリガーだけで、アクションがない。
Googleカレンダーに「カレンダーのリマインダー機能」がある
自動化ツール三兄弟に機能がないのであれば、仕方がない。自分でAPIを叩こうと思って、Googleさんのドキュメントをあさり始めた。
すぐに下記リンクが見つかる。Googleカレンダードキュメント内のリマインダーというタイトル。完璧じゃないか。
しかし、ドキュメントを読んだり、使い方を調べたりしていると、なにやら様子がおかしい。思っているリマインダー機能となにか違うようだ。
よくよく調べていくと、Googleリマインダーとは別に、「Googleカレンダーで設定した予定のリマインダー機能」があって、その設定をするためのAPIだったということ。ここにたどり着くまでに何度え???となったことか。
でも言われてみると確かにその機能ある。予定の設定画面に「通知」と書いてある。確かにドキュメントの名前も「Reminders and Notifications」だ。ドキュメントは悪くなかった。まだInboxと呼ばれるメールサービスが合った頃に使ってた!
答えをくれたのはGithubと勇敢なデベロッパー
こちらは「Googleリマインダーの設定方法」を知りたいのに、どうがんばって検索しても「Googleカレンダー予定上でのリマインダー機能の設定」が出てくる。
現代の自然言語クエリベースでの検索システムの限界を感じながら、半分諦め気味に検索結果を読み進めて行くと、光明を見つけた。
2019年に開発されたこのラッパーによると、GoogleリマインダーのAPIは公開されていないらしい。そりゃ、自動化ツールたちは対応できないわけだ。
その上で、このラッパーはGoogleカレンダー上のUIから送信されているHTTPリクエストから解析して、開発したとのこと。その発想はなかった。
その影響で、クッキー情報を設定しないとリクエストが通らないらしい。
これを見つけたところで一段落。次は、実際にこのラッパーを試してみたい。
google-reminder-api-wrapperを試してみた
ら、動いた!
どうやら2019年からAPI仕様は変わっていないらしい。環境変数をいじると将来困りそうだったので、Dockerの復習も含めてこちらを参考にしてみた。
次はSlashコマンドからPythonスクリプトを実行したい
詳しい友人からアドバイスをもらった。やはりAPIサーバーと立てて、そこをSlackから叩くのが良さそう。
シンプルに進めるにはflask一択だと思っていたけれども、今はfastAPIというのが良いらしい。せっかくなので使ってみる。
google-reminder-api-wrapperを実行するAPIを立てる
fastAPIのチュートリアルを見ながら、マージしてみた。久しぶりのDocker設定に戸惑うものの、とりあえず動き始めた。
ところで、このまま行くと誰でもこのAPIから私のGoogleリマインダーへ設定ができてしまう。それは困る(無駄に翻訳の技術書調)
認証サーバーやDBを作るところまでやりたくないのだけれども、、、
FastAPIのログイン機能を簡易的に利用する
こちらのページを参考に、最も単純そうなFastAPI-Loginを使用
Google cloud runへデプロイする
ローカルで構築できたコンテナとAPIをCloud Runへデプロイしたい
このあたりの記事が参考になった
Deplyエラーが直らない
Deployment failed
ERROR: (gcloud.beta.run.deploy) Cloud Run error: Container failed to start. Failed to start and then listen on the port defined by the PORT environment variable. Logs for this revision might contain more information.
PORT=8080の環境変数を足したら、少し進んだ!
次のエラー
Deployment failed
ERROR: (gcloud.beta.run.deploy) Deploying Revision.
何をどうやっても、エラーが直らない。
Failed to startのエラー分はCloud runのデフォルト文で、実際に参照するべきエラー分は別にあることが上記Qiitaに記述されているが、GCP上のログを見に行ってもエラー文はこれのみ。
ログをよくよくみていると、エラーログの直前にInfoログがたくさんあって、そのうちの1つが実際のPythonコードのエラーを吐いていた。
え、エラーログじゃなくて、Infoログにあるの???ここまででにてだいぶハマった
GCRではまだdocker-composeが使えない
上記で見つけたエラーを辿っていくと、どうやらdocker-compose.ymlて行っている設定が全然効いていない。
そこで初めて気づいたのだけれども、これまで見てきたGCRの例に1個もdocker-composew使用した例がない。これもしかして、GCRではdocker-composeが使えなくて、ずっとエラー吐いている???
Stack overflow情報ではあるけれども、やはり対応していないとのこと。
ここだけで土曜日を半日溶かした。
Dockerfileへ記述を移行して、デプロイしたらようやく通った!
デプロイ環境を用意しなかったので、とにかく何度も打つことになってしまったコマンド
上記記事によると-source .
が重要らしい
$ gcloud builds submit --project cloud-run-0001 --tag gcr.io/cloud-run-0001/helloworld
$ gcloud run deploy test --project xxx --region xxx --platform managed --source .
いろいろ調べていくと、上記でハマったエラーログは何らかコンテナ内にバグがあるとのこと。まずはローカルでちゃんと動くために下記コマンドで確かめるべし。Dockerを覚えてから、常々docker-composeに頼っていた自分にはなかなかハードな作業
$ PORT=8080 && docker run -it IMAGE_NAME
そんなこんなで要約デプロイが完了
flaskへ軌道修正
ようやくAPIがデプロイできることを確認できたので、次はSlashコマンドからのリクエストを受け取る実装しようとする。
ところが、調べても、調べてもFastAPIでSlackからのリクエストを受け取る情報がでてこない。
やはりこのあたりの情報はFlaskのほうが多い。諦めて、乗り換えを決意。
(本来、エンジニアとしてはここは自力で頑張りたいところだが、今回は確実に完成させるために手軽さを重視[1])
こちらの記事を参考に、Slackからのアクセスであることの認証を実装。上記のユーザ認証機能はいらなくなった。
-
こういうところが、つくづくエンジニア向いていないと思う ↩︎
flaskでSlashコマンドのテキストを受け取る
意外と、どの値を取ってくれが良いのかわからなくて困った。
結果的に今回はrequest.form['text']
で取得できた。このあたりのデバッグはもっとちゃんと環境を作れば楽になるらしい。
gcloud buildが通らなくなる
ようやっとすべての実装ができあがった。いろいろソース整理をしてから、再度gcloud build
を投げたら、下記エラーでビルドが通らなくなった。
build step 0 "gcr.io/cloud-builders/docker" failed: step exited with non-zero status: 1
いろいろ対応してみたら、.git
があるとビルドが通らないことが判明。まったくもって理由はわからない。わからないけれども、とりあえず前には進めて、これでようやく全てが揃った!!!
一通り処理が通った!!!
Slashコマンドからの引数を処理する部分とか、まったく賢く作れていないが、とりあえず人間がアジャストすればやりたい処理は動くようになった。
そのうち、このあたりの整備はしたい