ブログ書いてちゃんボットを作ってみた

この記事は「ボット (Bot) Advent Calendar 2017」 の24日目の記事です。
今日はクリスマスイブですねー！...深夜にモクモク書いてます。

# はじめに

今回はブログを書いてーとリマインドするボットを作ってみました。
というのも、今私が働いている会社には[技術ブログ](https://tech.ryukyu-i.co.jp)があるのですが如何せん書いてくれる人が少ないということでそこを改善できないかなーということで作ってみました。
月に一記事ということで担当者を月ごとに決めてリマインドするというのが基本です。
Hubot を使って実装しました。(初めて使いましたが他にいいフレームワークあったりするんですかね？)


まず一番気になるところは、むさ苦しいボットアイコンからリマインドされても全く乗り気しないしテンションも上がらない(?)というところなので、かわいいアイコンを用意しました！

![icon.png](https://qiita-image-store.s3.amazonaws.com/0/26206/f5435dd3-cde8-568a-7ad5-fdb8014bbeb6.png)

ブログ書いてちゃんです(仮称)
これでメンション来てもテンション爆上げですね！！ (謎)

Github にソースは公開してます。
https://github.com/ameyamashiro/write-a-post-bot

動作はこんな感じです。

<img width="790" alt="スクリーンショット 2017-12-24 2.12.48.png" src="https://qiita-image-store.s3.amazonaws.com/0/26206/d511dd6f-0f45-c255-c226-f33bcc2cc444.png">


# 仕様

実装が必要な機能をざっくりリストします。

- 担当者を選んで書いてーとメンション
- URL 付きでボットへメンションすることで記事を書いたとみなす
- 書いてなければちょくちょくリマインドする
- データ保存

## 適当に選んでこの期間に書いてーとメンション

月一でメンバーリストの中からその月の担当者を決めてメッセージを送ります。
この機能を実現んするためにはメンバーのリストが必要です。
次に期間で担当者を決定するので期間の管理も必要です。具体的には月を跨いだ時点でその月の担当者をランダムで決定します。

## URL 付きでボットへメンションすることで記事を書いたとみなす

単純に `@bot http://example.com/post` という感じでメッセージがあれば記事を書いたとみなして URL を保存して完了です。
`robot.hear` で受けます。

## 書いてなければちょくちょくリマインドする

単純に金曜日に書いてなければメッセージという感じにします。
実装するには定期的に実行する仕組みを用意する必要があります。
システムの cron や node-cron などありますが、今回はシンプルに setTimeout を使います。

## データ保存

通常データを保存するには MySQL やら MongoDB やらを使うと思うのですが今回みたいなちょろっとしたやつだとチョット多がかかりすぎる感じがするので今回はローカルのファイルで完結できる [NeDB](https://github.com/louischatriot/nedb) を使います。
(最終更新は 2年前になっているので簡単なプロジェクト以外では使わない方がいいかもしれません)

# 実装

細々書いてもあれなのでチョット変則的なところだけをまとめます。

## ユーザーの追加

Bot インターフェースからできた方がいいのですがまだ用意していないので現状では直接ソースを弄って以下のコードを追加して行います。

```coffeescript
db.members.insert [
  { id: 'a-san' }
  { id: 'b-san' }
  { id: 'c-san' }
]
```

id には slack 上での @ メンションで使う文字列を指定します。
そうすると members.db へ追加されます。

## @ メンション付きメッセージの送信

単純に `@robot.messageRoom` などに @ 付きの文字列を渡しても Slack にはただの文字列と解釈されてしまいます。
なので `<@id> メッセージ` という形式で指定する必要があります。
ただ、id には slack で通常チャットで入力するときのメンションで使うものではダメで slack でのユーザー毎に割り当てられている UUID のようなものを指定する必要があります。
slack adapter を使っている場合は `@robot.brain.data.users` でリストが取得できるので以下のようなメソッドを用意してあげると楽です。

```coffeescript
  getUserId: (name) =>
    users = @robot.brain.data.users
    for key of users
      if name == users[key].real_name
        return users[key].id
```

```coffeescript
@robot.messageRoom roomId, "<@#{@getUserId(to)}> あれ？すでに書いてますよね！？次回もよろしくね！"
```

## 次の金曜日の setTimeout 設定

毎週金曜日にリマインドを行うので setTimeut に次の金曜日までの時間を指定してあげる必要があります。
まず次の金曜日を取得する方法ですが、金曜日までの日数を単純に取得します。
JS だと new Date().getDay() で 0 ~ 7 で曜日が取得できます。
0 = 日, 7 = 土 です。金曜日は 5 になります。なので 5 - 現在の曜日で次の金曜日までの日数が取得できます。

```coffeescript
(5 - new Date().getDay()
```

次は日時の進め方ですが単純に日の数字を進めるだけだと月を跨いだときにおかしなことになるかもしれないので、現在日時を Epoch へ変換して数字を加算します。

```coffeescript
d = new Date(Date.now() + ((5 - new Date().getDay()) * 1000 * 60 * 60 * 24))
```

時刻はとりあえず AM 11:00 固定にしたかったので上で取得した Date を分割してまた Date へ投げました。

```coffeescript
nextDate = new Date(d.getFullYear(), d.getMonth(), d.getDate(), 11, 0)
```

ここまでで次の金曜日の Date オブジェクトが取得できたので最後に setTimeout に指定できるようにミリ秒へ変換して終了です。

```coffeescript
nextTime = nextDate.getTime() - Date.now()
setTimeout routine, nextTime
```

正直一度もテストしていないので問題なく動作するかはまだ確認できてません。

# 終わりに

このボット勝手に作っただけなので、ちゃんと運用できるかやメンバーに受け入れられるかもまだ未検証ですｗｗ
メンバーの登録が簡単にできないのでまだちょっとだけタスクはありますが更新されなかった場合は、、まあ察してくださいませ(´∀｀; )