1年ちょっと前にこんなQiitaを書いた:
Google AssitantでIFTTTの変数入力を再現する
簡単に要約すると
- それまで GoogleHome → IFTTT → Todoist という形で買い物リストを操作していたところ
- 仕様変更で塞がれたので、
- GoogleHome → GoogleKeep → ラズパイ → Todoist という新しいスキームを作った
しかし最近になってこのラズパイを経由する方法も塞がれてしまった。これが本記事の内容である。かなしいね。
※この記事はなにかを解決できた記録ではありません。どちらかというと、ここまで色々試したけど今のところ全部ダメだし諦めよっかな、という趣旨の作業記録です。
いきなり同期ができなくなる
ある日突然、GoogleKeepからTodoistへの同期が止まってしまった。
こういうことはたまにある。普段は何も考えず docker-compose restart をかけるのだがこのときは解決しなかった。
docker-compose logs で様子を見るとこんな調子。
Traceback (most recent call last):
File "/app/app.py", line 154, in
BrowserLoginRequiredException とあるので、最後のURLをブラウザで開いて承認すれば進むのかと思いきや
こんな画面でハングしてしまって一歩も進まなくなった。
例外クラス名でググる
BrowserLoginRequiredException でググるといくつか同種の事例が見つかる。
だいたいの結論は「二段階認証を有効にしてアプリパスワードでログインさせろ」。
ところが今回はすでにアプリパスワードでログインさせる形になっている。そしてエラーは出続けている。ううむ……。
パスワードまわりが怪しいのではと他にもあれこれ試してみた:
- アプリパスワードを削除して新しく作る
- アプリパスじゃない通常のパスワードで試す
- 二段階認証そのものを無効化 / 再有効化
- 実はパスワード間違ってるんじゃない?
- → わざと間違ったパスワードを入れてみて試す
- → エラーメッセージが変わるのでパスワード間違いではない
- 別のGoogleアカウントで試してみる
- 出てきたURLをAndroid Chromeで開く
しかし解決せず。
いろいろ最新化してみる
自分が遭遇する程度のエラーが完全初出ということは考えづらく、すでに解決されている場合も多々あるはず。ということで依存関係を全部最新化する。
まずDockerのベースイメージを python:3.9-slim-buster から python:3.12.1-slim-bullseye に上げる(3.13.0a2-slim-bookworm を試したが動かなかったのでちょっと落とした)。
あと依存ライブラリのバージョンも全部上げる。requirements.txt をこう修正。
gkeepapi==0.14.2 todoist-api-python==2.0.2 pyyaml==6.0 yamale==4.0.4 schedule==1.1.0
↓gkeepapi==0.15.0 todoist-api-python==2.1.3 pyyaml==6.0.1 yamale==4.0.4 schedule==1.2.1
docker-compose build → up してみる。同じエラーが出る。ダメだ、解決せず。
gkeepapiのソース読んでエラーの出処を探る
エラーメッセージから keep.login() を呼ぶ箇所まではわかっているので、その先の定義を読んでいく:
gpsoauth の perform_master_login() を呼んでいることがわかる。
さらに定義を追う:
https://github.com/simon-weber/gpsoauth/blob/master/gpsoauth/__init__.py#L103
この先はGoogleへのAPIリクエストらしい。つまりエラーの出処はこの gpsoauth ということになる。
gpsoauth単体で呼んでみる
こんな感じの login_test.py を書いて雑に実行してみる。エラーが出なければログイン自体の問題ではない、つまり gkeepapi の処理が悪いということになる。
from gpsoauth import perform_master_login, perform_oauth def getToken(email, password, android_id): response = perform_master_login(email, password, android_id) if "Token1" not in response: print("Master login failed.") if response.get("Error") == "NeedsBrowser": print(response.get("Url")) print(response["Token1"]) getToken('********@gmail.com', 'password', '3ee9002270d00157')
結果は NeedsBrowser エラーであった。そしてGoogleが伝えてきたURLをブラウザで開くと最初と同じハング画面に到達する。
苦し紛れにログイン部分を以下のように書き換えてもみたが、結果はやはり同じだった(完全に同じではなく、ハング画面のメッセージが日本語になった)。
perform_master_login(email, password, android_id, device_country="jp", operator_country="jp", lang="ja", sdk_version=33)
つまり gkeepapi は悪くなく、Googleが仕様を変えたかこの方法でのログインを制限しに来ている。こうなると根が深い。
そして諦めへ
そもそもの話、GoogleKeepには一般ユーザー向けの公開APIが存在しない。
エンタープライズ向けのAPIはあるのだが、それは一般ユーザーのアカウントではさわれない。
では gkeepapi はどうやって一般ユーザーのAPIだけでGoogleKeepに触っているのか? というと、(ちゃんと調べていないので半分想像だが)Androidデバイスに偽装してAuthTokenを取得することで制限を突破している。
AndroidのGoogleKeepアプリは当然APIで通信するので、AndroidのTokenさえ取れれば自作のアプリでも同じことができるという理屈だ。
このことは gpsoauth のコメントにも言及がある。perform_master_login() はAndroidデバイスがGoogleアカウントを初回に追加する処理と同じことをしている。
https://github.com/simon-weber/gpsoauth/blob/master/gpsoauth/__init__.py#L116-L117
今回はこの部分の仕様が変わったか塞がれたのだと考えられる。もともと非公式な方法だったのでダメになるのは時間の問題ではあった。
そして当然公式のサポートは一切ない。GoogleがKeepのAPIを一般ユーザーにも開放してくれるのが一番だが、それも期待できなさそうだ。
というわけで、諦めることにした。
今後……?
よくよく考えたらGoogleHomeに呼びかける方法では個人認識にかなりの高確率で失敗していて、決して使いやすいとは言えなかった。
「アカウントに基づく情報を利用するには、VoiceMatchの設定を確認してください」とエラーが出るのだ。それも連続で。何度も。ひどい。
※当然VoiceMatchの再認識は幾度となく試している
これは「あなたの声かどうかわからないので、あなたのリストは編集できません」という意味である。しかし自分のユースケースでは個人認識をしなくていいPublicなリスト操作がほしいだけだった。
こうなると個人認識必須というのはふつうに不便なだけの作りだし、IFTTT時代はPublicにリスト操作できていたことを考えれば純粋な退化でしかない。
今後だが、音声での買い物リスト追加用にAlexaスキルでも開発してみようかと考えている。家にちょうど眠っている貰い物のEcho Dotがあるので。
うまくいったらここか、Qiitaに書くかもしれない。どっちに書くかは気分で決める。と思う。
コメント