ニコ生チャット取得

コメントビューア用。
OAuthが要るようなAPIは使わず、未登録で取得できるコメントを取得。
あくまで執筆時点の情報です。仕様が変わる可能性もあるのであしからず。

前提として、ニコ生チャット取得はWebSocketの接続を2本行う必要があることに留意。


### コミュニティIDを元に、配信ページを取得

- `https://live.nicovideo.jp/watch/co{コミュニティID}`
  - 例: `https://live.nicovideo.jp/watch/co4930985` 

(2021/05/29にURLが変わったようです。`live2` -> `live`)

### ページに埋め込まれた放送情報を取得する
(2023/05/13あたりでprogram.broadcastIdが無くなったようです。audienceTokenの先頭に同じIDが入ってるので適当に切り出してください)

```js:
    const embeddedData = JSON.parse(document.getElementById("embedded-data").getAttribute("data-props"));
```
- 以下の場合は配信が終了してる
  - embeddedData.program.status === 'ENDED'
  - embeddedData.program.endTime < 現在時刻


```js
    const audienceToken = embeddedData.player.audienceToken;
    const frontendId = embeddedData.site.frontendId;
    const broadcastId = audienceToken.match(/^\d+/)[0];
```

### スレッドIDを取得する

WebSocketで以下に接続

```js
const url = `wss://a.live2.nicovideo.jp/unama/wsapi/v2/watch/${broadcastId}?audience_token=${audienceToken}&frontend_id=${frontendId}`
```


**2021/01/21追記**
WebSocket接続時、ちゃんとしたブラウザの`User-Agent`をヘッダに含めないと、CONNECT_ERRORが返ってくるようになりました。ChromeのUser-Agentあたりを適当に含めてあげてください。


メッセージのやり取りは以下の通り。簡単のためにJSONで表記しているが、実際は文字列化して送受信される。

```json:send
{
  "type": "startWatching",
  "data": { 
    "stream": { "quality": "high", "protocol": "hls", "latency": "low", "chasePlay": false },
    "room": { "protocol": "webSocket", "commentable": true },
    "reconnect": false
  },
}
```

```json:send
{ 
  "type": "getAkashic",
  "data": { "chasePlay": false } 
}
```

```js:receive
{
    "type":"room",
    "data":{
      isFirst: boolean,
      messageServer: {
        type: string,
        /** コメントサーバのWebSocketURL */
        uri: string,
      },
      name: string,
      /** スレッドID */
      threadId: string,
      waybackkey: string,
    }
}
```
- ここで抜き出すべき要素は以下。
  - data.messageServer.uri
  - data.threadId


### チャットサーバに接続してスレッドIDを送信

先程取得したコメントサーバのURLにWebSocket接続。
WebSocketのリクエストヘッダの指定が必要。

```js
    const ws = new WebSocket(wsUrl, 'niconama', {
      headers: {
        'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits',
        'Sec-WebSocket-Protocol': 'msg.nicovideo.jp#json',
      },
    });
```

以下WebSocketでの送受信。

```json:send
[
    {
        "ping": {
            "content": "rs:0"
        }
    },
    {
        "ping": {
            "content": "ps:0"
        }
    },
    {
        "thread": {
            "thread": "★スレッドID★",
            "version": "20061206",
            "user_id": "guest",
            "res_from": -150,
            "with_global": 1,
            "scores": 1,
            "nicoru": 0
        }
    },
    {
        "ping": {
            "content": "pf:0"
        }
    },
    {
        "ping": {
            "content": "rf:0"
        }
    }
]
```

### チャット情報の受信

コメントサーバのWebSocketにスレッドIDを送信したら、コメント情報が返ってくる。

```:receive
開いた時点までのチャット情報がドカッと来る。
obj.ping.content: 'rf:0'のものを受信したらおしまい。
```

以降は下記のchatオブジェクトを含んだメッセージを受信し続ける。

```typescript:receive
{
  chat: {
    anonymity?: number;
    /** チャットコメント */
    content: string;
    /** Dateを数値化したやつ */
    date: number;
    date_usec: number;
    /** 匿名の時は184が入ってる */
    mail?: string;
    /** コメント番号 */
    no: number;
    /**
     *  謎のフラグ
     * - 1: プレミアム会員
     * - 3: 配信者自身
     */
    premium?: number;
    score?: number;
    thread: string;
    user_id: string;
    vpos: number;
  };
}
```

また、定期的にWebSocketに対してpingを打つ必要がある。特にメッセージを含める必要はない。
これをしないと途中でメッセージが来なくなる。
JavaScriptなら、WebSocketインスタンスが持っているpingをsetInterval実行するだけでOK。

```js
ws.ping();
```