Twitch の配信開始を通知する Webhook を登録しそこから Twitter に流すやつ（非アプリ）

### 注
現在普通に Zapier でできますが，技術的にはなにか参考になるかもと思い残しておきます。

# 背景
Twitch は Twitter とアカウント連携できるのですが，なんと連携しても配信開始がつぶやかれたりはしません。以前は機能として提供されていたのですがいつしか消えていました。公式によると，「twitter リンクから飛んでくる人が少なかったのでその機能なくしました」とのことです。でも**わたしは使いたい**。調べると，IFTTT を使う方法がたくさん見つかり，設定してみたのですが，配信を開始してからtwitter への投稿がされるまで最大1時間かかったりしたので，ちょっとつかえないなと判断し，他に良さげなサービスもみつからなかったので仕方なく作りました。めんどくさかったです。

#### 本記事の関心範囲
- Twitch API を利用した配信開始時の通知 Webhook の登録仕様
- twitter へ投稿する webhook 用エンドポイントの作成（firebase functions利用と選定理由）

アプリ化は想定せず，自分個人が使える範囲で，とりあえず動かすまでとしています。前提基礎知識の解説はあんまりやらないです。

# Twitch API への Webhook 登録
https://dev.twitch.tv/docs/
API へのアクセスは Oauth (https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be) の利用が前提です。

Twitch でとりあえず使おうとする場合は簡易に書けば以下のようになります。
1. アプリを developers に登録してクライアントIDを得る
2. クライアントIDを使った Oauth プロセスでトークンを得る
3. トークンとクライアントIDを使って API を呼ぶ

### クライアントIDを得る
アプリ登録フォーム(https://dev.twitch.tv/console/apps) を適切に埋めれば発行されます。コールバック先は，アプリ公開するのでなければトークンがほしいだけなのでデタラメでも大丈夫。今回のケースでは `http://localhost` にしています。

### トークンを得る
以下のような URL を作成してアクセスします

```
https://id.twitch.tv/oauth2/authorize?client_id=さっきのクライアントID&redirect_uri=http://localhost&response_type=token&scope=bits:read
```
Oauth のアプリ権限許可画面が出てくるので許可します。
当然，リダイレクト先は存在しないので「ページがありません」的なエラーになりますが，実はトークンは発行されています。トークンは以下のように，リダイレクトされた URL のパラメータ `access_token` に指定されているので，これをコピーして得ることができます。（アプリとして作るならばこれを受け取ってどこかに取っておくかたちになりますね）

```
http://localhost#access_token=xxxxxxxx&scope=bits:read
```

### トークンとクライアントIDを使ってAPIを呼ぶ
curl を使えれば次のようにして可能です (以下は POST の場合）

```bash
curl -X POST -d "{\"なにかパラメータ項目\":\"内容\"}" -sS -H 'Content-Type: application/json' -H 'Authorization: Bearer さっき得たトークン' -H 'client-id: さっき得たクライアントID' https://api.twitch.tv/呼びたいAPIのエンドポイント
```

## 配信通知を受け取る Webhook の登録
配信通知を受け取るには，Twitch API に対して「配信が開始/停止されたらこの URL を呼んでね」とリクエストします。これを以降 Webhook の登録と記述します。
https://dev.twitch.tv/docs/api/webhooks-guide

Webhook の登録で重要なポイントは，**登録時，その URL が有効な API として存在しているかの疎通確認リクエストがある（これを challenge という)**ことです。

登録APIの呼び方は具体的には以下のようになります，リファレンス (https://dev.twitch.tv/docs/api/webhooks-reference) を参考にパラメータとセットでAPIを呼びます

```bash

curl -X POST -d "{\"hub.callback\":\"WebhookとするURL\", \"hub.mode\":\"subscribe\", \"hub.topic\":\"https://api.twitch.tv/helix/streams?user_id=130871908\", \"hub.lease_seconds\":\"864000\"}" -sS -H 'Content-Type: application/json' -H 'Authorization: Bearer さっきのアクセストークン' -H 'client-id: さっきのクライアントID' https://api.twitch.tv/helix/webhooks/hub
```
`hub.topic` に通知を受け取りたいイベントの API エンドポイント URL を指定します。このエンドポイントは普通に呼んでも現在の値を返してくれます。`hub.mode` に subscribe と設定して，通知を受け取ることを明示します。もしやめたければ，これを unsubscribe に設定してAPIを呼び直します。

この登録リクエストを送ると，Twitch サーバ側は `hub.callback` に設定した URL に疎通確認をはじめます。それに対し適切なレスポンスを得られれば当該 URL を Webhook として登録し，配信通知のたびに，その URL を配信情報付きで呼ぶことになります。

## Webhook 用 API の開発
登録するそもそもの呼び出し API を作る必要があります。Twitch 側から受け取った配信情報を整形し twitter へ投稿する API です。Twitter への投稿も Oauth 利用前提のAPI呼び出しをする必要があります。Twitter dev portal (https://developer.twitter.com/en/portal/dashboard) にてアプリを登録し，クライアントIDなどの各種情報を手に入れます。Twitch とは異なり，開発用途ではこの画面から直接アクセストークンを得ることができて便利ですが，アプリの情報を事細かに，かつ英語で書く必要があるので少し面倒です。
権限は投稿が必要なため `write` まで必要です。

取得したい情報は `consumer_key`, `consumer_secret`, `access_token_key`, `access_token_secret` です。write 権限をつけるので扱いには気をつける必要があります。

## Firebase functions の設定
必要な情報が揃ったら Firebase Functions を使って API を作ります。最初はもっと手軽な Google App Script (GAS) を使おうとしたのですが，**GAS で発行されるURLはリダイレクトになっていて，Twitch からの challenge が成功しない**ため，functions の利用に切り替えました。


Twitter への接続情報をハードコードするのもなんなので，以下のように環境変数として登録しておきます(1コマンドでやれる書き方もあります)

```bash

$ firebase functions:config:set credential.twitter.consumer_key="xxxxxxxxxx"
$ firebase functions:config:set credential.twitter.consumer_secret="xxxxxxx"
$ firebase functions:config:set credential.twitter.access_token_key="xxxxxx"
$ firebase functions:config:set credential.twitter.access_token_secret="xxx"
```


細かい手順については省きますが，以下のようにコードを書き `firebase deploy` します。
`npm twitter` は実行しておいて導入しておく必要があります。

```javascript
const functions = require('firebase-functions');
const Twitter = require('twitter');

exports.tw2tw = functions.https.onRequest(async (request, response) => {

  if (request.query['hub.challenge']) {
    // challenge をそのまま返して疎通確認とする
    response.send(request.query['hub.challenge']);
  } else {
    data = request.body.data;

    const config = functions.config();
    if(config.credential && config.credential.twitter){
      
      const client = new Twitter({
        consumer_key: config.credential.twitter.consumer_key,
        consumer_secret: config.credential.twitter.consumer_secret,
        access_token_key: config.credential.twitter.access_token_key,
        access_token_secret: config.credential.twitter.access_token_secret
      });

      const tweet = (data) => {
        if (data.length > 0) {
          const streamInfo = data[0];
          return '配信を開始しました: '
            + streamInfo.title
            + ' https://www.twitch.tv/azumagbanjo';
            // 配信 URL はペイロードに含まれないため一旦ハードコード
            // 環境変数にしても良かったな
        } else {
          return 'Twitch 配信を終了しました';
        }
      };

      await client.post('statuses/update', { status: tweet(data) })

      response.send('ok');  
    } else {
      response.send("Not Found Config");
    }
  }
});

```

デプロイが成功すればこの関数を呼び出すAPIが発行されるので，それを Twitch Webhook に登録すれば完成となります。

### 注意点
だれでもアクセスできる API として公開するため，URL がバレてしまえばだれでも設定したアカウントに投稿できる状態になります。twitch ドメインからのアクセスからのみ受け付けるようにする等，このあたりは取り扱い注意です。


# Repository
https://github.com/azumag/tw2tw
(動作確認用に使ったコードもそのままいれたりしてるので雑です）

# References
- https://dev.twitch.tv/docs/api/webhooks-reference
- https://qiita.com/pasta04/items/2ff86692d20891b65905
- https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be
- https://qiita.com/tanakaoriginal/items/4cbbaa47ec752c319296 
- https://blog.katsubemakito.net/firebase/functions-environmentvariable