Twilio Videoで全員が退室したらRoomを閉じる方法

:::note alert
2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
:::

# はじめに

みなさん、こんにちは。
KDDIウェブコミュニケーションズのTwilio事業部エバンジェリストの高橋です。

前回の記事で、REST API経由でRoomを生成すると、全員が退室しても５分間はRoomが存在するという説明をしました。
[Twilio Videoで都度Roomの設定を行う方法](https://qiita.com/mobilebiz/items/b300861475409fee8199)

そこで、この記事では５分待たずに全員がRoomから退室した時点でRoomを閉じる方法を説明します。

## なぜ閉じる必要があるのか

Twilio Videoの会議は、Room単位でログが生成されます。以下は生成されたログです。
![スクリーンショット 2020-05-31 17.12.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86046/af612a24-64b1-8788-4745-f78aec0b9088.png)

このログをみるとわかるように、ルームの`UNIQUE NAME`（Room名）は同じでも、`SID`（RMから始まる文字列）が異なるログが生成されています。
先程の5分が有効になっていると、一度全員が退室してから5分以内に誰かが入室してくると同じSIDをもつRoomログにまとまってしまいます。これだと、途中誰もいなかった時間も継続時間としてカウントされてしまうので会議時間としては正確ではないです。
また、そもそもこのログは、`Completed Room Logs`なので、完了していないと管理コンソールからは確認できません（継続中のRoomログを調べるにはREST APIを利用します）。

そこで、REST APIを使ってRoomを生成した場合も、全員が退出したタイミングでRoomデータを完了させる方法が必要になります。

## `StatusCallback`を使う

Twilio Videoには、Roomの属性として`StatusCallback`が用意されています。これは、Room上で発生した[様々なイベント](https://www.twilio.com/docs/video/api/status-callbacks#rooms-callback-events)に応じて、Twilio側から発行される通知になります。
残念ながら通知されるイベントの中に、「全員が退出した」というイベントはないので、「誰かが退出した」イベント（`participant-disconnected`）が発火したタイミングで、Roomに残っている人を数えて、全員いなくなったときに強制的にRoomを閉じるようにすれば良さそうです。

では、さっそく実装しましょう。

まずは、`StatusCallback`の指定ですが、こちらは前回の記事のコードを一部修正することで設定できます。

```javascript:video-token.js
exports.handler = async function(context, event, callback) {

・・・ 省略 ・・・

        if (rooms.length === 0) {
            // 開催中の部屋がないので定義
            return client.video.rooms.create({
                maxParticipants: MAX_PARTICIPANTS,
                recordParticipantsOnConnect: RECORDING,
                type: ROOM_TYPE,
                uniqueName: roomName,
                mediaRegion: 'jp1',
                statusCallback: `https://${context.DOMAIN_NAME}/video-status-callback`, // <--ここを追加
            });
        } else {
            return null;
        }

・・・ 省略 ・・・

};
```

次に、`StatusCallback`の受け側をつくります。

```javascript:video-status-callback.js
exports.handler = function(context, event, callback) {
    console.log('Status Callback called.');
    console.log(event.RoomSid, event.RoomStatus, event.StatusCallbackEvent);

    // 誰かが退出したら、残りの参加者を確認し、誰も残ってなければルームを閉じる
    if (event.StatusCallbackEvent === 'participant-disconnected') {
        const ACCOUNT_SID = ''; TwilioのAccountSid;
        const API_KEY = ''; Twilio VideoのAPI Key;
        const API_SECRET = ''; Twilio VideoのSecret;
        const roomName = event.RoomName;
        const roomSid = event.RoomSid;
        const client = require('twilio')(API_KEY, API_SECRET, {accountSid: ACCOUNT_SID});
        let fAlive = false;  // 生存者フラグ
        client.video.rooms(roomSid).participants.each({
            status: 'connected',
            done: err => {
                if (err) callback(new Error(`Room closing error: ${error}`));
                if (!fAlive) {
                    // 誰も残ってないので、ルームを閉じる
                    client.video.rooms(roomSid).update({
                        status: 'completed'
                    })
                    .then(room => {
                        console.log(`Room close. ${room.sid}`);
                        callback(null, 'OK');
                    })
                    .catch(error => {
                        callback(new Error(`Room closing error: ${error}`));
                    });    
                } else {
                    callback(null, 'OK');
                }
            }
        }, participant => {
            console.log(`participant: ${participant.identity} is alive.`);
            fAlive = true;  // 生存者がいたのでフラグをセット
        });
    } else {
        callback(null, 'OK');
    }
}
```

Roomを強制的に閉じるには、Roomを`Update`して`Status`を`Completed`にすればOKです。
また、現在入室している（残っている）参加者を知るには、[こちらのAPI](https://www.twilio.com/docs/video/api/participants#linkcode)を使えば良さそうです。
ところが、こちらのサンプルコードにある`.each`というメソッドが曲者で、得られたリストが終了したときの処理の書き方がわかりにくく、`callback`をどこで発行すればよいかで頭を悩ますことになります（`callback`を発行しないとFunctionsがタイムアウトしてエラーになります）。
結論から言うと、上記コードにあるように、`done:`パラメータを使って、リスト終了後の処理を書きます。

以上で、REST API経由でRoomを作成した場合も、全員が退室時にRoomが閉じられるようになります。
なお、Roomが閉じられると、`room-ended`イベントが発火するので、会議の時間を計算したり、録画データの後処理を行うなどに利用するとよいでしょう。

# まとめ

今後、BCP対策としてVideoを使ったサービスを開発したりする機会が増えてくるかと思いますので、ぜひ参考にしていただければと思います。

---
### Twilio（トゥイリオ）とは

https://cloudapi.kddi-web.com
Twilioは音声通話、メッセージング（SMS/チャット）、ビデオなどの 様々なコミュニケーション手段をアプリケーションやビジネスへ容易に組み込むことのできるクラウドAPIサービスです。初期費用不要な従量課金制で、各種開発言語に対応しているため、多くのハッカソンイベントやスタートアップなどにも、ご利用いただいております。