初心者がHubotを使ってHeroku上で動くSlack bot作ってみた(実践編)

  • 4
    いいね
  • 0
    コメント

早稲田大学Advent Calendar 17日目の記事でございます。かしこ。(遅くなりました・・)

前回: 初心者がHubotを使ってHeroku上で動くSlack bot作ってみた(導入編)
次回: 初心者がHubotを使ってHeroku上で動くSlack bot作ってみた(発展編)(4月中には・・・)

非エンジニアである初心者がSlack botを作るべく奮闘した話です。
Github社のすばらしいbotツールを用いて、動作サーバはHerokuをお借りし、Slackで動くbotを作ったわけです。(2016年12月仕様です)

初心者が初心者の目線で、初心者のために書きます。
仮に知らない単語が出てきても気にせず、とりあえず突き進んで導入してみることをお勧めします。あとでわかりますたぶん。

9日目と連載という形で、タイトルのようなbotを作っていきます。
前回の「導入編」はこちらです!
初心者がHubotを使ってHeroku上で動くSlack bot作ってみた(導入編)
(Herokuの料金体制の変更について、加筆・修正してありますのでそちらもご確認くださいませ。)

さてさて今回は、実践編です。
コードを書いて、みくりさんをいじっていきます!

またまた最後まで書いて気づいたのですが、長くなってしまったのでお話を分けて、redisやWebAPI利用などの機能実装をする「発展編」を書くことにしました。

今回は実装の手順と、基本機能(しかし無限大の用途がある)実装を書きました!


※ なお、こんな記事はごまんとあるのですが、今話題の「まとめ」のような感じですので、一度に全て確認できるのはこちらだけでございます。お楽しみください。

【動作環境】

  • OS : Mac OS X El Capitan 10.11.15
    • Node.js : 6.8.1
    • npm : 3.10.8
    • Yeoman : 1.8.5
    • heroku-toolbelt : 3.42.22 (x86_64-darwin10.8.0)
    • CoffeeScript : 1.11.1
  • hubot : ^2.19.0
    • hubot-slack : ^4.2.2
    • hubot-heroku-keepalive : ^1.0.2

前回のあらすじ

  • Yeoman で Hubot をインストールした
  • git 管理して、 Heroku サーバにデプロイした
  • Heroku の無料プランの隙をつく設定(労働時間の設定)をして、Slack とつないだ

Hubot の開発について

Hubot の開発言語

Hubot を開発していくにあたって、プログラミング言語は何を使うのかといいますと、基本的に CoffeeScript を使用していきます!

CoffeeScript とは、JavaScript を基にして、コードをより短く手間なく書くことができるように作られた比較的新しい言語です。コンパイルすると JavaScript のコードに自動で変換されて実行されます。

なお、そのため当たり前なのですが単純に JavaScript で記述することもできます。
ので、 CoffeeScript があまり好きではない方、JavaScript のが書けるという方は、そちらでどうぞ!

一応、Hubot のデフォルトスクリプトは CoffeeScript で記述されており、オススメという感じになっているので私自身はこちらを使っています。お好きな方で。笑

本記事は、CoffeeScript で説明していきます!



CoffeeScript の公式ページはこちらです。
[ TRY COFFEESCRIPT ] ページで、実際に書いてみることができます。

Hubot プロジェクトのディレクトリ構造

Hubot の mikuri プロジェクトのディレクトリは以下のような構造になっています。

mikuri
│
├── bin
│    ├── hubot
│    └── hubot.cmd
├── Procfile
├── node_modules
│    └── ... (中にはたくさんのディレクトリがあります)
├── external-scripts.json
├── hubot-scripts.json
├── package.json
├── scripts
│    └── ******.coffee
└── README.md

簡単に説明するとこんな感じです。

・ bin : 実行ファイル

・ Procfile : 実行命令 (実行ファイルを動かす命令)

・ node_modules : npm でインストールされた hubot スクリプトのモジュールたち

・ external-scripts.json : インストールしたモジュールを使う時にそのモジュールを記述するところ

・ hubot-scripts.json : Hubot の公認コミュニティスクリプトである hubot-scripts のリポジトリにあるスクリプトを使う時にそのスクリプトを記述するところ

・ package.json : プロジェクトや各モジュールなどの package を管理する node.js の設定ファイル

・ scripts : 独自のスクリプトを入れておくところ。起動した時点でここのスクリプトは読み込まれる

・ README.md : マークダウン式のreadmeです。

何を言っているんだって感じだと思います。僕もそう思います。
細かいことは気にしないほうがいいです。
でも今後説明を読んでいって、後々見返すとわかります。たぶん。

結局、初心者がこの中で使うディレクトリは一部ですので、このまま主な実装方法をごらんください。

主な実装方法

Hubot を実装するには、主に以下の3つの方法があります。

① デフォルトのスクリプトやモジュールを利用する

Hubot には多くのスクリプトやモジュールがデフォルトでnode_modulesにインストールされています。
(だいたいこれで事足りますってくらい充実してます)
これらをつかったり、コードを一部書き直したりすることで一からコードを書かずとも機能を拡張することができます。

bot のキャラに合わせて返答を書き直す必要があったりするのでコードに慣れてからの方がいいと思います。

② モジュールをnpmインストールして利用する

デフォルトに入っていなくても、Hubot のモジュールは、多くの先人たちが多くの遺産を残してくれています。
星の数ほどの便利モジュールが転がっているので、自分で実装してみるまえにググったらしたら大抵でてきたりします。

これらのモジュールをインストールして利用することで、機能を拡張することができます。

こちらも一部書き直したりしたほうがより便利に使用できますので、コードを読める、かけるといいと思います。

③ 自分でスクリプトを書く

Hubot でやりたい機能を考えて、それを実際にコードで記述しスクリプトを作っていきます。
先述したように言語は CoffeeScript です。

簡単な機能ならscriptsディレクトリに、自作の .coffee ファイルを置くことで実装ができます。


初心者の学習の順番としては、③ → ① → ② がオススメです!

③である程度コードに慣れ、コードを読めるようになってから、デフォルトや新たなスクリプトコードを読んでみて、使えそうだったらJSONファイルを書き換えて機能を追加するという方法をとるのがベストです!

玄人の方は、早速①、②をうまく使うことでかなり素早く実用的な bot を作ることができます。これが Hubot のいいところです。

Hubot はいいぞ!

さて、ここまでが前置きでした。長いですね・・・
では実際にスクリプトを書いていきましょう!

Hubot スクリプトを書いて、実行してみる

早くみくりさんに喋らせたい!!
さっそくスクリプトを書いていきましょう!

流れとしてはこんな感じです。

① ファイルを作る

② コードを書く

③ 説明を書く

④ ローカルでテストする

⑤ git にコミット&プッシュする & Heroku にデプロイする

⑥ 実行してみる

新規スクリプトファイルを作成する

ファイルの作成

mikuriプロジェクトのなかにあるscriptsにファイルを追加します。
greet.coffeeというファイルを作ってみましょう。

まずはmikuriプロジェクトまで移動して以下を続けます。

$ cd scripts

$ touch greet.coffee

greet.coffeeファイルができました!

TomDocを書く

ファイルを作ったら、次に以下のように記入します。ここからコピペでいいです。

greet.coffee
# Description:
#   <description of the scripts functionality>
#
# Dependencies:
#   "<module name>": "<module version>"
#
# Configuration:
#   LIST_OF_ENV_VARS_TO_SET
#
# Commands:
#   hubot <trigger> - <what the respond trigger does>
#   <trigger> - <what the hear trigger does>
#
# Notes:
#   <optional notes required for the script>
#
# Author:
#   <github username of the original script author>

これは、 TomDoc と呼ばれる作ったスクリプトの説明文になります。
それぞれ訳すとこのような感じになります

・ Description : このスクリプトの説明文
・ Dependencies : 依存関係の記述場所 (基本的に None でいいです) -> "モジュールの名前": "バージョン"
・ Configuration : 環境設定 (基本的に None でいいです)
・ Commands : トリガー(起動コマンド)と起動すると何が起こるかを書きます
・ Notes : メモメモ
・ Author : 作成者(githubのusername)

一応公式ではこれを書きなさいと言われていて、書かないとエラーになったりするので、書いておいてください。
各値はスクリプトに応じて変更します。基本できあがったら最後に編集します。
(公式 → https://github.com/github/hubot-scripts

これで新たにコードを記入できるgreet.coffeeスクリプトファイルの完成です!

スクリプトを書く

とりあえずものは試しに。
みくりさんに、「おはよう」というと、「おはようございます!」って返してもらうスクリプトを書いてみます。
先ほどのTomDocに続いて以下を記述します。

greet.coffee
module.exports = (robot) ->

  robot.respond /おはよう/i, (msg) ->
    msg.send "おはようございます!"

もうこれでオッケーです!
スクリプトの詳しい書き方については後で説明します。

説明を書く

さっきの TomDoc を変更します。

スクリプトコードと合わせてこんな感じになると思います。

greet.coffee
# Description:
#   みくりさんが挨拶をしてくれる機能です。
#
# Dependencies:
#   None
#
# Configuration:
#   None
#
# Commands:
#   hubot おはよう - "おはようございます!"と返答
#
# Notes:
#   初めて作りました。
#
# Author:
#   susuwatarin


module.exports = (robot) ->

  robot.respond /おはよう/i, (msg) ->
    msg.send "おはようございます!"

完璧ですね。ファイルを保存しましょう。

ローカルテストする

ファイルができたら、ローカルでテストしてみましょう。
ターミナルを開いて、hubot プロジェクトのディレクトリにいきます。

$ bin/hubot

そうしたら、なんやかんやでてきて hubot がローカルで動きます。
WARNINGとか言われますが無視です。

この時点で、文字がバアーーーーって出てきて hubot が起動しない場合は、なにかしらのエラーがあります。

もう一度よくスクリプトを見直してみてください。

$ bin/hubot
mikuri> [Fri Dec 16 2016 14:13:14 GMT+0900 (JST)] WARNING Loading scripts from hubot-scripts.json is deprecated and will be removed in 3.0 (https://github.com/github/hubot-scripts/issues/1113) in favor of packages for each script.

Your hubot-scripts.json is empty, so you just need to remove it.
[Fri Dec 16 2016 14:13:14 GMT+0900 (JST)] ERROR hubot-heroku-alive included, but missing HUBOT_HEROKU_KEEPALIVE_URL. `heroku config:set HUBOT_HEROKU_KEEPALIVE_URL=$(heroku apps:info -s  | grep web-url | cut -d= -f2)`
[Fri Dec 16 2016 14:13:14 GMT+0900 (JST)] INFO hubot-redis-brain: Using default redis on localhost:6379
[Fri Dec 16 2016 14:13:14 GMT+0900 (JST)] INFO hubot-redis-brain: Data for hubot brain retrieved from Redis

mikuri> mikuri おはよう
mikuri> おはようございます!

この状態で、 mikuri おはよう を試してオッケーなら次の項目に進みます。

git commit & pushする

タイトルの通りです。変更を git にコミットしてプッシュします。

commitする

そしたらそこで、

$ git add .

$ git commit

と入力します。
そうするとこんな画面に移り変わるはずです。
結構操作が難しいので、以下の指示通りに行ってください。


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Your branch is up-to-date with 'origin/master'.
#
# Changes to be committed:
#       new file:   scripts/greet.coffee
#
~                                                                               
~                                                                               
~                                                                               
~                                                                               
~                                                                               
~                                                                               
~                                                                            
~                                                                               
~                                                                               
~                                                                               
~                                                                               
~                                                                               
~                                                                      

① まず、キーボードの i を押して INSERT モードにします。(一番下に-- INSERT --って出てくる)
② 次に、一番上(# Please en〜より上)に以下のように記入します。

greet.coffeeの追加

- greet.coffeeを追加しました
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
・
・
・

③ Escキーを押します。(一番下の-- INSERT --が消える)
:wqと入力します。(一番下に:wqと出る)
⑤ Enterキーを押します。

これで元の画面に戻ったはずです!commit完了です。

pushする

最後にリモートリポジトリに、push します。

$ git push origin master

これで master ブランチに push されました。
つまり、GitHub の方に、新しく greet.coffee ってファイルを作ったよという変更が送られて、それが反映されました。

そんでもって前回、GitHub と Heroku の同期設定をしていたので、この変更が Heroku サーバーにもデプロイされます。

噛み砕いて言うと、変更がbotに伝わりました。


・・・何を言ってるか何をやってるか全くわからないと思います。
僕も最初わかりませんでした。が、適当に見よう見まねしていました。

でも知りたいのはわかります。わかりますが、git について詳しく説明すると日が暮れるので、参考URLを載せときます。

Git入門:Git初学習者のための効率的な学習方法を考えてみた
Git を学ぶためのサイトの紹介などをしてくれてるとても良い記事
読むサイトの順番を間違えると一気に泥沼にはまる Git 入門サイトをステップごとに紹介してくれてます。

実行してみる

さぁお待ちかね!!!みくりさんに喋ってもらいます!
他に誰もいないですし、mikuriさんとDMでいいと思います。

スクリーンショット 2016-12-16 0.21.43.png

ふおおおおお!!!きたあああ!
みくりさーん!!!!

みなさま、実行できましたでしょうか?
DMではない場合は、 @mikuriでメンションをつけるか、bot名(mikuri)を先頭につけて話しかける必要があります。

スクリーンショット 2016-12-16 13.23.50.png

こんな感じ。
ここまでが、スクリプトを書いて実行できるようにするまでの一連の流れになります。

いろんなスクリプトを書いてみる

さて、スクリプトを書いて実行する一連の流れがわかったところで、みくりさんにいろんな機能を実装していきましょう!
ここからは機能ごとに、解説していきます。

メッセージを送って返答させる

最も簡単な機能で、さきほどの例「おはよう」機能です。
こちらの入力した文をうけとって、それに合わせた文を返答する機能です。

メッセージを受け取らせる

まず、Hubot がメッセージを受け取る方式は二つあります。

respond

この例は先ほどの「おはよう」です。3行目にrobot.respondとあります。

greet.coffee
module.exports = (robot) ->

  robot.respond /おはよう/i, (msg) ->
    msg.send "おはようございます!"

respond はチャットルーム内において、bot が直接話しかけられた言葉のみを観察していて、指定した文字列を"含んでいた"場合その下の処理を行います。
つまり、「おはよう」と誰に言ったかもわからずつぶやいたセリフには反応してくれません。
相手を指定した、「@mikuri おはよう」か、「mikuri おはよう」に対してのみ、「おはようございます!」と返してくれます。

また、文に「おはよう」を含んでいればいいので、「@mikuri おはようございます」「@mikuri みくり、おはよう」などにも反応してくれます。

(Slack では DM の場合、常にメンションが付いているものとみなされるので、DMの部屋では「おはよう」だけでも平気です。)

hear

3行目が、robot.hear になっています。

tsukareta.coffee
module.exports = (robot) ->

  robot.hear /((疲|つか)れた|I'm tired.)/i, (msg) ->
    msg.send "おつかれさま!"

こちらは respond とは違い、チャットールーム上のすべての文を観察していて、一致する文字列があった時、その下の処理を行います。
つまり、「つかれた」と誰に言うでもなく虚空につぶやいても、みくりさんが優しく抱きしめてくれます。
すいません、抱きしめてはくれるのは妄想の中だけでした。

ここではもう一つ新たな要素がありますね。「つかれた」がなんか変な形になっています。
これは正規表現というものです。
文字列の一致判定をするときに役に立つもので、なんとなく分かると思いますが「疲れた」でも「つかれた」でも「I'm tired」でも反応するようになっています。

CoffeeScript の正規表現についてはこちらをご参照ください。
http://kyu-mu.net/coffeescript/regexp/

メッセージを返答する

さて、今度は返答の方に注目してみます。

send

つかれたの例を見てみると、4行目が、msg.send となっています。

tsukareta.coffee
module.exports = (robot) ->

  robot.hear /つかれた/i, (msg) ->
    msg.send "おつかれさま!

これは単純に、botが言葉をチャットルームに投げるだけのものです。

スクリーンショット 2016-12-16 14.35.15.png

reply

対して、上のsendの部分をreplyに書き換えるとこうなります。

tsukareta.coffee
module.exports = (robot) ->

  robot.hear /つかれた/i, (msg) ->
    msg.reply "おつかれさま!"

"スクリーンショット 2016-12-16 14.21.18.png

そう、メンションをつけて返してくれるんですね!!
誰かが疲れたと言ったら、その誰かに対してリプライ、してくれるわけです。

random

みなさん、ちょっと思ってることがあると思います。
『同じ文章返ってきても面白くない・・・』

そんなあなたに、 random を伝授します。

「おはよう」スクリプトの後に書いてみましょう。似たような機能であれば書き足していくことができます。
「おやすみ」をいうとランダムで文字列を返してくれます。

greet.coffee
module.exports = (robot) ->

  robot.respond /おはよう/i, (msg) ->
    msg.reply "おはようございます!"

# ここから↓が今回追加したやつ

  robot.respond /(おやす(|み)|Good Night)/i, (msg) ->
    goodnight = msg.random [
      "おやすみなさい"
      "おやすみなさい"
      "おやすみなさい"
      "おやすみなさい、また明日!"
      "おやすみなさい、また明日!" 
      "もう私も寝ますね!おやすみなさい" 
      "もう私も寝ますね!おやすみなさい"
      "もう寝ちゃうんですか?"
      "今日は一緒に寝ますか?"
    ]
    msg.reply "#{goodnight}"

こうすると、どうなるかというと
goodnight という変数の中に msg.random で random 以下の文字列配列の中にあるどれかを代入してくれ、それを返します。
(なお、sendreply の文字列に変数を入れる際は #{変数名} とします。)

簡単に確率を操作するには、同じ文字列を増やすという荒技をつかえます。

そうすると、「おやすみ」というごとに違う文字列を返してくれます。ご覧あれ。

スクリーンショット 2016-12-16 15.40.55.png

はうううううううううう!!!!!みくりさん・・・・!!
(言わせてる感がすごい)

人工無能botで、random の可能性は割と無限大なので、お楽しみください。

ちなみに追加実装しました。

スクリーンショット 2016-12-16 15.41.04.png

もうこれ以上文章を書き進められそうにないです。

自分の名前を呼ばせたい

cliff.coffee
module.exports = (robot) ->

  robot.respond /お(しごと|仕事)(終|お)わりました(?|か?)/i, (msg) ->
    msg.send "(#{msg.message.user.name}さんがかわいすぎる件についてーーーーー!!)"

変数として、msg.message.user.nameを使うと、トリガーを入力したユーザの情報を引き出すことができます。
つまり、こうなります。

スクリーンショット 2016-12-16 15.53.40.png

もう言うことがないくらいに最高ですね。

決まった日時に何か言わせる

続いて、トリガーコマンドがなくとも(話しかけずとも)決まった日時に何か言ってもらう処理を書きたいと思います。
決まった日時といえば、あれです。
お待ちかね、火曜はハグの日。

火曜の朝になると、「今日はハグの日ですよ」と言ってくれる機能を作ります。

cron

まず、定期で実行するためには、npmで cron モジュールをインストールする必要があります。
また通常ではUTCで時間を設定しなくてはならず面倒なので、タイムゾーンを指定できる time モジュールというモジュールもインストールします。
ターミナルを開いてmikuriディレクトリに入ったら、以下を入力していきます。

$ npm install cron --save

$ npm install time --save

これで二つがインストールされればオッケーです。
続いて、package.json を確認します。

package.json
{
  "name": "mikuri",
  "version": "0.0.0",
  "private": true,
  "author": "susuwatarin",
  "description": "She is an angel.",
  "dependencies": {
    "cron": "^1.1.1",          //ここにcronがある
    "hubot": "^2.19.0",
    "hubot-diagnostics": "0.0.1",
    "hubot-google-images": "^0.2.6",
    "hubot-google-translate": "^0.2.0",
    "hubot-help": "^0.2.0",
    "hubot-heroku-keepalive": "^1.0.2",
    "hubot-maps": "0.0.2",
    "hubot-pugme": "^0.1.0",
    "hubot-redis-brain": "0.0.3",
    "hubot-rules": "^0.1.1",
    "hubot-scripts": "^2.17.2",
    "hubot-shipit": "^0.2.0",
    "hubot-slack": "^4.2.2",
    "time": "^0.11.4"          //ここにtimeがある
  },
  "engines": {
    "node": "0.10.x"
  }
}


このようになっていて、crontime がどこかに書いてあれば大丈夫です。

そこまで終わったら、ファイルを作って、TomDocを書いて、以下のようなコードをかきます。

hugday.coffee
# Description:
#   みくりさんが火曜の朝にハグの日をお知らせしてくれる機能です。
#
# Dependencies:
#   None
#
# Configuration:
#   None
#
# Commands:
#   None
#
# Notes:
#   火曜はハグの日。
#
# Author:
#   susuwatarin

cronJob = require('cron').CronJob

module.exports = (robot) ->

  cronjob = new cronJob(
    cronTime: "0 30 8 * * tue"    # 実行時間
    start:    true                # すぐにcronのjobを実行するか
    timeZone: "Asia/Tokyo"        # タイムゾーン指定
    onTick: ->                    # 時間が来た時に実行する処理
      robot.send {room: "#general"}, "今日はハグの日ですよ!"
  )

さきほどダウンロードした cron モジュールの機能(定期的に処理を実行する)を使っています。

module.exports = (robot) -> よりも前に、 cronJob = require('cron').CronJob を書いておくことが大事です。

そうしたら、cronJob の設定をします。
見たとおりなんですが、

crontime

crontime: "* * * * * *" は実行時間の設定です。

六ヶ所 * がついていますが、それぞれ "秒 分 時 日 月 曜日" になっていて、以下のような数値を設定することができます。

"秒 分 時 日 月 曜日"

  秒 : 0 - 59
  分 : 0 - 59
  時 : 0 - 23
  日 : 0 - 30    // 例) 1日が0、31日は30
  月 : 0 - 11    // 例) 1月が0、12月は11
  曜日 : 0 - 6 または mon - sun   // 例) 月曜日が0、木曜は3、日曜日が6

月と日と曜日が初心者的には若干わかりにくいですね。
曜日は、mon など、三文字の英略語でも指示できるのでこれが安全です。

時間だけ設定して、他は*にしておくと、毎日起動します。
逆に、曜日だけ設定して、他は*にしておくと、その曜日の 00:00 に起動します。

なんとなくわかっていただけたでしょうか?笑

start

この仕様がいまいちわからないので true でいいです。

timeZone

よほどのことがなければ、 "Asia/Tokyo" でいいと思います。

onTick

ここの矢印のあと、しっかりインデントをずらして処理を書きます。

hugday.coffee
onTick: ->
      robot.send {room: "#general"}, "今日はハグの日ですよ!"

初めて出てきましたが、sendrespond の後に {room: "#@@@@@@"}, で指定すると指定された部屋にメッセージを飛ばしてくれます。

後はこれまでと同じです。

あいにく書いている日は火曜ではなかったので、適当な時間を設定し、テストでみくりさんに実行してもらいました。

"* 30 23 * * fri"

スクリーンショット 2016-12-17 23.30.16.png

はぁああああ!!!!
早く帰ります!!!!

やってから思いましたが、メンションをつけても良いですね笑

デフォルトスクリプトを読んでみる

デフォルトで入っているexample.coffeeにはいろいろなコードの使用例が書かれていてコメントアウトされています。もうなんとなく読めるはずです。

コメントアウトを解除して、デプロイすると実際に使ってみることができるようになるので、参考までに。

https://github.com/github/hubot/blob/master/docs/scripting.md

おわりに

初心者がhubotを使ってHeroku上で動くSlack bot作ってみた、実践編を公開してみました。
卒研等で大変遅くなってしまいお待たせしてしまいました・・

今回は実践編でした。
「火曜はハグの日」を実装しました!!が、もうコンテンツ的にかなり厳しいです。
毎週火曜にslackにハグの日通知が来るのですが、悲しい気分になります。

また長くなってしまったので次回は「発展編」として、"初心者なり"に、redisを使ったbotの短期記憶活用や、WebAPIの利用、ツイッター連携など紹介、実装していきたいです。

ではでは、駄文ながらここまでお付き合いありがとうございました。

皆さんもぜひhubotを使って、slackにbotを導入しましょう!たのしい!

参考文献

http://engineering.otobank.co.jp/entry/2015/06/19/000300
第3回 Hubotを色々なサービスと繋げてみる
Hubot メモ
http://supportdoc.net/support-coffee/12-hubot_files.html

Hubot導入とhubot-script作成までやってみる