はじめに
こちらは、完走賞ゲットのため小ネタを毎日投稿しようとチャレンジする Advent Calendar 2022 の 23日目の記事です。
前日に公開した 2つの記事のうちの 1つ、Zoom の API / SDK を使ってみよう! by Zoom Advent Calendar 2022 の 22日目の記事(※ 以下の内容)を書いた際の裏話です。
●【Zoom 2022】Zoom の会議開始を LINEアプリにお知らせする仕組みを Zoom Webhook と enebular で作る(Webhook のバリデーション対応も行いつつ) - Qiita
https://qiita.com/youtoy/items/8d2aa5257c0f4e9e3cc7
その記事で当初やろうとしていたのが、「Zoom Webhook を IFTTT と組み合わせる話」だったのですが、それがうまくいかなかった原因について、記録を残すためのものです。
(それと、IFTTT の設定手順の部分を、後で再利用するための記録としても)
今回の結論
結論から書くと、Zoom Webhook に今年の秋ごろに追加された、バリデーションの仕組みに IFTTT では対応できない、というのが原因でした(※ 詳細はこの後で)。
公式ドキュメントでいうと、「Using webhooks のページの Validate your webhook endpoint の部分」に書かれている、以下の赤背景の説明の内容です。
この後の流れ
この後の記事の内容ですが、「当初、IFTTT連携の記事を書いていたもの」を記載して、その内容の失敗をした部分から、「Zoom Webhook と IFTTT が連携できなかった原因の話」とつなげていこうと思います。
【当初、用意していた記事の内容】 全体構成と動作している様子
全体構成
今回の仕組みの全体構成は、以下のとおりです。
Zoom --(Webhook)--> IFTTT --(API連携)--> スマート電球
Zoom の Webhook を、様々なサービス間をつなぐのに便利な「IFTTT」を使って受け、それを IFTTT連携ができるスマート電球につなぐ形です。
動作している様子
※ IFTTT連携ができなかったことにより、上で書いた構成では実現できず ⇒ 動画なし
【当初、用意していた記事の内容】 実装について
ここから、実装について解説していきます。
IFTTT の準備
まず、Zoom の設定をする前に、IFTTT で Zoom からの Webhook を受ける部分を用意します。
https://ifttt.com/ にアクセスして「Create」ボタンを押します。
IFTTT のトリガーの設定
サービスを選ぶページでは、テキスト入力欄に「webh」まで入力してフィルタすると、「Webhooks」が残りますので、これを選びます。
そして、以下の画面では「Receive a web request」を選びます。
次の画面でイベント名を入力します。
このイベント名は、Webhook を受ける URL に使われる文字列になるのですが、今回は「zoom_webhook」という名称にしました。
IFTTT のアクションの設定
ここでは、Webhook を受けとった時に動作させる処理を設定していきます。
今回、自分が IFTTT とアカウント連携をさせていて、連携動作が可能になっている「TP-Link Tapo」を使うことにしました(※ ここでは、アカウント連携などまで説明すると長くなりすぎるので、そこは割愛します)。テキスト入力欄に「tapo」と入力すると、TP-Link Tapo がフィルタされて表示されるので、それを選びます。
ここで、複数の動作を選ぶことができますが、TP-Link Tapo のアプリ側で挙動の詳細を制御できる「ショートカット」を使ったものにします(※ これも、TP-Link Tapoアプリ側の説明などまで加えると、説明が長くなるので省略)。
なお余談ですが、ここで選んだ「IFTTT の TP-Link Tapo のショートカットを使った動作」は、つい最近、以下の記事を書いた時にも利用したものです。
●【VUI 2022】スマート電球 2種を声で制御: Alexa と IFFFT の SwitchBot・TP-Link Tapo の組み合わせ - Qiita
https://qiita.com/youtoy/items/c4c4e374d3b2f524ee6e
以下のように、TP-Link Tapoアプリ側で設定している「電球を点灯させつつ色を自動調整する」という内容で動作させるようにしました。
設定を入力した後に、「Create action」ボタンを押して次に進みます。
そして、以下の画面で「Continue」ボタンを押し、その後の画面で「Finish」ボタンを押して、一連の手順を完了させます。
IFTTT の Webhook用URL を確認
最後に、IFTTT で Webhookを受けるための URL を確認します。
そして、検索用のテキストボックスに「webhook」と入力します。
そうすると、以下のように「IFTTT の Webhooks」が残ります。
その「Webhooks」を選んでください。
その後に表示されるページでは、「Documentation」を選びます。
その後の画面では、Webhook用の URL に関する説明などが書かれています。
その中の、以下の赤矢印で示した部分に、先ほど自分で設定したイベント名を入力します。
上の手順通りに進めていた場合は、「zoom_webhook」になってると思いますので、それを入力しました。そのイベント名を入力して出来上がったものが、今回設定した IFTTT の Webhook用URL となります。
ちなみに、上記のイベント名の入力を行うと、この画面で curlコマンドを使った POSTリクエスト用のコマンドなどの部分も、そのイベント名を含むものに書きかわります。
ここで確認できた Webhook用の URL を、Zoom側での設定に使います。
Zoom Webhook の利用について
Zoom Webhook の使い方については、ありがたいことに以下の記事で丁寧に説明されています。
手順は以下の記事をベースにします。
●はじめての Zoom Webhook - Qiita
https://qiita.com/yosuke-sawamura/items/a45453f1492a43211b96
ここでは、上記の手順と異なる差分のみを記載することにします。
まず、Webhook のトリガーとなるイベントを「Zoomミーティングの開始」にします。
そして、エンドポイントの URL は、上で確認した IFTTT の Webhook用URL を設定します。
このように Zoom側の設定を進めていったのですが、このエンドポイントURL の設定が、うまくいかなくなったところでした。
上記のエンドポイントURL の設定がうまくいかなかった話
上記で赤文字のメッセージが出ていて、そこには「URL validation failed. Try again later.」と書かれています。
そして、「Webhook のバリデーション」という内容について、公式のドキュメントを見てみると、Webhook を受ける側で以下のレスポンスを返すような処理を実装する必要があることが書かれていました。
そして、そのレスポンスを生成する方法は、Node.js を用いた処理で例示されていました。
const crypto = require('crypto')
// Webhook request event type is a challenge-response check
if(request.body.event === 'endpoint.url_validation') {
const hashForValidate = crypto.createHmac('sha256', ZOOM_WEBHOOK_SECRET_TOKEN).update(request.body.payload.plainToken).digest('hex')
response.status(200)
response.json({
"plainToken": request.body.payload.plainToken,
"encryptedToken": hashForValidate
})
}
上記の処理の出力が先ほどのレスポンスの内容になるようです。
そして、この処理の入力になるのが、Zoom からの Webhook に含まれる「plainToken」と、Zoom の Webhook の設定を行う際に出てきていた「Secret Token」 です。
レスポンスとしてで返すべき JSON の内容は、以下となるようです。
- plainToken: Zoom からの Webhook に含まれる「plainToken」そのまま
- encryptedToken: Zoom からの Webhook に含まれる「plainToken」と、Zoom の Webhook の設定に出てきた「Secret Token」を使って、HMAC-SHA-256 を計算(※ 出力は 16進数に)
この対応を行うことで、Zoom の設定でバリデーションが OK となりました。
このような仕様があるため、IFTTT の Webhook のような仕組みだと、バリデーションに関する対応を行うことができず、それにより Zoom の Webhook の利用ができないという状況になりました。