Hubot+bitbucket+HipChat+ReviewBoardでレビューガチャを作成した


# はじめに
弊社ではGitのホスティングサービスとしてBitbucketを、チャットとしてHipChatを、レビューツールとしてReviewBoardを使用しています。
今回は、レビュアーを自動でガチャるレビューガチャを上記3システムのAPIを駆使して作成したので紹介します。
なお、仕様が弊社向けにかなり特殊になっているため特にコードの公開などは行いません。
API/batchはLaravel5で,hubotはcoffescriptで作成しています。

# 前提
## Bitbucket
Bitbucketのユーザはマージ権限を持つGeneralとマージ権限を持たないRookieの2種類どちらかの権限を所持しています。また、Bitbucketユーザにはエンジニア以外にデザイナーも存在します。

## HipChat
HipChatにはエンジア以外にデザイナーや企画のメンバーもいます。

## ReviewBoard
レビューボードのユーザは、自分が所属するグループに分類されています。

# 仕様
エンジニアがhipchatでレビュアーを選択したいとなったときに、
@hubot reviewerとすることで、ランダムにレビュアーが2人選ばれることとします。
ただし、レビュアーは以下の条件を満たす人が選ばれることとします。
・自分が所属するグループと所属しないグループから1人ずつ選択する
・自分自身は自分のレビューのレビュアーに選ばれない
・BitbucketのRookie権限のユーザはレビュアーに選ばれない
・非エンジニアはレビュアーに選ばれない
また、レビュアーに選ばれた時にはメンション付きで呼ばれることとします。

# 各サービスで必要な情報
## hipchat
|要素|説明|
|:--:|:--:|
|user_name | hubotの引数としてuser_nameが与えられるため|
|mention_name | hipchatでメンション付きで呼びたいため。|
|email        | 各サービスを示す一意のIDとしたいため|
|hipchat_id   | hipchat上のid|
|hipchat_private_api_url|hipchatで個人のユーザ情報を取得するapiURL|

## reviewboard
|要素|説明|
|:--:|:--:|
|group|そのユーザが属するグループ判断のため|

## bitbucket
|要素|説明|
|:-:|:-:|
|role|そのユーザがgeneralかrookieか|

# 詳細設計
前処理/実処理をやる都合上batch処理で前処理を行い実処理はapiを1つ作成して行う
## 前処理
1. メンションネームとそのユーザのid,メールアドレス,user_nameをhipchatから取得
2. ユーザのグループ情報をReviewboardから取得
3. ユーザのroleをbitbucketから取得

## 実処理
1. hipchatでメンションをつけて飛ばすとそのユーザ名が引数として渡される(User Name)  
2. coffescript側で引数をパラメータとして与えてapiを叩く  
ex:bot-api.example.com/user_name  
3. 出力
の形をとっています。 
(画像を貼る)

# 各種情報取得API
## hipchat API
### userAPI
```
http://api.hipchat.com/v2/user?auth_token=
```
auth_tokenは以下の記事で紹介したように取得する
http://qiita.com/takky/items/51f8b2942437c9897ee1

これでuserの基本的な情報を取得できる。
https://www.hipchat.com/docs/apiv2/method/get_all_users/

なお返却情報はjsonで以下のような形式になる

```json
items: [
{
id: 12345678,
links: {
self: "https://api.hipchat.com/v2/user/12345678"
},
mention_name: "TT",
name: "TANAKA TARO"
},
{
id: 12346586,
links: {
self: "https://api.hipchat.com/v2/user/12346586"
},
mention_name: "D",
name: "Yamada Taro"
},
```

```
https://api.hipchat.com/v2/user/12345678?auth_token=
```

上記APIでユーザの個別情報を取得できる。

```json
{
created: "2013-04-11T02:13:25+00:00",
email: "t_tanaka@email-address.com",
group: {
id: 76929,
links: {
self: "https://api.hipchat.com/v2/group/{group_id}"
},
name: "{Team Name}"
},
```

これで全ユーザのメンションネーム/メールアドレス/ユーザーネームが取得できる

## ReviewboardAPI

```
http://{reviewboard_server}/review/api/groups/{group}/users/
```
https://www.reviewboard.org/docs/manual/2.5/webapi/2.0/resources/review-group-user-list/
reviewboardは自社サーバにあるため、自社サーバのアドレスを指定している
reviewboardのAPIはデフォルトでXMLを返却するのでContent-Typeはapplication/jsonにする必要がある

jsonを指定した場合の返却情報は以下のようになる

```
{"username": "tanaka_taro", "first_name": "tanaka_taro", "last_name": "", "links":{"watched":{"href": "http://{reviewboard_server}/review/api/users/tanaka_taro/watched/",…},
```

## bitbucketAPI
```
https://api.bitbucket.org/1.0/groups/{team_bitbucket_admin_user}
```
bitbucketAPIは id/passが必要なので、
CURLOPT_USERPDで id:passで送ること。
jsonでteam_adminの下にいるroleとそのroleに紐づくユーザが取得可能

```
   "name": "general",
    "permission": "write",
    "email_forwarding_disabled": null,
    "auto_add": true,
    "members": [
      {
        "username": "mem-t_tanaka",
        "first_name": "TANAKA",
        "last_name": "TARO",
        "display_name": "TANAKA TARO",
        "avatar": "{avartor_url}",
        "resource_uri": "/1.0/users/mem-t_tanaka",
        "is_team": false
      },
```

# hubot実装
基本的に

```
robot.respond /select reviewer/i, (msg)->
	user = msg.message.user.name
    tmp = user.split(" ")
    user = tmp[0] + "_" + tmp[1]
    url = "http://bot-api.com/reviewers/#{user}"
```
のようにAPIを叩いて、返却結果を出力するだけ。
ここで、ポイントは@hubot select reviewerすると、
Tanaka Taro
のようにhipchatに登録したユーザ名が変数userに入る点。
apiを叩く際にスペースが入っていると叩けないため、_で連結して叩くようにしている。

# api実装
http://bot-api.com/reiewers/Tanaka_Taro で叩くと、

```json
{"data":[{"hipchat_mention_name":"A-SAN"},{"hipchat_mention_name":"B-SAN"}],"my_mention_name":[{"my_mention_name":"WATASHI"}]}
```
のように返却される

方法は、
・DBから自分のユーザ名で自分が所属するユーザリストをSELECT
・DBから各グループごとに自分以外のユーザをSELECT
・コントローラで上の結果からランダムに1人ずつ取得
・DBから自分のmentionnameをSELECT
という流れで取得してきている。

hubot側は上記jsonをパースして@hipchat_mention_nameで返却すれば
botがレビュワーを選択したようになる。

# Q&A
Q.各種APIからの情報取得はどのタイミングで行っているの？
A.毎月定期的にbatchを動かして取得している
Q.なんでhubotから直接各種API叩かないの?
A.hipchatのAPIのlimit が 100アクセス/5分で弊社のユーザ数だと5分以内に2回目叩いた時にAPIが叩けなくなるから