zoom-sdk-electron でできることを調べるまでのアレコレ

# 概要

zoomが提供しているElectronのAPIでできそうなことがリファレンスだとちょっとわかりにくいのでデモアプリを起動しようと思ったが、案外ハマりどころが多かったのでそれについて。

## 参考資料

[Zoom Electron SDK 入門
](https://qiita.com/stringthread/items/ef487624ec8508e6401c)

非常にわかりやすかったのですが、具体的なコードの変更内容が例示されていなくて詰まってしまったのでその補足をしたい。当時と若干バージョンが異なる関係で行数のところがずれてしまうので...

記事を読む限り、この方のときはむしろ私が書いているように SDK Key/Secretによる認証が基本だったように見えるのですが...（https://github.com/zoom/zoom-sdk-electron/commit/dbdff5a65dff40c1bccb7a1b269391ea72d8f6b5#diff-3b4cc93c41d4067694a6d9ff6ae923efL643 このコミットで大規模に改修が入ってますね）

※ [CHANGELOG](https://github.com/zoom/zoom-sdk-electron/blob/master/CHANGELOG.md) 見る限りでは開発用途ではJWT Tokenのほうが、`more secure, more convenient, and more versatile.` ということのようです。SDK Key/Secret認証のメソッド消さなくてもいいのに...

# まずは必要なものの準備

※ 前提としてMac版で実施しています

- https://github.com/zoom/zoom-sdk-electron

こちらのページから必要な事項を確認します。

- ソースコード（Gitでcloneする）
  - A device with Mac OS or Windows OS
  - Mac OS: MacOS 10.10 or later. とのこと
- node.js 12.0.0 version と書いてある
  - nvmで用意しておこう
- zoomのアカウント（またMarketplaceでのAPIキー、トークン等の生成）
  - あとでやります 

# 基本の準備〜ビルド

node 12.0.0.0 （マイナーバージョンまであわせる必要はないかも）

```
$ nvm install 12.0.0
$ node -v
v12.0.0
```

ソースコード

```
$ git clone https://github.com/zoom/zoom-sdk-electron
```

手順通りに必要なものを入れて、ビルドします

```
$ npm install --save-dev electron@5.0.2 -g
$ npm install node-gyp -g
$ npm install bindings -g

$ cd zoom-sdk-electron
$ sh build_nodeaddon_mac.sh
```
手元の環境ではめっちゃwarningが出ました。

```
gyp info ok
cp: ../../../../../../Bin/Mac/Release: No such file or directory
cp: ./build/Release/zoomsdk.node.dSYM: unable to copy extended attributes to ../../../../../../Bin/Mac/Release: No such file or directory
cp: ../../../../../../Bin/Mac/Release/Contents: No such file or directory
cp: ./build/Release/zoomsdk.node.dSYM/Contents: unable to copy extended attributes to ../../../../../../Bin/Mac/Release/Contents: No such file or directory
cp: ../../../../../../Bin/Mac/Release/Contents/Resources: No such file or directory
cp: ./build/Release/zoomsdk.node.dSYM/Contents/Resources: unable to copy extended attributes to ../../../../../../Bin/Mac/Release/Contents/Resources: No such file or directory
cp: ../../../../../../Bin/Mac/Release/Contents/Resources/DWARF: No such file or directory
cp: ./build/Release/zoomsdk.node.dSYM/Contents/Resources/DWARF: unable to copy extended attributes to ../../../../../../Bin/Mac/Release/Contents/Resources/DWARF: No such file or directory
cp: ../../../../../../Bin/Mac/Release/Contents/Resources/DWARF/zoomsdk.node: No such file or directory
cp: ../../../../../../Bin/Mac/Release/Contents/Info.plist: No such file or directory
```

結果的に失敗しているようにも見えますが、大丈夫っぽいので突き進みます。

デモの起動

```
$ sh run_demo_mac.sh
run_demo_mac.sh: line 12: cd: ./demo: No such file or directory
```

こちらもエラーか？！と思いますがそういうもんです。このあといろいろ生成してくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13879/bb5d21fb-3c54-0d54-fc2e-86bfe184d7a7.png)


無事起動しました...が...

```
objc[43150]: Class ZoomLauncher3rdSdkIPCReciever is implemented in both /Users/tetsunosuke/work/zoom/electron/zoom-sdk-electron/demo/node_modules/electron/dist/Electron.app/Contents/Frameworks/ZoomSDKChatUI.framework/Versions/A/ZoomSDKChatUI (0x10d2e6b30) and /Users/tetsunosuke/work/zoom/electron/zoom-sdk-electron/demo/node_modules/electron/dist/Electron.app/Contents/Frameworks/zChatApp.bundle/Contents/MacOS/zChatApp (0x115173560). One of the two will be used. Which one is undefined.
InitSDK 0
Error: Send error, 60 Operation timed out
Error: Send error, 60 Operation timed out
Error: Send error, 60 Operation timed out
```

エラーっぽいなにかが出ています・・・。気にしなくても動作はしているので一旦無視します。

# で？

このままStart Demo を押すと

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13879/43443fa7-2eb4-32f3-ae22-f72c525ae7ae.png)

というように `jwt token` を求められますが、jwt token の有効期限のことを考えると、 とりあえずのお試しには、 `SDK Key/SDK Secret` のほうがいいんじゃないかなと思います。

以後、その前提で手を入れていきます。参考記事の `Zoom Electron SDK 入門` とはここからがやや異なります。

# 手を加えていきます

## その前にSDKで Key/Secret を発行しておく

https://marketplace.zoom.us/ よりサインアップして、

Develop → Build App と選択します。 Choose your app type から SDK を選択しましょう。

各種情報を入力してアプリを作成すると 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13879/cc2800f3-953b-939b-209d-f7fa50ccfba5.png)

SDK Key、 SDK Secret が払い出されるので控えておきます。

## アプリの起動を簡略化します。

こちらも参考記事と同様に内部フローを把握しておくと

- main.js: createWindow() -> showDomainWindow() -> pages/domain.html が開かれる
- dmain.html: doinit() -> senddomainmsg() -> asynchronous-message で "setDomain", 画面で選ばれたdomain, enable_logがメインプロセスに送られる
- main:js は asynchronous-messageを受け取って`functionObj[arg1](arg2, arg3, arg4, arg5);` を呼び出す

という流れになっており、「ボタンを押す」操作は

```diff
 function createWindow() {
   // Create the browser window.
-  showDomainwindow();
+  // showDomainwindow();
+  functionObj["setDomain"](
+      "https://www.zoom.us", /* domain */
+      true                    /* enable_log */
+  );
 }
```
こうしたのと同じと言えます。

※ ところでこのdomainって一体何・・・？

また、 jwt tokenを要求する画面は `pages/index.html` が呼び出されます。
これは、 ProcSDKReady() が呼ばれたときに、 showAuthWindow() が呼ばれているからですね。

ここから先の処理は

- index.html: ボタンクリックで　dosdkauth() が呼ばれる -> authWithJwtToken が asynchronous-messageで呼び出される

となっているので、ここを一気にスキップします。

今回は認証をSDKのKey/Secretで行うので、 `zoomauth.AuthWithJwtToken(sdk_context);` の代わりに、`zoomauth.SDKAuth(key, secret);` を使うようにします。

つまり

```diff

+  sdkLogin: function(key, secret) {
+    let ret = zoomauth.SDKAuth(key, secret);
+    if (ret == 0) {
+      showWaitingWindow();
+    }
   } 
```

このように新しい関数を main.js に実装して、

```diff
 function ProcSDKReady() {
-  showAuthwindow()
   var options = {
     authcb: sdkauthCB,
     logincb: loginretCB,
     logoutcb: null
   }
   zoomauth = zoomsdk.GetAuth(options);
+  functionObj["sdkLogin"](
+      "ここに取得していた SDK Keyを",  /* key */
+      "ここに取得していた SDK Secretを"   /* secret */
+  );
 }
```

と書くことで、sdkLoginが呼ばれ、コールバック（sdkauthCB）以降が動き出します。

これと、言語を日本語化する処理（ZoomSDK_LANGUAGE_ID.LANGUAGE_English→ZoomSDK_LANGUAGE_ID.LANGUAGE_Japanese）を加えて、差分は以下のようになりました。

```diff:main.js
@@ -489,13 +489,16 @@ function showStartJoinWindow() {
 }

 function ProcSDKReady() {
-  showAuthwindow()
   var options = {
     authcb: sdkauthCB,
     logincb: loginretCB,
     logoutcb: null
   }
   zoomauth = zoomsdk.GetAuth(options);
+  functionObj["sdkLogin"](
+      "<省略>",  /* key */
+      "<省略>"   /* secret */
+  );
 }

 function apicallresultcb(apiname, ret) {
@@ -625,7 +628,7 @@ let functionObj = {
       path: '', // win require absolute path, mac require ''
       domain: domain,
       enable_log: enable_log,
-      langid: ZoomSDK_LANGUAGE_ID.LANGUAGE_English,
+      langid: ZoomSDK_LANGUAGE_ID.LANGUAGE_Japanese,
       locale: ZoomAPPLocale.ZNSDK_APP_Locale_Default,
       logfilesize: 5
     }
@@ -2613,6 +2616,12 @@ let functionObj = {
     }
     let ret = zoomsms.SetDefaultCellPhoneInfo(opts);
     console.log('SetDefaultCellPhoneInfo', ret);
+  },
+  sdkLogin: function(key, secret) {
+    let ret = zoomauth.SDKAuth(key, secret);
+    if (ret == 0) {
+      showWaitingWindow();
+    }
   }
 }

@@ -2629,7 +2638,10 @@ app.on('window-all-closed', function () {

 function createWindow() {
   // Create the browser window.
-  showDomainwindow();
+  functionObj["setDomain"](
+      "https://www.zoom.us", /* domain */
+      true                   /* enable_log */
+  );
 }
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13879/08b4a0f7-f5ab-efed-26c2-bbf3aedaaa37.png)

これでzoomクライアントとしてのメインの実装は完了です！

# あとはデモ用の用途をお楽しみください

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13879/99ead3ed-e0fb-77b0-8e45-e558a1bc89c7.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13879/e5be432d-b9f0-6318-3840-f09321776f37.png)