LoginSignup
4
0

More than 3 years have passed since last update.

Azure FunctionsとGoでZoom会議参加者を取得する

Last updated at Posted at 2021-01-22

はじめに

リモートワークにより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さん、ありがとうございました。

この記事が誰かのお役に立てれば幸いです。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0