ニコニコ生放送のコメント取得･投稿

2021/01 更新

getplayerstatusが廃止されるためこのドキュメントは使い物にならなくなります！
詳細: https://blog.nicovideo.jp/niconews/146308.html
XMLソケットも廃止されるはずなので、今後はWebSocketで頑張ることになります。

---

2020/07 更新

このドキュメントは諸事情により今後更新することはありません。
また、今後同じ手法が利用できる保証もしませんので、ご承知おきください。

---

以前ブログにまとめていましたが、情報がメチャクチャだったので改めて。
正しい保証はしませんし、もし問題があればすぐ消す予定ですので、あしからず。
また、諸事情により新配信のAPIは解説しません。

# はじめに
**UserAgentの指定を忘れないように**しましょう。
**ソフト(ライブラリ)名･連絡先を必ず入れるようにしましょう**。
例)`NicoLiveLib/1.0;@ingen084`[^1]とか。

C#の場合は以下のように設定することでUserAgentを設定することができます。

```cs
//var client = new HttpClient();
client.DefaultRequestHeaders.Add("User-Agent", "AppName/1.0;@twitterid");
```
また、C#の場合は`HttpClient`のCookieを有効にする必要があります。

```cs
var handler = new HttpClientHandler
{
	CookieContainer = new CookieContainer(),
	UseCookies = true
};
//既にセッションが分かっている場合ここでセットする
//handler.CookieContainer.Add(new Uri("http://www.nicovideo.jp"), new Cookie("user_session", Session, "/", ".nicovideo.jp"));

//ここでCookieが有効になったHttpClientが作成できる
var client = new HttpClient(handler);
```

# ログインする
まずログインしなければ話が始まりませんよね。
ログインは実は何個か方法があるのですが、今回は普通にログインフォームの方法でやってみましょう。

```
Url: https://account.nicovideo.jp/api/v1/login
Method: POST
Content-Type: x-www-form-urlencoded
```

| Key | Value |
|:-:|:-:|
| mail_tel | メールアドレス |
| password | パスワード |

ログインに成功した場合、Cookieの `user_session` にセッション情報が保存されます。

# ログインチェック
この手法は、ほぼすべてのAPI･ページで使用可能です。
レスポンスのHeadersの`x-niconico-authflag`が`1`であればログイン中となります。
ログインされていない状態では、`0`になります。
また、ログイン中であれば`x-niconico-id`にユーザーIDが格納されています。


# 生放送の情報を取得する
旧配信~~を象徴する~~APIですが、~~しばらくは無くなることはないと思います~~。
**[なくなります！](https://blog.nicovideo.jp/niconews/146308.html)** 使わないようにしましょう。

```
Url: http://live.nicovideo.jp/api/getplayerstatus/{LiveId or Alias}
Method: GET
```

## 生放送IDまたはエイリアスについて
生放送IDとは`lv`からはじまる番号のことです。必ずlvからはじまる必要があります[^2]。
エイリアスについてはコミュニティ番号･チャンネル番号~~や、旧Nsenの`nsen/toho`など~~です。アクセスするとそのコミュニティ･チャンネルの最新の枠の情報が返ってきます。[^3]
サンプル(リンク回避してます): ttp://live.nicovideo.jp/api/getplayerstatus/ch2631252[^4]

## レスポンス
XMLで返ってきます。あまりにも返ってくる量が多いのでここでは全部紹介しませんが、重要なものを簡単に紹介します。

- `getplayerstatus@status` 正常にリクエストが完了した場合、`ok`
- `getplayerstatus/stream/id` lvから始まる生放送ID、エイリアスを使用して取得した場合でもlv番号がわかります。
- `getplayerstatus/stream/is_owner` 配信者権限が存在する場合、`1`
- `getplayerstatus/stream/start_time` 放送開始時間(UnixTime)、コメント投稿の際に必要
- `getplayerstatus/rtmp/url` 配信する際のRTMPアドレス
- `getplayerstatus/rtmp/ticket` RTMPアドレスの後ろに`/`を挟んで記述する文字列
- `getplayerstatus/ms/addr` コメントサーバのアドレス
- `getplayerstatus/ms/port` コメントサーバのポート
- `getplayerstatus/ms/thread` コメントサーバのスレッドID

# コメントの取得
コメントの取得(受信)はXMLソケットというものを使用します[^5]。
ざっくりとしたXMLソケットの解説としてはTCPで接続し、文字コードはUTF8で

```xml
<node>...</node>\0
```
(\0はエスケープシーケンス)
という形でXMLノードが送信されてくる形になります。

## コメントサーバへ接続
1. `getplayerstatus`で得たコメントサーバのアドレス･ポートにTCPで接続します。
2. `<thread thread="スレッドID" res_from="取得する過去ログの数" version="20061206" scores="1" />\0`(\0は以下略)  
  を送信します。スレッドIDは`getplayerstatus`のものを、取得する過去ログの数は**負の数**で、`-1000`が限界のようです。
3. サーバから`thread`ノードが飛んできたら接続成功です。以後リアルタイムにコメントが飛んできます。  
  - `@ticket` はコメント投稿の際に必要になります。取っておいてください。

## 飛んでくるXMLノードの解説
### `thread`
こちらから`thread`を送信し、そのスレッドの受信を開始した、という意味だと思われます。

### `leave_thread`
詳しく検証してないので不明ですが、生放送が終了したときに呼ばれており、そのスレッドの受信を終了した、という意味だと思われます。

### `chat`
コメント自体です。

- `InnerText` コメント本文、一部文字はエスケープされているので注意してください。
- `@no` コメント番号、公式生放送などコメント番号のない放送の場合はそもそも属性がついてきません。
- `@mail` コマンド
- `@thread` スレッドID
- `@vpos` 生放送時間(UnixTime)
- `@date` 実時間?(UnixTime)
- `@user_id` ユーザID 184の場合は文字列になります。
- `@premium` プレミアムフラグ
  - 例: 0=一般 1=プレミアム 2=アラート 3=放送主(運営+プレミアム) 4=運営コメント  
    らしい。
- `@anonimity` 匿名(184)なら`1`
- `@score` 共有NGスコア、負の数で、0だった場合はそもそも属性がついてきません。

### `chat_result`
コメント投稿(後述)した際にその結果が帰ってきます。
 - `@status` `0`なら成功 失敗すると`1`もしくは`4`

## 注意
10分間？コメントがないなどで通信がない場合、切断されてしまうようです？
また、コメントが長い場合や初回の過去ログ送信は1つのパケットで一気に送信されてくるため、バッファが溢れてパケットが分断された際の処理を忘れないようにしましょう。

# コメントの投稿
## vposについて
`vpos = 現在の時間 - 放送開始時間`(UnixTime)で算出されます。

## PostKeyの取得
コメントを投稿する際に使用するトークンを取得します。このトークンは1度きりのため、コメントするたびに取得する必要があります。

```
Url: http://ow.live.nicovideo.jp/api/getpostkey?thread=スレッドID
Method: GET
```
レスポンスは

```
postkey=実際のキー
```
となります。正常に取得できなかった場合は、実際のキーの部分はなく、`postkey=`のみになります。

## コメントの送信
受信時に接続したXMLソケットに送信します。

```xml
<chat thread="スレッドID" ticket="チケット" vpos="vpos" postkey="PostKey" mail="コマンド" user_id="自身のユーザーID">コメント本文</chat>\0
```
チケットは`thread`で取得したものを使用します。
送信が完了したら`chat_result`が向こうから送信されてきます。

## 注意
getplayerstatus後一定時間経つとPostKeyが取得できなくなり、コメントが投稿できなくなります。
コレを回避するために一定時間ごとにHeartbeatを送信する必要があります。

# Heartbeatを送信する
上記の通り、大体1~2分に1度、Heartbeatを送信する必要があります。

```
Url: http://ow.live.nicovideo.jp/api/heartbeat
Method: POST
Content-Type: x-www-form-urlencoded
```
| Key | Value |
|:-:|:-:|
| v | 生放送ID(lv番号) |

## レスポンス
重要なもののみ解説します。

- `heartbeat@status` 成功した場合`ok`
- `heartbeat/watchCount` 現在の視聴者数
- `heartbeat/commentCount` 現在のコメント数
- `heartbeat/ticket` 更新されたチケット?
- `heartbeat/waitTime` 次Heartbeatをするまでの秒数?

# 新配信での生放送への運営コメント投稿について
[大百科にありました](http://dic.nicovideo.jp/l/%E9%81%8B%E3%82%B3%E3%83%A1api)[^6]。

[^1]: ライブラリのネーミングセンスが死んでいる…。
[^2]: 驚いたことにニコキャスの枠の情報も取得できてしまいます…内部実装を考えたくない…。
[^3]: ざっと見た感じ新配信ではエイリアスのようなシステムはなく、各lv番号のページからリダイレクトするというシステムのようです。
[^4]: ご存知のかたも多いとは思いますが、視聴ページではコレと同じ様に <http://live.nicovideo.jp/watch/ch2631252> のような形で接続することができます。
[^5]: 新配信ではWebSocketも使用できるようになっているみたいですが、ここでは取り扱いません。
[^6]: 編集履歴を見ると例のSlackにいらっしゃる方は見たことがあるかもしれない名前が…うわなにをするやめr