リモート会議を開催するとき、アプリケーションのZoomを使われてる人も多いと思います。
Zoomには、Pythonによってプログラミング可能なWeb-APIであるREST APIが用意されています。
このAPIを使えば、リモート会議を組み込んだアプリケーションを開発することができます。
Zoom REST APIは、REST APIのため、HTTPプロトコルのリクエスト・レスポンスを使っています。
専用のAPIライブラリなどは、提供されていません。したがって、プログラミング環境で、HTTPリクエスト・レスポンスを送受信できれば、使うことができます。
Zoom REST APIには、会議だけでなく、ウェビナー(セミナー)、チャットなどZoomで使える機能が一通り、揃っています。
※2023年9月1日より、JWTアプリケーションは未サポートとなるようです。
JWTアプリケーションを使用している方には、以下のようにServer-To-Server OAuthなどの使用を
促されるメールがきているはずです。
■ Zoom REST APIを使うための準備
Zoom REST APIを使うためには、まず、以下を実施する必要があります。
1)Zoomにアカウント登録する
Zoomの無料のアカウントを作成します。誕生日(意味不明)、メールアドレスを入力して作成ます。
2)開発者用のデベロッパーサイトにログインする
Zoomのデベロッパーサイトから、1)で作成したアカウントでサインインします。
3)Server-To-Server OAuthアプリケーションを作成する
Zoomサーバへのリクエストを認証するために、トークンを使います。
そのため、Server-To-Server OAuthアプリケーションを作成しておく必要があります。
右上のプルダウンから「Build App」を選択し、アプリケーションタイプのServer-To-Server OAuthの「Create」をクリックし、アプリケーション名を入力し、作成します。
作成後、以下のようにREST APIで使うAPIキー(Client ID)とAPI秘密鍵(Client Secret)が生成されます。これらをメモしておきます。
■ 会議機能を使う
主な会議機能は次の通りです。
・リモート会議を生成する
・リモート会議招待テキストを取得する
・リモート会議情報を取得する
・リモート会議全リストを取得する
(リモート会議の存在をチェックする)
・リモート会議を削除する
Pythonによるコード例を以下に示します。
# -*- coding: utf-8 -*-
import requests
import json
import datetime
from dateutil import tz
import dateutil.parser
import jwt
import time
import random
from pprint import pprint
from tzlocal import get_localzone
from requests.auth import HTTPBasicAuth
#
# Meetingクラス
#
class MeetingClass:
_token = None
_meeting_id = None
_meeting_status = None
_meeting_uuid = None
_meeting_json = None
CONST_ZOOM_URL = 'https://api.zoom.us/v2' # Zoom APIのURL
CONST_ZOOM_API_KEY = 'XXXXXXXXXXXXXXXXXXXXXX' # API key(Server-to-Server OAuth : client_id)
CONST_ZOOM_API_SECRET = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' # API secret(Server-to-Server OAuth : client_secret)
CONST_ZOOM_USER_ID = 'user@test.org' # 主催者のメールアドレス
CONST_ZOOM_TOKEN_EXPIRE_SEC = 600 # トークン有効期限:600秒
# トークン取得メソッド
def _GetToken(self):
# endpoint to get invitationn text
end_point = f"https://zoom.us/oauth/token"
# ベーシック認証設定(client id と Client Secretを設定)
auth=HTTPBasicAuth(self.CONST_ZOOM_API_KEY, self.CONST_ZOOM_API_SECRET)
# ヘッダ生成
headers = {'Host': 'zoom.us',
'Content-Type' : 'application/x-www-form-urlencoded'}
data = 'grant_type=account_credentials'
data += '&'
data += 'account_id=8elTaiRVThOVdCsoL6AMPA'
try:
# リクエスト送信(生成:POST)
response = requests.request('POST',
end_point,
headers=headers,
data=data,
auth=auth)
# 次の形式のレスポンスのテキストをJSON形式に変換
#{
# “Access_token“: String,
# “Token_type”: “bearer”,
# “Expire_in”: long,
# “scope” : [String]
#}
token_data = json.loads(response.text)
self._token = token_data['access_token']
return self._token
except Exception as e:
print(e)
return None
# デフォルト設定JSON生成メソッド
def _CreateDefaultSettings(self,contact_name:str,contact_email:str):
return {
'host_video': True, # start video when the host joins
'participant_video': True, # start video when the participants join
'join_before_host': True, # no host
'jbh_time': 0, # Allow the participant to join the meeting at anytime.(デフォルトっぽい)
'mute_upon_entry': True, # mute paticipants upon entry
'approval_type': 0, # automatically approve
'cn_meeting': False, # host meeting in china
'in_meeting': False, # host meeting in india
'watermark': False, # add a watermark when viewing a shared screen
'use_pmi': False, # personal meeting id
'registration_type': 1, # The meeting's registration type
'audio': "voip", # How participants join the audio portion of the meeting
'auto_recording': "none", # The automatic recording settings
'enforce_login': False, # enfoce login
'waiting_room': False, # Whether to enable the Waiting Room feature if this values true,this disables the join_before_hosts setting
'registrants_email_notification': False, # Whether to sedn registants email notifications abount their registration approval cancellation or rejection
'meeting_authentication': False, # if true ,only authenticated users can join the meeting
'contact_name': contact_name, # The contact name for meeting registration
'contact_email': contact_email} # The contact email for meeting registration
# リクエストヘッダ生成メソッド
def _GetHeaders(self,expire_sec: int):
# トークン取得
token=self._GetToken()
if token is None:
return None
# トークンを設定し、ヘッダ(JSON)作成
headers = {'Authorization': f"Bearer {self._token}",
'Content-Type': 'application/json'}
return headers
#
# リモート会議生成メソッド
# topic
# start_time
# duration_min
# pass_code
# contact_name
# contact_email
#
def CreateMeeting(self, topic: str, start_time: datetime, duration_min: int,pass_code: str,contact_name:str,contact_email:str):
# リモート会議作成用のURLを生成
end_point = f'/users/{self.CONST_ZOOM_USER_ID}/meetings'
# トークンのタイムアウト値を指定し、リクエストヘッダを生成
headers = self._GetHeaders(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)
if headers is None:
return None
# パスコードが未指定なら自動生成する
if not pass_code:
pass_code = str(random.randint(100000, 999999))
# 会議のデフォルトパラメータを設定
default_setting = self._CreateDefaultSettings(contact_name,contact_email)
# リクエストのボディ部を生成
body = {
"topic": topic, # 会議の題名(Invitationに含まれる)
'type': 2, # scheduled meeting
"start_time": start_time.isoformat(),
"duration": duration_min,
"timezone": "Osaka, Sapporo, Tokyo",
"password": pass_code,
#"agenda": agenda,
"settings": default_setting}
try:
# リクエスト送信(生成:POST)
# This API has a daily rate limit of 100 requests per day.
# Therefore, only 100 Create a Meeting API requests are permitted within a 24 hour window for a user.
response = requests.request('POST',
self.CONST_ZOOM_URL + end_point,
headers=headers,
data=json.dumps(body)).json()
# レスポンス(JSON)からデータ取得
self._meeting_id = response['id'] # meeting id
self._status = response['status'] # 会議の状態
self._uuid = response['uuid'] # uuid
self._meeting_json = response # レスポンス全体
return self._meeting_id
except Exception as e:
print(e)
return None
#
# リモート会議招待用テキスト取得メソッド
#
def GetInvitation(self, meeting_id: int):
# リモート会議招待用テキスト取得のURLを生成
end_point = f"/meetings/{meeting_id}/invitation"
# トークンのタイムアウト値を指定し、リクエストヘッダを生成
headers = self._GetHeaders(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)
if headers is None:
return None
# リクエスト送信(取得:GET)
invitation = requests.request("GET",
self.CONST_ZOOM_URL + end_point,
headers=headers).json()
return invitation
#
# リモート会議情報取得メソッド
#
def GetMeeting(self, meeting_id: int):
# リモート会議情報取得のURLを生成
end_point = f"/meetings/{meeting_id}"
# トークンのタイムアウト値を指定し、リクエストヘッダを生成
headers = self._GetHeaders(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)
if headers is None:
return None
# リクエスト送信(取得:GET)
return requests.request("GET",
self.CONST_ZOOM_URL + end_point,
headers=headers).json()
#
# リモート会議更新メソッド
#
def UpdateMeeting(self, meeting_id: int,topic: str, start_time: datetime, duration_min: int,pass_code: str,contact_name:str,contact_email:str):
# リモート会議更新用のURLを生成
end_point = f"/meetings/{meeting_id}"
# トークンのタイムアウト値を指定し、リクエストヘッダを生成
headers = self._GetHeaders(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)
if headers is None:
return False
# パスコードが未指定なら自動生成する
if not pass_code:
pass_code = str(random.randint(100000, 999999))
# 会議のデフォルトパラメータを設定
default_setting = self._CreateDefaultSettings(contact_name,contact_email)
# リクエストのボディ部を生成
body = {
"topic": topic,
'type': 2, # scheduled meeting
"start_time": start_time.isoformat(),
"duration": duration_min,
"timezone": "Osaka, Sapporo, Tokyo",
"password": pass_code,
"settings": default_setting}
try:
# リクエスト送信(更新:PATCH)
response = requests.request('PATCH',
self.CONST_ZOOM_URL + end_point,
headers=headers,
data=json.dumps(body))
# 正常(status code : 204)
if response.status_code == 204:
return True
return False
except Exception as e:
print(e)
return False
#
# リモート会議削除メソッド
#
def DeleteMeeting(self, meeting_id: int):
# リモート会議削除用のURLを生成
end_point = f"/meetings/{meeting_id}"
# トークンのタイムアウト値を指定し、リクエストヘッダを生成
headers = self._GetHeaders(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)
if headers is None:
return False
try:
# リクエスト送信(削除:DELETE)
response = requests.request('DELETE',
self.CONST_ZOOM_URL + end_point,
headers=headers)
# 正常(status code : 204)
if response.status_code == 204:
return True
return False
except Exception as e:
print(e)
return False
#
# リモート会議リスト取得メソッド
#
def GetMeetingList(self,next_page_token,page_number):
# リモート会議リスト取得用のURLを生成
end_point = f'/users/{self.CONST_ZOOM_USER_ID}/meetings'
# 取得するタイプに”スケジュール”、1ページ当たりの取得件数を100件で指定
params = '?type=scheduled&page_size=100'
#params = '?type=scheduled&page_size=1'
# 次ページの取得の場合、次ページ用トークンとページ番号を指定
if len(next_page_token) > 0:
params += '&next_page_token='+ next_page_token
if len(page_number) > 0:
params += '&page_number='+ page_number
# トークンのタイムアウト値を指定し、リクエストヘッダを生成
headers = self._GetHeaders(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)
if headers is None:
return None
# リクエスト送信(取得:GET)
return requests.request("GET",
self.CONST_ZOOM_URL + end_point + params,
headers=headers).json()
#
# リモート会議リストを全件取得するメソッド
#
def GetMeetingListAll(self):
meetings=[]
next_page_token = ''
page_number = 1
# リモート会議リスト取得
response =self.GetMeetingList('','')
# レスポンスのページ番号を取得
if 'page_number' in response and response['page_number'] != None:
page_number=int(response['page_number'])
# 次ページ用トークンを取得
next_page_token=response['next_page_token']
# 取得した会議情報を保存
meetings.extend(response['meetings'])
# 全ページ分繰り返し
while(next_page_token != None and len(next_page_token) > 0 ):
# リモート会議リスト取得
response =self.GetMeetingList(next_page_token,str(page_number))
# レスポンスのページ番号を取得
if 'page_number' in response and response['page_number'] != None:
page_number=int(response['page_number'])
#else:
# page_number += 1
# 次ページ用トークンを取得
next_page_token=response['next_page_token']
# 取得した会議情報を保存
meetings.extend(response['meetings'])
return meetings
#
# リモート会議リストから該当時間の会議があるかチェックするメソッド
#
def IsExistMeeting(self,meeting_list,start_time,duration):
# 終わりの時間にする
end_time = start_time + datetime.timedelta(minutes=duration)
for scheduled_meeting in meeting_list:
# リモート会議情報のタイムゾーンを取得
scheduled_timezone = tz.gettz(scheduled_meeting['timezone'])
# リモート会議情報の開始時間をタイムゾーン付きの時間に変換する
scheduled_start_time = dateutil.parser.parse(scheduled_meeting['start_time']).astimezone(scheduled_timezone)
# 終わりの時間を取得
scheduled_end_time = scheduled_start_time + datetime.timedelta(minutes=scheduled_meeting['duration'])
# 該当時間の会議があるかチェック
if start_time >= scheduled_start_time or end_time <= scheduled_end_time :
return True
return False
if __name__ == '__main__':
client = MeetingClass()
#
# リモート会議を生成する
#
# 開始・終了時間 --> 開始時間、会議時間(分)に変換
day = '2022/6/6'
start_time = '16:30'
end_time = '17:30'
mtg_start_time = datetime.datetime.strptime(day + ' ' + start_time, '%Y/%m/%d %H:%M')
mtg_end_time = datetime.datetime.strptime(day + ' ' + end_time, '%Y/%m/%d %H:%M')
duration_min = int((mtg_end_time - mtg_start_time).seconds / 60)
# 開始時間にTimeZone付与
ja = get_localzone()
mtg_start_time = mtg_start_time.astimezone(ja)
# リモート会議生成
mtg_id = client.CreateMeeting(topic='api test',
start_time=mtg_start_time,
duration_min=duration_min,
pass_code = 'test',
contact_name = 'icebreak',
contact_email = 'icebreak_mt1@itresourcetech.net')
#
# リモート会議招待テキストを取得する
#
invitation = client.GetInvitation(mtg_id)
pprint(invitation)
#
# リモート会議情報を取得する
#
meeting = client.GetMeeting(mtg_id)
pprint(meeting)
#
# リモート会議全リストを取得する
#
meeting_list=client.GetMeetingListAll()
pprint(meeting_list)
print(len(meeting_list))
#
# リモート会議の存在をチェックする
#
JST = tz.gettz('Asia/Tokyo')
start_time=datetime.datetime(2022,6,6,16,30,tzinfo=JST)
result= client.IsExistMeeting(meeting_list,start_time,60)
print(result)
#
# リモート会議を削除する
#
result = client.DeleteMeeting(mtg_id)
ポイントは、以下の通りです。
1)HTTPリクエストのヘッダに、取得したトークンを設定する
APIキー(Client ID)とAPI秘密鍵(Client Secret)を使って、所定の形式のトークンを取得し、HTTPリクエストのヘッダに設定する必要があります。
CONST_ZOOM_URL = 'https://api.zoom.us/v2' # Zoom APIのURL
CONST_ZOOM_API_KEY = 'XXXXXXXXXXXXXXXXXXXXXX' # API key(Server-to-Server OAuth : client_id)
CONST_ZOOM_API_SECRET = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' # API secret(Server-to-Server OAuth : client_secret)
CONST_ZOOM_USER_ID = 'user@test.org' # 主催者のメールアドレス
CONST_ZOOM_TOKEN_EXPIRE_SEC = 600 # トークン有効期限:600秒
# トークン取得メソッド
def _GetToken(self):
# endpoint to get invitationn text
end_point = f"https://zoom.us/oauth/token"
# ベーシック認証設定(client id と client secretを設定)
auth=HTTPBasicAuth(self.CONST_ZOOM_API_KEY, self.CONST_ZOOM_API_SECRET)
# ヘッダ生成
headers = {'Host': 'zoom.us',
'Content-Type' : 'application/x-www-form-urlencoded'}
data = 'grant_type=account_credentials'
data += '&'
data += 'account_id=8elTaiRVThOVdCsoL6AMPA'
try:
# リクエスト送信(生成:POST)
response = requests.request('POST',
end_point,
headers=headers,
data=data,
auth=auth)
# 次の形式のレスポンスのテキストをJSON形式に変換
#{
# “Access_token“: String,
# “Token_type”: “bearer”,
# “Expire_in”: long,
# “scope” : [String]
#}
token_data = json.loads(response.text)
self._token = token_data['access_token']
return self._token
except Exception as e:
print(e)
return None
# リクエストヘッダ生成メソッド
def _GetHeaders(self,expire_sec: int):
# トークン取得
token=self._GetToken()
if token is None:
return None
# トークンを設定し、ヘッダ(JSON)作成
headers = {'Authorization': f"Bearer {self._token}",
'Content-Type': 'application/json'}
return headers
2)会議時間には、タイムゾーンを指定する
リモート会議なので、参加者がどこにいてもインターネットさえつながれば会議できます。そのため、リモート会議を生成する際、会議時間は必ずタイムゾーンを指定する必要があります。
#
# リモート会議を生成する
#
# 開始・終了時間 --> 開始時間、会議時間(分)に変換
day = '2022/6/6'
start_time = '16:30'
end_time = '17:30'
mtg_start_time = datetime.datetime.strptime(day + ' ' + start_time, '%Y/%m/%d %H:%M')
mtg_end_time = datetime.datetime.strptime(day + ' ' + end_time, '%Y/%m/%d %H:%M')
duration_min = int((mtg_end_time - mtg_start_time).seconds / 60)
# 開始時間にTimeZone付与
ja = get_localzone()
mtg_start_time = mtg_start_time.astimezone(ja)
# リモート会議生成
mtg_id = client.CreateMeeting(topic='api test',
start_time=mtg_start_time,
duration_min=duration_min,
pass_code = 'test',
contact_name = 'icebreak',
contact_email = 'icebreak_mt1@itresourcetech.net')
3)APIからリモート会議生成時に設定できるオプションと自アカウントで事前設定するオプションがある
以下のようなオプションがAPIから設定できます。
# デフォルト設定JSON生成メソッド
def _CreateDefaultSettings(self,contact_name:str,contact_email:str):
return {
'host_video': True, # start video when the host joins
'participant_video': True, # start video when the participants join
'join_before_host': True, # no host
'jbh_time': 0, # Allow the participant to join the meeting at anytime.(デフォルトっぽい)
'mute_upon_entry': True, # mute paticipants upon entry
'approval_type': 0, # automatically approve
'cn_meeting': False, # host meeting in china
'in_meeting': False, # host meeting in india
'watermark': False, # add a watermark when viewing a shared screen
'use_pmi': False, # personal meeting id
'registration_type': 1, # The meeting's registration type
'audio': "voip", # How participants join the audio portion of the meeting
'auto_recording': "none", # The automatic recording settings
'enforce_login': False, # enfoce login
'waiting_room': False, # Whether to enable the Waiting Room feature if this values true,this disables the join_before_hosts setting
'registrants_email_notification': False, # Whether to sedn registants email notifications abount their registration approval cancellation or rejection
'meeting_authentication': False, # if true ,only authenticated users can join the meeting
'contact_name': contact_name, # The contact name for meeting registration
'contact_email': contact_email} # The contact email for meeting registration
他にも、参加者に画面共有を自動許可する設定などは、自アカウントの設定画面から事前に設定する必要があります。
4)Zoom REST APIの定義に従って、URLとHTTPのメソッドを設定する。
機能ごとにURLを使い分けます。その際、自アカウントのメールアドレスを指定する必要があります。
REST APIに沿って、使う機能毎にGET/POST/PATCH/DELETEのメソッドを指定する必要があります。
#
# リモート会議生成メソッド
#
def CreateMeeting(self, topic: str, start_time: datetime, duration_min: int,pass_code: str,contact_name:str,contact_email:str):
# リモート会議作成用のURLを生成
end_point = f'/users/{self.CONST_ZOOM_USER_ID}/meetings'
# トークンのタイムアウト値を指定し、リクエストヘッダを生成
headers = self._GetHeaders(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)
# パスコードが未指定なら自動生成する
if not pass_code:
pass_code = str(random.randint(100000, 999999))
# 会議のデフォルトパラメータを設定
default_setting = self._CreateDefaultSettings(contact_name,contact_email)
# リクエストのボディ部を生成
body = {
"topic": topic, # 会議の題名(Invitationに含まれる)
'type': 2, # scheduled meeting
"start_time": start_time.isoformat(),
"duration": duration_min,
"timezone": "Osaka, Sapporo, Tokyo",
"password": pass_code,
#"agenda": agenda,
"settings": default_setting}
try:
# リクエスト送信(生成:POST)
# This API has a daily rate limit of 100 requests per day.
# Therefore, only 100 Create a Meeting API requests are permitted within a 24 hour window for a user.
response = requests.request('POST',
self.CONST_ZOOM_URL + end_point,
headers=headers,
data=json.dumps(body)).json()
# レスポンス(JSON)からデータ取得
self._meeting_id = response['id'] # meeting id
self._status = response['status'] # 会議の状態
self._uuid = response['uuid'] # uuid
self._meeting_json = response # レスポンス全体
return self._meeting_id
except Exception as e:
print(e)
return None
5)すでに登録済の会議リストの複数ページ取得には、次ページ番号と次ページトークンを指定する
登録した会議がたくさんある場合、APIを使って会議リストを取得すると、次ページ用のページ番号とトークンが返却されます。次ページを取得する場合、これらを指定して取得する必要があります。
#
# リモート会議リスト取得メソッド
#
def GetMeetingList(self,next_page_token,page_number):
# リモート会議リスト取得用のURLを生成
end_point = f'/users/{self.CONST_ZOOM_USER_ID}/meetings'
# 取得するタイプに”スケジュール”、1ページ当たりの取得件数を100件で指定
params = '?type=scheduled&page_size=100'
#params = '?type=scheduled&page_size=1'
# 次ページの取得の場合、次ページ用トークンとページ番号を指定
if len(next_page_token) > 0:
params += '&next_page_token='+ next_page_token
if len(page_number) > 0:
params += '&page_number='+ page_number
# トークンのタイムアウト値を指定し、リクエストヘッダを生成
headers = self._GetHeaders(self.CONST_ZOOM_TOKEN_EXPIRE_SEC)
# リクエスト送信(取得:GET)
return requests.request("GET",
self.CONST_ZOOM_URL + end_point + params,
headers=headers).json()
#
# リモート会議リストを全件取得するメソッド
#
def GetMeetingListAll(self):
meetings=[]
next_page_token = ''
page_number = 1
# リモート会議リスト取得
response =self.GetMeetingList('','')
# レスポンスのページ番号を取得
if 'page_number' in response and response['page_number'] != None:
page_number=int(response['page_number'])
# 次ページ用トークンを取得
next_page_token=response['next_page_token']
# 取得した会議情報を保存
meetings.extend(response['meetings'])
# 全ページ分繰り返し
while(next_page_token != None and len(next_page_token) > 0 ):
# リモート会議リスト取得
response =self.GetMeetingList(next_page_token,str(page_number))
# レスポンスのページ番号を取得
if 'page_number' in response and response['page_number'] != None:
page_number=int(response['page_number'])
#else:
# page_number += 1
# 次ページ用トークンを取得
next_page_token=response['next_page_token']
# 取得した会議情報を保存
meetings.extend(response['meetings'])
return meetings
6)WebHook機能を使うには、アカウントから事前にURLを指定しておく
WebHook機能とは、リモート会議生成・開始・終了・削除などが発生した際にZoomサーバから自分のサーバのURLを呼び出してくれる機能です。
いわゆるコールバックですが、あらかじめアカウントからURLを設定しておく必要があります。また、呼び出してほしいイベントを指定しておく必要があります。
■ まとめ
一つのアカウントで同じ時間帯でダブルブッキングできません。同じ時間帯で会議を設定するには、複数のアカウントが必要になります。(有料アカウントでは、一つのアカウントで、「ホストを追加(有料)」することで対応できるようです)
ソフトウェア開発・システム開発業務/セキュリティ関連業務/ネットワーク関連業務/最新技術に関する業務など、「学習力×発想力×達成力×熱意」で技術開発の実現をサポート。お気軽にお問合せ下さい