HubotでSlackのChannelとTrelloのBoardを連携


10/31(土)開催の香川大学工学部祭の実行委員会が発足しました．
情報系学科のメンバが多いため，[Slack](https://slack.com/)と[Trello](https://trello.com/)を導入して，脱LINEのモダンなチーム運営を目指しています．

Hubotを使ってSlackのChannelとTrelloのBoardを連携させてみたので，紹介します．

## 前提条件

Slackでは「stage(ステージ企画)」や「public(広報)」など，担当毎にChannelを分けています．
Trelloでは実行委員会のOrganizationを作成し，同様に「Stage」や「Public」など，担当毎にBoardを分けています．

ここで言う連携とは，「stage」ChannelでHubotを使ってTrelloを操作した場合，「stage」Boardに反映されるということです．
HubotでBoardを指定する必要が無くなり，直感的に連携できるかなと思います．

## 実装方法

HubotからTrelloを操作するために[node-trello](https://github.com/adunkman/node-trello)を使っています．
ここでは`hubot trello list <list>`で，指定したListのCard一覧を返すコマンドを示します．
(Listは「Todo」「Doing」「Done」の3段階に設定しています．)

node-trelloは非同期で結果が帰るため，API毎に関数を分け，配列に積んで順に処理するようにしています．
コードに多少コメントを付けているので，参考にして下さい．

```coffeescript
trelloAPI = require('node-trello')

module.exports = (robot) ->
  ORG = process.env.HUBOT_TRELLO_ORGANIZATION

  trello = new trelloAPI(
    process.env.HUBOT_TRELLO_API_KEY
    process.env.HUBOT_TRELLO_API_TOKEN
  )

  # OrganizationのBoard一覧を取得
  # https://trello.com/docs/api/organization/index.html#get-1-organizations-idorg-or-name-boards
  getOrganizationsBoards = (msg, args) ->
    url = "/1/organizations/#{ORG}/boards"
    trello.get url, (err, data) =>
      for board in data
        # BoardをChannel名で特定 (Stage, Publicなど)
        if board.name.toLowerCase() is msg.envelope.room
          args['boardID'] = board.id
          return args['callbacks'].shift()(msg, args)

  # BoardのList一覧を取得
  # https://trello.com/docs/api/board/index.html#get-1-boards-board-id-lists
  getBoardsLists = (msg, args) ->
    url = "/1/boards/#{args['boardID']}/lists"
    trello.get url, (err, data) =>
      for list in data
        # Listを名前で特定 (Todo, Doing, Doneなど)
        if list.name.toLowerCase() is args['listName']
          args['listID'] = list.id
          return args['callbacks'].shift()(msg, args)

  # ListのCard一覧を取得
  # https://trello.com/docs/api/list/index.html#get-1-lists-idlist-cards
  getListsCards = (msg, args) ->
    url = "/1/lists/#{args['listID']}/cards"
    trello.get url, (err, data) =>
      names = []
      for card in data
        names.push(card.name)
      msg.reply("```¥n#{names.join('¥n')}¥n```")

  robot.respond /trello\s+list\s+(\S+)$/i, (msg) ->
    # getOrganizationsBoards, getBoardsLists, getListsCardsの順で呼出
    getOrganizationsBoards(msg, {
      callbacks: [getBoardsLists, getListsCards]
      listName: msg.match[1]
    })
```

Slackで動かすと，このようになります．
(実際には[easy-table](https://github.com/eldargab/easy-table)を使って，表形式でDueやMembersを表示しています．)

![trello_list.png](https://qiita-image-store.s3.amazonaws.com/0/58742/8b98d358-3280-097f-1935-79a678eedb0c.png)

## Kaonashi

上記のコードは[Kaonashi](https://github.com/hico-horiuchi/kaonashi)という名前のBotとして運用中です．
他にも，下記のようなTrelloとの連携機能を実装しているので，ぜひご覧下さい．

  - [kaonashi/trello.coffee at master · hico-horiuchi/kaonashi · GitHub](https://github.com/hico-horiuchi/kaonashi/blob/master/scripts/trello.coffee)

```
hubot trello list    <list>          - カードの一覧を表示
hubot trello add     <list> <name>   - カードをリストに追加
hubot trello move    <list> <card>   - カードをリストに移動
hubot trello show    <card>          - カードの詳細を表示
hubot trello archive <card>          - カードをアーカイブ
hubot trello comment <card> <text>   - カードにコメントを追加
hubot trello assign  <card> <member> - カードに担当者を追加
hubot trello due     <card> <date>   - カードに締切を設定
hubot trello member                  - ボードのメンバーの一覧を表示
hubot trello member  <name>          - メンバーが担当するカードの一覧を表示
```

## Hubot×ChatOps勉強会

08/22(土)に神戸で[第2回 Hubot×ChatOps勉強会](http://hubot-chatops.connpass.com/event/17902/)を開催します!
HubotやChatOpsに興味のある方，Pluginを開発している方など，ぜひお越し下さい．
発表して頂ける方も募集中ですので，[@hico_horiuchi](https://twitter.com/hico_horiuchi)まで気軽にお声掛け下さい．

![4f4f7c00342367d0b2b51d51510dab0a.png](https://connpass-tokyo.s3.amazonaws.com/thumbs/4f/4f/4f4f7c00342367d0b2b51d51510dab0a.png "4f4f7c00342367d0b2b51d51510dab0a.png")