Closed21

Slack/SlashコマンドからGoogleリマインダーを設定したい

TakumaYoshiokaTakumaYoshioka

実現したいこと

今年に入って我が家にはGoogle Nest Hubが導入された。いろいろ便利に使っているが、そのうちの一つとしてGoogleリマインダーに設定されたリマインダーを指定時間になるとリマインド+内容を画面表示してくれる。狭い部屋にはこれがなかなか便利だ。

一方で、我が家にはかれこれ3年くらい運用している家庭内Slackがある。

そこで、Slack上のリマインダーとGoogle Nest Hubでのリマインダーが同期したら素晴らしいと思った。これを実現すべく、Slackリマインダー→Googleリマインダーの設定を調べ始めたら沼だった。

TakumaYoshiokaTakumaYoshioka

まずはいつもの自動化ツール達のコネクタを調査

この手のものは、まず定番自動化ツールのコネクタを調べるべし。
いつもの通り3兄弟の機能を調べる。→IFTTT, Zapier, Integromat

ここで最初の問題に直面

Googleリマインダーの機能がGoogleカレンダー/タスク/Keepのどれに属するかわからない

どうやら、各ツールにGoogleリマインダーというコネクターは無いようだ。確かにリマインダーをセットするときはGoogleカレンダーからセットする。

しかしGoogleカレンダーのコネクターにはリマインダーセット機能はない。少し調べると、GoogleタスクやGoogle keepとリンクした上での使い方もあるらしい。しかし、どのコネクターにもその機能はない。

ならば、Google Assistantか?と思ったものの、そもそもトリガーだけで、アクションがない。

TakumaYoshiokaTakumaYoshioka

Googleカレンダーに「カレンダーのリマインダー機能」がある

自動化ツール三兄弟に機能がないのであれば、仕方がない。自分でAPIを叩こうと思って、Googleさんのドキュメントをあさり始めた。
すぐに下記リンクが見つかる。Googleカレンダードキュメント内のリマインダーというタイトル。完璧じゃないか。

https://developers.google.com/calendar/api/concepts/reminders

しかし、ドキュメントを読んだり、使い方を調べたりしていると、なにやら様子がおかしい。思っているリマインダー機能となにか違うようだ。

よくよく調べていくと、Googleリマインダーとは別に、「Googleカレンダーで設定した予定のリマインダー機能」があって、その設定をするためのAPIだったということ。ここにたどり着くまでに何度え???となったことか。

でも言われてみると確かにその機能ある。予定の設定画面に「通知」と書いてある。確かにドキュメントの名前も「Reminders and Notifications」だ。ドキュメントは悪くなかった。まだInboxと呼ばれるメールサービスが合った頃に使ってた!

TakumaYoshiokaTakumaYoshioka

答えをくれたのはGithubと勇敢なデベロッパー

こちらは「Googleリマインダーの設定方法」を知りたいのに、どうがんばって検索しても「Googleカレンダー予定上でのリマインダー機能の設定」が出てくる。

現代の自然言語クエリベースでの検索システムの限界を感じながら、半分諦め気味に検索結果を読み進めて行くと、光明を見つけた。

https://github.com/windmark/google-reminder-api-wrapper

2019年に開発されたこのラッパーによると、GoogleリマインダーのAPIは公開されていないらしい。そりゃ、自動化ツールたちは対応できないわけだ。

その上で、このラッパーはGoogleカレンダー上のUIから送信されているHTTPリクエストから解析して、開発したとのこと。その発想はなかった。
その影響で、クッキー情報を設定しないとリクエストが通らないらしい。

これを見つけたところで一段落。次は、実際にこのラッパーを試してみたい。

TakumaYoshiokaTakumaYoshioka

次はSlashコマンドからPythonスクリプトを実行したい

詳しい友人からアドバイスをもらった。やはりAPIサーバーと立てて、そこをSlackから叩くのが良さそう。

シンプルに進めるにはflask一択だと思っていたけれども、今はfastAPIというのが良いらしい。せっかくなので使ってみる。

TakumaYoshiokaTakumaYoshioka

google-reminder-api-wrapperを実行するAPIを立てる

fastAPIのチュートリアルを見ながら、マージしてみた。久しぶりのDocker設定に戸惑うものの、とりあえず動き始めた。
ところで、このまま行くと誰でもこのAPIから私のGoogleリマインダーへ設定ができてしまう。それは困る(無駄に翻訳の技術書調)

認証サーバーやDBを作るところまでやりたくないのだけれども、、、

TakumaYoshiokaTakumaYoshioka

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.
TakumaYoshiokaTakumaYoshioka

何をどうやっても、エラーが直らない。
Failed to startのエラー分はCloud runのデフォルト文で、実際に参照するべきエラー分は別にあることが上記Qiitaに記述されているが、GCP上のログを見に行ってもエラー文はこれのみ。

ログをよくよくみていると、エラーログの直前にInfoログがたくさんあって、そのうちの1つが実際のPythonコードのエラーを吐いていた。
え、エラーログじゃなくて、Infoログにあるの???ここまででにてだいぶハマった

TakumaYoshiokaTakumaYoshioka

GCRではまだdocker-composeが使えない

上記で見つけたエラーを辿っていくと、どうやらdocker-compose.ymlて行っている設定が全然効いていない。
そこで初めて気づいたのだけれども、これまで見てきたGCRの例に1個もdocker-composew使用した例がない。これもしかして、GCRではdocker-composeが使えなくて、ずっとエラー吐いている???

Stack overflow情報ではあるけれども、やはり対応していないとのこと。
ここだけで土曜日を半日溶かした。
https://stackoverflow.com/questions/63782456/docker-compose-yml-in-google-cloud-run

TakumaYoshiokaTakumaYoshioka

デプロイ環境を用意しなかったので、とにかく何度も打つことになってしまったコマンド

上記記事によると-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 .
TakumaYoshiokaTakumaYoshioka

いろいろ調べていくと、上記でハマったエラーログは何らかコンテナ内にバグがあるとのこと。まずはローカルでちゃんと動くために下記コマンドで確かめるべし。Dockerを覚えてから、常々docker-composeに頼っていた自分にはなかなかハードな作業

$ PORT=8080 && docker run -it IMAGE_NAME

https://qiita.com/kenbu/items/5f549bf5883adede2974

そんなこんなで要約デプロイが完了

TakumaYoshiokaTakumaYoshioka

flaskへ軌道修正

ようやくAPIがデプロイできることを確認できたので、次はSlashコマンドからのリクエストを受け取る実装しようとする。
ところが、調べても、調べてもFastAPIでSlackからのリクエストを受け取る情報がでてこない。

やはりこのあたりの情報はFlaskのほうが多い。諦めて、乗り換えを決意。
(本来、エンジニアとしてはここは自力で頑張りたいところだが、今回は確実に完成させるために手軽さを重視[1]

こちらの記事を参考に、Slackからのアクセスであることの認証を実装。上記のユーザ認証機能はいらなくなった。

https://qiita.com/sh-tatsuno/items/55cd5f9e78b212fb57c2

https://qiita.com/ao_log/items/6bcc73d93bba1a068f2e

脚注
  1. こういうところが、つくづくエンジニア向いていないと思う ↩︎

TakumaYoshiokaTakumaYoshioka

flaskでSlashコマンドのテキストを受け取る

意外と、どの値を取ってくれが良いのかわからなくて困った。
結果的に今回はrequest.form['text']で取得できた。このあたりのデバッグはもっとちゃんと環境を作れば楽になるらしい。

TakumaYoshiokaTakumaYoshioka

gcloud buildが通らなくなる

ようやっとすべての実装ができあがった。いろいろソース整理をしてから、再度gcloud buildを投げたら、下記エラーでビルドが通らなくなった。

build step 0 "gcr.io/cloud-builders/docker" failed: step exited with non-zero status: 1

いろいろ対応してみたら、.gitがあるとビルドが通らないことが判明。まったくもって理由はわからない。わからないけれども、とりあえず前には進めて、これでようやく全てが揃った!!!

このスクラップは2022/04/26にクローズされました