ChatWork APIで、チャットワークがもっと便利になるChrome拡張を作ってみた


ChatWorkはとても便利なのですが、投稿のリプライ回数が多くなると、話の流れを把握するのに苦労することがあります。「RE」ボタンを押せばポップアップが表示されますが、10件20件と追っていくのはとても辛いのです。

そこで、ChatWork APIを利用し、リプライを自動で辿ってまとめて表示するChrome Extension「ChatWork Reply Tracer」を作ってみました。

（追記：タイトル修正しました）

## 元記事

下記エントリの転載になります。

* [ChatWork APIで、チャットワークがもっと便利になるChrome拡張を作ってみた](http://www.rokurofire.info/2016/02/08/cw_chrome_ext/)

## ChatWork Reply Tracer

![cw_api.gif](https://qiita-image-store.s3.amazonaws.com/0/10172/fff08871-433f-9450-74c5-1fe4ae94510e.gif "cw_api.gif")

replyをたどって、つながりのある投稿をまとめて表示するという機能です。

* Extensionを有効にすると、replyのある投稿右側に「trace」ボタンが表示される
* 「trace」ボタンをクリックすると、APIを叩いてreply投稿をたどり、replyの無い投稿までを取得
* ページ右側に、取得した投稿だけを表示

「trace」を押した投稿より上の古い投稿のみを検索しています。
また、投稿内に複数のREが合った場合、1個目のみしか辿っていません。APIの実行回数上限がそこまで多くないため、今回は分岐して辿る処理は作っていません。

## ソースコード

* [github](https://github.com/hirokiy/chatwork-extention-reply_tracer)

## Chatwork APIについて

* [チャットワークAPIドキュメント](http://developer.chatwork.com/ja/)

プレビュー版が提供されており、APIの利用には申請が必要です。

<blockquote>
APIトークンでのAPI認証はご本人自身による利用を想定しており、チャットワークAPIを利用したWebサービスなどを運用されたい場合は、今後提供予定のOAuth 2.0による認証方式を使用してください。
</blockquote>

APIトークンによる認証のため、このChrome Extensionはパッケージ配布していません。
Extensionのbackground画面でトークンを入力させる方法もありますが・・・"開発者向けAPI"とのことなので、そういった使い方はどうかと思い躊躇。（いいのかな・・・）

ソースコードは公開していますので、興味のある方は自由にフォークしてください。

## APIの叩き方

### エンドポイント

* [エンドポイント: /rooms](http://developer.chatwork.com/ja/endpoint_rooms.html)

リプライ投稿を取得したいので、利用するエンドポイントは /rooms で、以下のようになります。

```
/rooms/{room_id}/messages/{message_id}
```

ajaxでのアクセスも "headersに「キー名：X-ChatWorkToken」を利用する" という点以外はいたって普通です。

```JavaScript
// rid：チャットのID
// mid：投稿のID

jQuery.ajax({
    url: "https://api.chatwork.com/v1/rooms/" + rid + "/messages/" + mid,
	headers: {
		'X-ChatWorkToken': "xxxxxここにトークンが入りますxxxxx"
	},
    type: 'GET',
	json: true
}).done(function (data, status, xhr) {
	if (xhr.status === 200) {
		// 正常処理
	} else {
		// 例外処理
	}
	return true;
}).fail(function (data, status, xhr) {
	// エラー時の処理
	return false;
}).always(function () {
    return true;
});
```

### レスポンスヘッダ

レスポンスヘッダには、API利用回数上限などの情報が書かれています。

```
...
X-RateLimit-Remaining: 66			// APIの残りコール可能回数
X-RateLimit-Reset: 1454854074	// APIの利用回数がリセットされる時間
X-RateLimit-Limit: 100			// APIの利用回数上限
...
```

### 利用回数制限について

2016年2月現在、APIのリクエスト数は、5分あたり100回までとなっています。
今回は、手動で実行するタイプのツールなため、ぱっと見で残りの回数が分かるようにしています。

![cw_ex01.jpg](https://qiita-image-store.s3.amazonaws.com/0/10172/288bec1c-857e-949a-b726-c5e3af8e112b.jpeg "cw_ex01.jpg")


```JavaScript
// 取得方法
xhr.getResponseHeader("X-RateLimit-Remaining");
xhr.getResponseHeader("X-RateLimit-Limit");
```

## メインの処理の流れ

* **1：**タイムラインが表示されたら、緑色の「RE」ボタンのある投稿を探す
* **2：**各投稿の「RE」ボタンのdata属性にあるridとmidを取得。「trace」ボタンオブジェクトを作成し、clickイベント（APIを叩くイベント）を追加。ボタンのHTMLも出力。
* **3：**「trace」がクリックされるとAPIを再帰的に叩き、リプライの無い投稿にたどり着いたらHTMLを右側のエリアに出力

APIの叩き方が分かれば、あとはDOMを見て力技です。
思いっきりHTMLの構造に依存するため、構造に変更があった際は修正必至です。

### レスポンスデータの処理

APIを叩くと、以下の様なjson形式で情報が帰ってきます。

```
{
  "message_id": 9999999,
  "account": {
    "account_id": 123,
    "name": "Bob",
    "avatar_image_url": "https://example.com/ico_avatar.png"
  },
  "body": "[rp aid={account_id} to={room_id}-{message_id}] テキストテキストテキストテキストテキスト",
  "send_time": 1384242850,
  "update_time": 0
}
// {account_id}：リプライした投稿の投稿者ID
// {message_id}：リプライした投稿の投稿ID
```

bodyの中に “[rp aid={account_id} to={room_id}-{message_id}]” という記述が存在するかを正規表現で判定し、存在する場合は message_id を抜き出します。

この message_id は、リプライ先の投稿IDになっていますので、今度はこの message_id を元にAPIを叩きます。

bodyに “[rp〜〜]” が存在しない場合、リプライをたどるのを終了します。

### タイムラインの変更を監視

ChatWorkはスクロールするとタイムラインのHTMLが書き換えられてしまいますので、タイムラインのHTML変更を検知する必要があります。

```JavaScript
var observeTgt = document.getElementById("_timeLine");
var observer = new MutationObserver(function (mrecords, mobserver) {
	var checkChange = function (x) {
		if (x.addedNodes &&
			x.addedNodes instanceof NodeList &&
			x.addedNodes.length > 0 &&
			x.type === 'childList') {
			return true;
		}
		return false;
	};
	
	if (mrecords.some(checkChange)) {
		// イベント実行
	}
});

// 子要素のみを対象、孫要素以下は検知しない
observer.observe(observeTgt, {childList: true, subtree: false});
```

タイムラインのHTMLが変更されると、 **1**、 **2**の処理が再度実行されます。
この時、監視対象の子要素のみにしないと、無限ループが発生するので要注意です。
（タイムラインHTML変更後にtraceボタンを孫要素以下のDOMに追加しているため）

こちらを参考にしています。

<ul>
<li><a href="http://d.hatena.ne.jp/IT7C/20151222/1450771078" target="_blank">http://d.hatena.ne.jp/IT7C/20151222/1450771078</a></li>
</ul>

## まとめ

HubotとAPIを連携させるのが流行ってるみたいですが、今回はもう少し手軽にフロントのみで動作するものを作ってみました。
とても簡単に利用できるAPIですので、気に入らない点は自分でガンガン機能追加できそうです。

今後OAuth 2.0形式でのAPI提供を予定しているみたいですのでそちらも期待です。