はじめに
リモートワークによりZoom会議が増え、参加者を記録して残すことが増えてきました。
今までは会議にAcceptしたりした人をカウントしたり、会議参加者リストをスクリーンショットしたりして参加者を取得していましたが、書き漏らしたり、スクリーンショットを忘れていたりといったことが起きていました。
Zoom自体に会議参加者をエクスポートする機能はあるのですが、せっかくなのでAPIを叩いて取得できるツールを作ってみようと思い、作り始めました。
構成
ツールを作るに当たり、必要な/やってみたい技術、どんな形にしたいかをまとめてみました。
完成形のツールイメージ
→ Meeting UUIDをチャットツールから送信するだけで参加者一覧の名前が返ってくる-
使用する技術と理由
- Slack(チャットツールなら何でも良かったのですが、手元にSlackがあったので)
- Azure Functions(会社でAzureを使っているため個人でも使ってみたかった)
- Go(未体験の言語学習)
- Zoom(参加者取得)
この技術を使ってイメージ図を作成しました。SlackからAzure Functionsへリクエストを飛ばし、カスタムハンドラーのGoへリクエストを渡します。カスタムハンドラー側でZoom APIにMeeting UUIDをつけたリクエストを飛ばし、レスポンスから参加者を取得してSlackに返します。
Azure FunctionsでGoを使う
去年の12月に、Azure Functionsがアップデートされ、 どんな言語でもCustom handlerで使用できるようになりました。これまではAzure Functionsで使用できる言語は限られていたので、このアップデートはすごく助かりました。
Azure Functions in Any Language with Custom Handlers
また、別ブログにて、このCustom HandlerにGoを使ったサンプルも投稿されていました。この記事が私のやりたいことと似ていたので、この記事を基にしてZoomを組み合わせてみることにしました。
How to build a serverless app using Go and Azure Functions
開発環境
Go: go1.15.2 darwin/amd64
Visual Studio Code: 1.52.1
ツールの制限事項
今回作成したツールでは、過去の会議参加者のAPI(GET
/past_meetings/{meetingUUID}/participants)を使用しています。このAPIは有料プランのPro以上でしか呼べないので、本ツールを使う場合、Zoomの料金プラン(Choose a plan)で登録しておく必要があります。
作成手順
ZoomのAPI使用設定
ZoomのApp Marketplaceで、JWT認証によるアプリを作成します。このアプリ作成には以下記事を基に作成しました。
Zoom App Marketplace
Zoom APIの使い方:OAuth・JWT による認証方法・Postman での実行方法
Zoom APIを試してみる JWT編
作成した結果がこちらです。今回はここに記載されているAPI KeyとAPI Secretを、後述する環境変数にセットして使用します。
コード
コードを書く準備として、ローカル環境構築をしておきます。この構築では、MicrosoftがGoのクイックスタートガイドを公開しているので、それに沿って構築してください。
Quickstart: Create a Go or Rust function in Azure using Visual Studio Code
今回作成したコードはMeetingParticipantsExporterです。以降、このコードについて説明します。
HTTPServerの起動
mainでHTTP Serverを起動させておきます。これでFunctions HostからCustom Handlerに対して、/api/zoom
でリクエストを送信できるようになります。
func main() {
port, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT")
if !exists {
port = "7071"
}
http.HandleFunc("/api/zoom", function.ExportParticipants)
fmt.Println("Go server listening on port", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
Slackからのリクエストを検証
Verifying requests from Slackにて、Slackからのリクエスト検証方法が記載されています。この検証法と、参考にしたHow to build a serverless app using Go and Azure Functionsを基に、ヘッダのシグネチャやタイムスタンプを使用して、Slackからのリクエストかどうかを判定します。
Zoom APIの使用
JWT認証用のトークン作成
Zoom APIを使用するにはJWT認証用のトークンを作成する必要があります。そのトークンをを作成するために、goで書かれているjwtを使用することにしました。このjwtでは、API KeyやSecret Keyが必要だったので、
「ZoomのAPI使用設定」項目で取得したAPI Key、API Secretを、それぞれAPIKEY、SECRETKEYとして環境変数にセットして使用しています。
以下、トークン作成コードです。
import (
・・・
"github.com/cristalhq/jwt"
・・・
)
type Env struct {
SecretKey string `required:"true"`
ApiKey string `required:"true"`
}
・・・
func ExportParticipants(w http.ResponseWriter, r *http.Request) {
・・・
key := []byte(goenv.SecretKey)
signer, _ := jwt.NewSignerHS(jwt.HS256, key)
// tokenは作成後1分間有効
now := time.Now()
after := now.Add(time.Minute)
claims := jwt.RegisteredClaims{
Issuer: goenv.ApiKey,
ExpiresAt: jwt.NewNumericDate(after),
}
builder := jwt.NewBuilder(signer)
token, _ := builder.Build(claims)
これでZoom APIに使用するトークンを作成できました。
会議参加者のAPIへリクエスト・レスポンス
会議参加者のAPIへリクエストを送ります。使用するAPIの仕様(GET /past_meetings/{meetingUUID}/participants)を見ると、
URL: https://api.zoom.us/v2/past_meetings/{meetingUUID}/participants
Header: Authorization: "Bearer " + JWTアクセストークン
で送る必要があります。
また、レスポンスは以下のような形式となっているため、participants
内のname
を取得するようにします。
{
"page_count": 1,
"page_size": 30,
"total_records": 1,
"next_page_token": "aliqua",
"participants": [
{
"id": "8b29rgg4bb",
"name": "Ram Shekhar",
"user_email": "ram.shekhar.123@fkdngfjg.fdghdfgj"
}
]
}
このURL生成、リクエスト、レスポンスからの参加者取得は以下のとおりです。
var tokenStr string
tokenStr = token.String()
meetingParticipantsUrl := "https://api.zoom.us/v2/past_meetings/"
url := meetingParticipantsUrl + meetingUUID + "/participants"
req, _ := http.NewRequest("GET", url, nil)
bearerAccessToken := "Bearer " + tokenStr
req.Header.Set("Authorization", bearerAccessToken)
client := new(http.Client)
resp, _ := client.Do(req)
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Printf("Response body: %s", body)
if err != nil {
fmt.Printf("Error ocurred while executing ReadAll. %s", err)
log.Fatal(err)
return
}
//Unmarshall json
var meetingParticipantsResponseBody MeetingParticipantsResponseBody
if err := json.Unmarshal(body, &meetingParticipantsResponseBody); err != nil {
fmt.Printf("Error ocurred while unmarshal body. %s", err)
log.Fatal(err)
return
}
fmt.Println("Participant")
participantNames := ""
for _, participant := range meetingParticipantsResponseBody.Participants {
fmt.Printf("%s\n", participant.Name)
participantNames += participant.Name
participantNames += "\n"
}
Slackへレスポンス
Slackへは参加者のテキストのみを返すようにします。
type SlackResponse struct {
Text string `json:"text"`
}
・・・
slackResponse := SlackResponse{Text: participantNames}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(slackResponse)
Azure上での動作
Azure Functionsでアプリ作成
Azure Portal上で関数アプリを選択し作成していきます。
リソースグループ、関数アプリ名には適当な名前をセットします。ランタイムスタックやバージョンでは、Custom Handlerを使うようにしておきます。
OSはLinuxを使うようにしておき、プランにはサーバーレスを選択します。
ここまでの設定に問題がなければ、エラーメッセージが出ず、作成ボタンを押すことができます。
Azure Functionsにコードをアップロード
ローカル環境のコードをLinux/x64環境用にビルドしておきます。
> GOOS=linux GOARCH=amd64 go build -o go_main cmd/main.go
このコードをVSCodeからアップする方法もありますが、ここではコマンドでアップする方法を実行します。MicrosoftのAzure Functions Core Tools の操作を参照して、Azure Functions Core Toolsをインストールします。
インストール完了後、func azure functionapp publish アプリ名 --custom
を実行することでAzure Functionsにコードをアップロードできます。
ここでのアプリ名はAzure Functionsの関数アプリで作成したアプリ名となります。
❯ func azure functionapp publish MeetingParticipantsExporter --custom
Getting site publishing info...
Uploading package...
Uploading 7.72 MB [###########################################################]
Upload completed successfully.
Deployment completed successfully.
Syncing triggers...
Functions in MeetingParticipantsExporter:
zoom - [httpTrigger]
Invoke url: https://meetingparticipantsexporter.azurewebsites.net/api/zoom?code=xxxxx
これでAzure Functionsへのコードアップロードは完了です。
アップロード後のInvoke url: https://meetingparticipantsexporter.azurewebsites.net/api/zoom?code=xxxxx
は、後述するSlackのAPI使用設定で使用するため、控えておいてください。
SlackのAPI使用設定
slack apiのサイトから、Create New Appsを選択し、任意のアプリ名を入力します。
アプリ作成後、Slash Commandsを選択し、新しいコマンドを作成します。
コマンドの作成ではSlackからどのように呼び出すかを入力していきます。
/zoom meetinguuid
と呼び出したいので、コマンドには/zoom
とします。またRequest URLは、先程控えていたURLを入力します。
コマンドの作成が終わったら、Install to Workspaceボタンを押して、作成したアプリをワークスペースにインストールします。
インストールが終わったら、App Credentialsにある、Sigining Secretを控えておきます。
環境変数設定
Azure Functionsの構成項目で、ZoomのAPI Key、Secret Key、SlackのSigining SecretをそれぞれAPIKEY、SECRETKEY、SLACK_SIGNING_SECRETの環境変数としてセットします。
環境変数セット後、Azure Functionsのアプリを再起動しておきます。
これで準備は整いました。
結果
Slackから/zoom meetinguuidを送ると、参加者のリストが返されます。
おわりに
各技術を使って、Zoomの参加者を取得するツールを作成しました。触ったことのない技術を独学で使っていくのは大変でしたが、一歩ずつ前に進める経験は結構楽しいですね。
ツールを作るにあたり、本記事に掲載した記事の作者の方々、Twitterでアドバイスを頂いた、@nthonyChuさん、@u_phyさん、ありがとうございました。
この記事が誰かのお役に立てれば幸いです。