# ZoomAPI授業資料 ## ZoomMarketPlaceにログイン [ZoomMarketplace](https://marketplace.zoom.us/) サインインをしましょう。アカウントを持っていない場合はサインアップ。 右上のDevelopタブをクリックしてBuildAppを選択しましょう。 作りたいアプリケーションのタイプが選択できます。 ユーザー認証機能の実装時間がないので授業ではJWTを使いますが本番のサービスに組み込む場合はOAuthを使いましょう。 他にもチャットボットやSDKとして使うことも可能みたいです。 [公式ドキュメント](https://marketplace.zoom.us/docs/guides) ## JWTでアプリを作成 ### 1.JWTのCreateを選択し流れにそって情報を入力しActivationしましょう。 ``` Create a JWT app :アプリケーション名 Company Name:会社名 Name:ユーザー名 Email address:メールアドレス ``` 必須項目を入力すれば自動的にアクティベーションされます。 ### 2.App credentials ここで自分のAPI keyとAPI Secret keyがここで確認できます。 また、ページ下部にあるView JWT Tokenタブを開くとここで有効期限を設定してJWT Tokenを発行できます。 とりあえず簡単にAPIを使ってみたいのでGASで処理を書いて実行してみましょう。 まずは基本のユーザー取得からいきましょう。 [公式ドキュメント](https://marketplace.zoom.us/docs/api-reference/zoom-api) ## GoogleスプレッドシートからGASを書く ### 1.Googleスプレッドシートを起動します。 今回はリモートで自由参加の授業なので環境依存を限りなく減らすためにGoogleAppSscriptで処理を書きます。基本的にブラウザ上で完結するようにしましたので一緒に手を動かしながらトライしてみましょう。 ### 2.スプレッドシートのツールタブ内のスクリプトエディタをクリック AppsScriptのページが開いているかと思います。ここにコードを書いていきます。 ### 3.デフォルトのmyFunctionを削除して以下のコードを記述 ``` // UserID取得処理 function getZoomUserId() { const request = UrlFetchApp.fetch('https://api.zoom.us/v2/users/', { method: 'GET', contentType: 'application/json', headers: { Authorization: `Bearer 自分のAPIToken}` }, //JWTToken }); const users = JSON.parse(request.getContentText()); console.log(users); } ``` ### 4.実行する関数を選択して実行 するとGoogleからの認証を求められます。今回はテストなので一旦全て許可しましょう。 許可をした上で再度実行をすると実行ログにユーザーデータが表示されているはずです。 ## 今回作ってみるアプリケーション 今回はお問い合わせフォームからユーザーが時間を指定してZoomミーティンルームを作成できる。またそのデータがスプレッドシートに保存されユーザーにはメールが送られてくる形になります。 ### 1. まずはTokenをベタ打ちから自前で発行する処理に変更 [トークンのドキュメント](https://marketplace.zoom.us/docs/guides/auth/jwt) #### ユーザー取得処理をconsoleからreturnに変更。 ``` // UserID取得処理 function getZoomUserId() { const request = UrlFetchApp.fetch('https://api.zoom.us/v2/users/', { method: 'GET', contentType: 'application/json', headers: { Authorization: `Bearer 自分のAPIToken}` }, //JWTToken }); const users = JSON.parse(request.getContentText()); return users.users[0].id;//<-ここ変更!! } ``` #### 次にトークン発行処理を作成 ``` const getZoomAccessToken = () => { const ZOOM_API_KEY = '*****************'; const ZOOM_API_SECRET = '*****************'; //エンコード関数 const encode = (text) => Utilities.base64Encode(text).replace(/=+$/, ''); //Header const header = { alg: 'HS256', typ: 'JWT' }; const encodedHeader = encode(JSON.stringify(header)); //Payload   const payload = { iss: ZOOM_API_KEY, exp: Date.now() + 9000,//トークンの有効時間(ご自由に設定可能)    }; const encodedPayload = encode(JSON.stringify(payload)); //Header.Payload const toSign = `${encodedHeader}.${encodedPayload}`; //Signature const signature = encode( Utilities.computeHmacSha256Signature(toSign, ZOOM_API_SECRET) ); return `${toSign}.${signature}`; }; ``` ### 2.ミーティング作成処理 [ミーティング作成ドキュメント](https://marketplace.zoom.us/docs/api-reference/zoom-api/meetings/meetingcreate) #### ミーティング作成用の関数を作成 ``` //ミーティング作成処理 function createZoomMeeting() { //ミーティングオプション情報 const meetingOptions = { "topic":"サンプルミーティング", "type": "2", // ミーティングのタイプは基本2 "start_time": "2021-09-10T10:00:00:00Z", //開始時刻 "duration": "30", // ミーティングの時間を設定 "timezone": "Asia/Tokyo", // タイムゾーンは日本にしましょう "password": "", // ミーティングにパスワードを設定したい場合は記入 "agenda": "", // 以下細かい設定 "settings": { "host_video": "false", "participant_video": "false", "cn_meeting": "false", "in_meeting": "false", "join_before_host": "false", "mute_upon_entry": "false", "watermark": "false", "use_pmi": "false", "approval_type": "0", "registration_type": "1", "audio": "both", "auto_recording": "local", "enforce_login": "false", "enforce_login_domains": "", "alternative_hosts": "", "global_dial_in_countries": [ "" ], "registrants_email_notification": "false" } }; //APIを呼び出して実行 const request = UrlFetchApp.fetch( `https://api.zoom.us/v2/users/${getZoomUserId()}/meetings`, { method: 'POST', contentType: 'application/json', headers: { Authorization: `Bearer ${getZoomAccessToken()}` }, payload: JSON.stringify(meetingOptions), } ); const result = JSON.parse(request); console.log(result); } ``` ここまで書いたらcreateZoomMeetingを選択して実行するとおそらくmeetingが完成するはずです。 対象のアカウントでログインして確認してみましょう。 meetignが完成していればOKです。 ### 3.メール送信処理 ミーティングルームができたら自動通知メールを送信する処理を作成しましょう。 #### sendMail関数を作成 ``` function sendMail(meeting){ let recipient = "適当なメールアドレス"; let subject = "【お問い合わせありがとうございます】"; let options = { name: "オンライン接客対応室", noReply: true }; let body = "〇〇様\n" + "\n" + "オンラインミーティングのご予約ありがとうございます。" + "\n" + "ミーティングURL: " + meeting['join_url']; GmailApp.sendEmail(recipient,subject,body,options); } ``` ## フォームを作成 ### 1. AppsScriptからファイルを追加しindex.htmlを作成し以下の内容をコピペ ``` <!DOCTYPE html> <html> <head> <base target="_top"> <style> .Form { margin-top: 80px; margin-left: auto; margin-right: auto; max-width: 720px; } @media screen and (max-width: 480px) { .Form { margin-top: 40px; } } .Form-Item { border-top: 1px solid #ddd; padding-top: 24px; padding-bottom: 24px; width: 100%; display: flex; align-items: center; } @media screen and (max-width: 480px) { .Form-Item { padding-left: 14px; padding-right: 14px; padding-top: 16px; padding-bottom: 16px; flex-wrap: wrap; } } .Form-Item:nth-child(5) { border-bottom: 1px solid #ddd; } .Form-Item-Label { width: 100%; max-width: 248px; letter-spacing: 0.05em; font-weight: bold; font-size: 18px; } @media screen and (max-width: 480px) { .Form-Item-Label { max-width: inherit; display: flex; align-items: center; font-size: 15px; } } .Form-Item-Label.isMsg { margin-top: 8px; margin-bottom: auto; } @media screen and (max-width: 480px) { .Form-Item-Label.isMsg { margin-top: 0; } } .Form-Item-Label-Required { border-radius: 6px; margin-right: 8px; padding-top: 8px; padding-bottom: 8px; width: 48px; display: inline-block; text-align: center; background: #5bc8ac; color: #fff; font-size: 14px; } @media screen and (max-width: 480px) { .Form-Item-Label-Required { border-radius: 4px; padding-top: 4px; padding-bottom: 4px; width: 32px; font-size: 10px; } } .Form-Item-Input { border: 1px solid #ddd; border-radius: 6px; margin-left: 40px; padding-left: 1em; padding-right: 1em; height: 48px; flex: 1; width: 100%; max-width: 410px; background: #eaedf2; font-size: 18px; } @media screen and (max-width: 480px) { .Form-Item-Input { margin-left: 0; margin-top: 18px; height: 40px; flex: inherit; font-size: 15px; } } .Form-Item-Textarea { border: 1px solid #ddd; border-radius: 6px; margin-left: 40px; padding-left: 1em; padding-right: 1em; height: 216px; flex: 1; width: 100%; max-width: 410px; background: #eaedf2; font-size: 18px; } @media screen and (max-width: 480px) { .Form-Item-Textarea { margin-top: 18px; margin-left: 0; height: 200px; flex: inherit; font-size: 15px; } } .Form-Btn { border-radius: 6px; margin-top: 32px; margin-left: auto; margin-right: auto; padding-top: 20px; padding-bottom: 20px; width: 280px; display: block; letter-spacing: 0.05em; background: #5bc8ac; color: #fff; font-weight: bold; font-size: 20px; } @media screen and (max-width: 480px) { .Form-Btn { margin-top: 24px; padding-top: 8px; padding-bottom: 8px; width: 160px; font-size: 16px; } } </style> </head> <body> <header> <h1>アンケート</h1> </header> <main> <form class="Form" method="POST" action="アプリケーションのURL"> <div class="Form-Item"> <p class="Form-Item-Label"><span class="Form-Item-Label-Required">必須</span>氏名</p> <input name="user_name" type="text" class="Form-Item-Input" placeholder="例)山田太郎"> </div> <div class="Form-Item"> <p class="Form-Item-Label"><span class="Form-Item-Label-Required">必須</span>メールアドレス</p> <input name="user_email" type="email" class="Form-Item-Input" placeholder="例)example@gmail.com"> </div> <div class="Form-Item"> <p class="Form-Item-Label isMsg"><span class="Form-Item-Label-Required">必須</span>お問い合わせ内容</p> <input type="datetime-local" name="meeting_time" class="Form-Item-Input"> </div> <input type="submit" class="Form-Btn" value="送信する"> </form> </main> </body> </html> ``` ### 2.コード.gsに画面表示処理を追加 一番上の方で ``` //トップページの表示処理 function doGet() { var toppage=HtmlService.createTemplateFromFile("index"); return toppage.evaluate(); } ``` ### 3.記述して保存できたらデプロイ #### 右上のデプロイボタンから新しいデプロイ。 ``` ウェブアプリ 名前:お好きな名前 アクセスできるユーザー:全員 ``` URLが発行されるのでコピーしておいて別タブで開く ### 4.コピーしたURLをindex.htmlのformのアクション先に指定 ### 5.デプロイを管理から更新 ## フォームからデータを受け取ってミーティングを作成 ## 1.doPost関数を作成 ``` function doPost(request) { //データ保存する対象のスプレッドシート取得 var st = SpreadsheetApp.openById("自分のスプレッドシートのID").getSheetByName("シート1"); var USER_NAME=request.parameters.user_name.toString(); var USER_EMAIL=request.parameters.user_email.toString(); var MEETIGN_TIME=request.parameters.meeting_time.toString()+":00"; //ZOOMmettingを作成する関数の呼び出し const meeting = createZoomMeeting(USER_NAME,USER_EMAIL,MEETIGN_TIME); //スプシの最終行取得 var lastRow = st.getLastRow(); //データ保存 st.getRange(lastRow + 1, 1).setValue(USER_NAME); st.getRange(lastRow + 1, 2).setValue(USER_EMAIL); st.getRange(lastRow + 1, 3).setValue(MEETIGN_TIME); st.getRange(lastRow + 1, 4).setValue(meeting['start_url']); //完了画面にリダイレクト var result=HtmlService.createTemplateFromFile("result"); return result.evaluate(); } ``` ## 2. createZoomMeeting関数の引数をアップデートして処理を変更 ``` //ミーティング作成処理 function createZoomMeeting(user_name,user_email,meeting_time) { const topic = user_name + "様との打ち合わせ";//追記!! //ミーティングスキーマ const meetingOptions = { "topic":topic,//変更!! "type": "2", // ミーティングのタイプは基本2 "start_time":meeting_time, //開始時刻 変更!! "duration": "30", // ミーティングの時間を設定 "timezone": "Asia/Tokyo", // タイムゾーンは日本にしましょう "password": "", // ミーティングにパスワードを設定したい場合は記入 "agenda": "", // 以下細かい設定 ``` ## 3.メール送信関数の引数も追加して処理を完成させる ``` //メール関数 function sendMail(meeting,user_email,user_name){ let recipient = user_email;//変更!! let subject = "【お問い合わせありがとうございます】"; let options = { name: "オンライン接客対応室", noReply: true }; let body = user_name +"様\n"//追記!! + "\n" + "オンラインミーティングのご予約ありがとうございます。" + "\n" + "ミーティングURL: " + meeting['join_url']; GmailApp.sendEmail(recipient,subject,body,options); } ``` ## 4.createZoomMeeting関数のリターン直前でメール送信処理を呼び出し ``` ~~~省略~~~ const result = JSON.parse(request); sendMail(result,user_email);//追記!! return result; } ```