VPNに繋いだままZoom接続した人にSlackを飛ばそう

社内VPNに接続したままZoomを行う人が多いと帯域が圧迫して辛いですね。
そんなわけで、VPNに接続した状態でZoomに接続している人に、Slackで通知を飛ばすような仕組みを作りましょう。

なお、コードサンプルではLaravelを用いて実装しています。

## 前提
- VPNで固定なGlobalIPとなる
- SlackとZoomの登録メールアドレスが一致している
- ZoomがBusinessPlan以上であること
 
## つくりかた
VPNのアクセスユーザーとZoomの接続ユーザーを突き合わせる形でもできますが、今回はZoomのAPIを利用してGIPと接続種別を見る形とします。

1. Zoomの [Participant/Host joined meeting](https://marketplace.zoom.us/docs/api-reference/webhook-reference/meeting-events/participant-joined-meeting) を利用してWebHook通知を受ける
2. Zoomの [Get Meeting Detail API](https://marketplace.zoom.us/docs/api-reference/zoom-api/dashboards/dashboardmeetingdetail) を利用してMeetingの参加者リストを取得する
3. 参加者リストから、参加したユーザーのネットワーク情報を取得する
4. SlackAPIの [users.lookupByEmail](https://api.slack.com/methods/users.lookupByEmail)を利用し、ユーザーのSlackUserIDを取得する
5. SlackAPIの [chat.postMessage](https://api.slack.com/methods/chat.postMessage)を利用し、ユーザーにメッセージを送信する

## つくる
[SlackのApp](https://api.slack.com/apps)及び[ZoomのApp](https://marketplace.zoom.us/)は、予め作成し、APIDocsを参照の上、適切な権限を付与しておいてください。

### 1. ZoomのWebHook通知を受ける
[Participant/Host joined meeting](https://marketplace.zoom.us/docs/api-reference/webhook-reference/meeting-events/participant-joined-meeting) を利用すると、Zoomに人が参加してきたときに、WebHookで通知を受けることができます。
これを利用して、ユーザーのアカウント情報を取得します。

これ自体は単純なEventWebhookなので、適当にPOSTで受け取れるURLを準備しましょう。
今回使用するのは `meeting.participant_joined` のEventなので、適当にデータを抜き出します。

```
if($request->event == 'meeting.participant_joined'){
    $payload = $request->payload;
    $meeting_id = $payload['object']['id'];
    $joined_user_id = $payload['object']['participant']['participant_user_id'];
}
```

### 2. Zoomミーティングの参加者リストを取得する
Zoomの管理者ダッシュボード、あるいは[Get Meeting Participants API](https://marketplace.zoom.us/docs/api-reference/zoom-api/dashboards/dashboardmeetingparticipants)を利用すると、ミーティングに参加しているすべてのユーザーのGIPとネットワーク接続種別を取得することができます。
EventWebHookで取得するデータにはこれらの情報が存在しないので、二度手間になるのですが、先程取得したMeetingIDを利用して、ミーティングの参加者リストを取得しましょう。

```
$headers = [
    'Content-Type'  => 'application/json',
    'Authorization' => 'Bearer ' . config('zoom.token'),
];

$params = [
    'page_size' => 300,
];

$response = Http::withHeaders($headers)
    ->get("https://api.zoom.us/v2//metrics/meetings/$meeting_id/participants", $params);    

$participants = json_decode($response->body(), 1);
```

### 3. ターゲットユーザーのネットワーク情報を取得する
2.で取得したミーティングリストから、今回のターゲットのユーザーIDを検索します。
今回はLaravelを使用しているので、データをcollectionに入れて検索をします。

```
$users = collect($participants['participants']);

$check = $users->where('id', '=', $joined_user_id)
    ->where('ip_address', '=', '127.0.0.1') // 対象となるGIPを入れる
    ->where('network_type', '=', 'PPP') // IPSecの場合はPPP, SSL-VPNの場合は...確認していない
    ->where('status', '=', 'in_meeting')
    ->first();

```


### 4. ターゲットのSlackのUserIdを取得する
3.でミーティングにjoinしてきたユーザーが存在する場合、メールアドレスからSlackのUserIdを取得します。
このとき、ZoomのEmailが取得できない、Slackに該当するユーザーが居ない場合に向けたエラーハンドリングを準備することを推奨します。

### 5. ターゲットにSlackでメッセージを送信する
4.で取得したUserIDに対して適当なメッセージを送信します。

```
if(!is_null($check)){
    // SlackのUserIDを取得
    $email = $payload['object']['participant']['email'];

    $headers = [
        'Authorization' => 'Bearer ' . config('slack.token'),
        'Content-Type' => 'application/x-www-form-urlencoded',
    ];

    $response = Http::withHeaders($headers)
        ->asForm()
        ->get('https://slack.com/api/users.lookupByEmail', ['email' => $email]);
    
    $slack_user_data = json_decode($response->body(), 1);

    // メッセージを送信
    $params = [
        'channel' => $slack_user_data['id'],
        'text' => "VPNに接続した状態でZoomを行っている可能性があります。\nVPN負荷軽減のため、可能な場合VPNから切断を行ってください。",
    ];

    $response = Http::withHeaders($headers)
        ->asForm()
        ->post('https://slack.com/api/chat.postMessage', $params);

    $response = json_decode($response->body(), 1);
}
```

![pipi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2075956/52551943-3e49-18bb-174a-01b93c157978.png)


できました。

皆様もZoom接続やYouTube視聴をする際はVPNを外すようにしましょう。