Lambda で MedliaLive の稼働状況を監視して Slack に通知するようにした話

:::note
AWS LambdaとServerless Advent Calendar 2022 2日目の記事です🎄
:::

## はじめに
みなさん、おはようございます！リバネス開発チームのトミー（@tomyf）です😀

弊社では、自社サービス向けのライブ配信システム（以降、ライブ配信システムと表記します）で AWS Elemental MedliaLive を活用しています。
ライブ配信システムを簡単に説明しますと、リバネスが運営する [超異分野学会](https://hic.lne.st) や [テックプランター](https://techplanter.com/) 、ウェビナーなどの配信を、リバネスIDを持つユーザ限定で視聴できるプラットフォームです。

現在はライブ配信が必要なイベントごとに、専用のチャンネルを作成してライブ配信システムに紐付けており、イベント担当者がライブ配信システムからチャンネルの起動/停止を行っています。
皆さんご存知かもしれませんが AWS Elemental MedliaLive は実際の映像のアップストリームとは別に、チャンネルが起動している時間で料金が発生します。
前述の通り、チャンネルの起動/停止はイベント担当者が行っているため、チャンネルの停止を忘れた時に、気がついたら結構な料金が発生していたということがありました😓

そこで AWS Lambda で AWS Elemental MedliaLive の稼働状況を監視して Slack に通知するようにしたので紹介します！

## 環境
- Slack
- Incoming Webhook
- AWS Lambda (Node.js 14.x)
- AWS Elemental MedliaLive
- Amazon EventBridge

## Slack の Incoming Webhook を利用する
Incoming Webhook を利用すると外部ソースから Slack にメッセージを投稿することができるようになります。
![スクリーンショット 2022-11-28 9.42.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304335/72991ccd-94d6-1e5c-6322-2ee98cf03860.png)
メッセージを投稿したい Slack のチャンネルを選択して Incoming Webhook を追加すると、インテグレーションの設定の項目から Webhook URL を取得できます。
![スクリーンショット 2022-11-28 9.51.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304335/4becd4e4-7037-9c93-163f-280d34d0d975.png)
この URL に対して、以下のようなリクエストを送信すると Slack に投稿できます。
```
POST https://hooks.slack.com/services/xxxxxxxxxx
BODY {
 "text": "メッセージ"
}
```

## AWS Lambda から Slack に投稿する
Slack に投稿するための Webhook URL に対して、リクエストを投げる AWS Lambda 関数を作成します。
Webhook URL は予め環境変数に設定しておきます。
```text
SLACK_WEBHOOK_URL: https://hooks.slack.com/services/xxxxxxxxxx
```
```python
const https = require('https')

const sendMessageToSlack = (message) => {
    const url = new URL(process.env.SLACK_WEBHOOK_URL)
    const params = {
        text: message
    }
    const options = {
        hostname: url.hostname,
        path: url.pathname,
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Content-Length': Buffer.byteLength(JSON.stringify(params))
        }
    }
    const request = https.request(options, response => {
        console.log(response)
    })
    request.on('error', (e) => {
        console.error(e)
    })
    request.write(JSON.stringify(params))
    request.end()
}

exports.handler = (event) => {
    sendMessageToSlack('メッセージ')
}
```

これで AWS Lambda から Slack に投稿することができるようになりました。

## AWS Lambda から AWS Elemental MedliaLive のチャンネルを取得する
`aws-sdk` をインポートして AWS Elemental MedliaLive のチャンネルを取得する API を利用します。弊社では、アジアパシフィック（東京）とアジアパシフィック（シンガポール）でチャンネルを作成しているため、それぞれ取得するようにしています。取得したチャンネル一覧から `State` が `RUNNING` になっているデータだけにフィルタリングします。
```python
const AWS = require('aws-sdk')

exports.handler = (event) => {
    const regions = ['ap-northeast-1', 'ap-southeast-1']
    Promise.all(regions.map(async (region) => {
        const medialive = new AWS.MediaLive({ region })
        const listChannels = await medialive.listChannels().promise()
        return {
            region: region,
            channels: listChannels.Channels.filter(channel => channel.State === 'RUNNING')
        }
    })).then((regionChannels) => {
        let messages = []
        regionChannels.forEach((regionChannel) => {
            if (!regionChannel.channels.length) return

            messages.push(`_${regionChannel.region}_`)
            messages.push(...regionChannel.channels.map(channel => `• ${channel.Name} (${channel.Id})`))
        })

        if (!messages.length) return
        
        messages.unshift('*【稼働中の配信サーバ】*')
        sendMessageToSlack(messages.join('\n'))
    })
}
```
チャンネルを起動した状態で、作成した AWS Lambda 関数を実行すると以下のようなメッセージが投稿されます。
![スクリーンショット 2022-11-28 14.35.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304335/6ac12ce0-aa90-35bf-8b9a-b7492a0f796f.png)

## AWS Lambda 関数を定期実行する
作成した AWS Lambda 関数を定期的に実行するために Amazon EventBridge の設定を行います。Cron 式に `0 11,23 * * ? *` を設定し、毎日午前8時と午後8時に AWS Lambda 関数が実行されるように設定しました。
![スクリーンショット 2022-11-28 14.52.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304335/9a5d8907-2ee6-411a-8f28-f9bfe8383564.png)

## おわりに
こちらの Slack 通知を導入してから、チャンネルの停止忘れがあっても、その日のうちに気がついて停止することができるようになりました！
イベントスタッフが慣れるまでは、そこそこな頻度で Slack に通知が流れていたので、導入していなかったら結構な出費が発生するところでした😇

自社用のライブ配信システムを構築することは、少し珍しいケースかも知れませんが、導入を検討されている方がいらっしゃいましたら、是非ご活用ください！

余談ですが、ライブ配信システムから AWS Elemental MedliaLive のチャンネルの起動/停止やアップストリーム先のエンドポイント取得ができるため、イベントスタッフは AWS を直接操作することなく運用できるようになってます！
ライブ配信システムは Laravel で実装されていますが、もし需要があれば別途記事を書こうかと思います。