研究室でHubot使ってる話

うどん大学 情報系 院1年の堀内です．
[Hubot×ChatOps勉強会](http://connpass.com/event/9370/) をやったりしています．

この記事は [Hubot Advent Calendar 2014](http://www.adventar.org/calendars/384) の12日目です．
@bouzuya さんが[Hubotの解説を連載](http://qiita.com/bouzuya/items/c7d0ad80c357aab6b696)していて，基礎から勉強させてもらってます．

今回は，研究室のチャットルームに居る [Huboco](https://github.com/hico-horiuchi/huboco) ちゃんの話をします．
(チャットは [Typetalk](https://typetalk.in/) を使っています．)

![icon.png](https://qiita-image-store.s3.amazonaws.com/0/58742/8caf6abe-56c3-8a30-1cc4-51fa696f50f4.png)

## ゼミ日程と発表者を通知

僕の所属する [最所研究室](http://air.eng.kagawa-u.ac.jp/) では，週に1回，全員が集って進捗報告のゼミをしています．
後期からは，2人ずつプレゼン形式で報告するようになりました．(発表練習，資料作成のため．)

ラボメンから以下のような要望があったので，『ゼミ日程と発表者を通知』するscriptを作りました．
([今期のアニメを教えてくれる](https://github.com/hico-horiuchi/huboco/blob/master/scripts/anime.coffee)くらいしか能がなかったHubocoちゃんに仕事をしてもらうため．)

  - ゼミの日程変更を教えて欲しい．(前日に通知して欲しい．)
  - 3週に1回，発表担当になるが，忘れてしまう．


コマンド形式の [semi.coffee](https://github.com/hico-horiuchi/huboco/blob/master/scripts/semi.coffee) と，cronで通知する [semi_cron.coffee](https://github.com/hico-horiuchi/huboco/blob/master/scripts/semi_cron.coffee) に分けています．
(思ったよりコードが肥大化したため．bouzuyaさんに怒られそう．)
ここでは，ゼミの日程や発表者をjson形式で保存しておき，それをHubotで読み込んで使ってます．

```json
{
  "day": 3,
  "start_at": "14:40",
  "end_at": "16:10",
  "changes": [
    { "from": "2014/12/10(水)", "to": "2014/12/09(火) 10:30～12:00" }
  ],
  "presen": {
    "start_at": "2014/12/10",
    "rotate": 2,
    "users": [
      ["ぼうずや"],
      ["ひこにゃん"]
    ],
    "accounts": [
      ["@bouzuya"],
      ["@hiconyan"]
    ]
  }
}
```

実際に使うとこんな感じ．

![semi.png](https://qiita-image-store.s3.amazonaws.com/0/58742/8cec4c6d-d1e9-a95a-7a30-206ded94692f.png)


前日になると発表者を煽るリプライ飛ばして決ます．

![semi_cron.png](https://qiita-image-store.s3.amazonaws.com/0/58742/8354e557-8a2d-ca31-d17d-949f5fe88162.png)

## 卒論の締切を通知

つい先日4年生から，あるリクエストを受けた．

> 「卒論の締切まで何日か，毎日通知してくれる機能が欲しい」

せっかくなので修論の締切も教えてくれるように実装してみる．
cronと [monent.js](http://momentjs.com/) を使えば，簡単にできるかな．
`hubot thesis bachelor 2015/02/10` で締切を設定，`hubot thesis` で何日か表示する．

```coffeescript
# Description:
#   卒業論文・修士論文の締切を通知
#
# Commands:
#   hubot thesis - 締切までの日数を表示
#   hubot thesis bachelor <YYYY/MM/DD> - 卒論の締切を設定
#   hubot thesis master <YYYY/MM/DD> - 卒論の締切を設定
#
# Configuration:
#   HUBOT_THESIS_ROOMS: 締切を通知する部屋

cron = require('cron').CronJob
moment = require 'moment'
moment.locale 'ja'

module.exports = (robot) ->
  THESIS = ['bachelor', 'master']
  THESIS_JP = { bachelor: '卒論', master: '修論' }

  say = (room, message) ->
    envelope = room: room
    robot.send envelope, message

  dead_line = ->
    brain = robot.brain
    return false unless brain.data.thesis

    str = []
    today = moment()
    for key in THESIS
      date = brain.data.thesis[key]
      if date
        days = parseInt (moment(date, 'YYYY/MM/DD') - today) / 86400000
        str.push "#{THESIS_JP[key]}の締切まで #{days} 日"
    return str.join('、') + 'です'

  robot.respond /thesis$/i, (msg) ->
    str = dead_line()
    msg.reply str if str
    msg.reply '締切が設定されていません' unless str

  robot.respond /thesis\s+([a-z]+)\s+([0-9\/]+)$/i, (msg) ->
    key = msg.match[1]
    return unless key in THESIS
    date = msg.match[2]
    return unless date

    brain = robot.brain
    brain.data.thesis = {} unless brain.data.thesis
    brain.data.thesis[key] = date
    brain.save

    msg.reply "#{THESIS_JP[key]}の締切を #{date} に設定しました"

  new cron '0 0 10 * * *', ->
    rooms = process.env.HUBOT_THESIS_ROOMS
    return unless rooms
    str = dead_line()
    return unless str
    for room in rooms.split(',')
      say room, str
  , null, true, 'Asia/Tokyo'
```

実際に使うとこんな感じ．

![huboco_thesis.png](https://qiita-image-store.s3.amazonaws.com/0/58742/b541fdbd-61f3-08a2-bf20-0eacc3b4e296.png)

## まとめ

研究室で，ゼミや卒論の通知にHubotを使ってる話でした．

Hubot歴は4ヶ月くらいになるけど，CoffeeScriptを綺麗に書けない…．
bouzuyaさんも言ってたけど，scriptはAPIを叩くとか，ミニマムな方が良いですね．

明日からはbouzuyaさんの連載が再開されます．
楽しみですね!

## 追記

```coffeescript
THESIS = ['bachelor', 'master', 'doctor']
THESIS_JP = { bachelor: '卒論', master: '修論', doctor: '博論' }
```

このようにすると，博士論文にも対応できます．
B4，M2，D3のみなさん，論文執筆がんばってください!