(追記)こちら、2年前、2016年12月時点の記事ですが現在も動作確認はできています。
※ただ、Herokuの契約など変わっているため、注意は必要かと思いますので、ご注意ください。
まぁイマドキは、GASで作るのがよい・・?
早稲田大学Advent Calendar 17日目の記事でございます。かしこ。
前回: 初心者がHubotを使ってHeroku上で動くSlack bot作ってみた(導入編)
次回: 初心者がHubotを使ってHeroku上で動くSlack bot作ってみた(発展編)(うっ頭が・・・)
非エンジニアである初心者が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 の公式ページは[こちら](http://coffeescript.org/)です。 [ 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を書く
ファイルを作ったら、次に以下のように記入します。ここからコピペでいいです。
# 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に続いて以下を記述します。
module.exports = (robot) ->
robot.respond /おはよう/i, (msg) ->
msg.send "おはようございます!"
もうこれでオッケーです!
スクリプトの詳しい書き方については後で説明します。
##説明を書く
さっきの TomDoc を変更します。
スクリプトコードと合わせてこんな感じになると思います。
# 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でいいと思います。
ふおおおおお!!!きたあああ!
みくりさーん!!!!
みなさま、実行できましたでしょうか?
DMではない場合は、 @mikuriでメンションをつけるか、bot名(mikuri)を先頭につけて話しかける必要があります。
こんな感じ。
ここまでが、スクリプトを書いて実行できるようにするまでの一連の流れになります。
#いろんなスクリプトを書いてみる
さて、スクリプトを書いて実行する一連の流れがわかったところで、みくりさんにいろんな機能を実装していきましょう!
ここからは機能ごとに、解説していきます。
メッセージを送って返答させる
最も簡単な機能で、さきほどの例「おはよう」機能です。
こちらの入力した文をうけとって、それに合わせた文を返答する機能です。
###メッセージを受け取らせる
まず、Hubot がメッセージを受け取る方式は二つあります。
####respond
この例は先ほどの「おはよう」です。3行目にrobot.respond
とあります。
module.exports = (robot) ->
robot.respond /おはよう/i, (msg) ->
msg.send "おはようございます!"
respond
はチャットルーム内において、bot が直接話しかけられた言葉のみを観察していて、指定した文字列を"含んでいた"場合その下の処理を行います。
つまり、「おはよう」と誰に言ったかもわからずつぶやいたセリフには反応してくれません。
相手を指定した、「@mikuri おはよう」か、「mikuri おはよう」に対してのみ、「おはようございます!」と返してくれます。
また、文に「おはよう」を含んでいればいいので、「@mikuri おはようございます」「@mikuri みくり、おはよう」などにも反応してくれます。
(Slack では DM の場合、常にメンションが付いているものとみなされるので、DMの部屋では「おはよう」だけでも平気です。)
####hear
3行目が、robot.hear
になっています。
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
となっています。
module.exports = (robot) ->
robot.hear /つかれた/i, (msg) ->
msg.send "おつかれさま!
これは単純に、botが言葉をチャットルームに投げるだけのものです。
####reply
対して、上のsend
の部分をreplyに書き換えるとこうなります。
module.exports = (robot) ->
robot.hear /つかれた/i, (msg) ->
msg.reply "おつかれさま!"
そう、メンションをつけて返してくれるんですね!!
誰かが疲れたと言ったら、その誰かに対してリプライ、してくれるわけです。
####random
みなさん、ちょっと思ってることがあると思います。
『同じ文章返ってきても面白くない・・・』
そんなあなたに、 random
を伝授します。
「おはよう」スクリプトの後に書いてみましょう。似たような機能であれば書き足していくことができます。
「おやすみ」をいうとランダムで文字列を返してくれます。
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 以下の文字列配列の中にあるどれかを代入してくれ、それを返します。
(なお、send
やreply
の文字列に変数を入れる際は #{変数名}
とします。)
簡単に確率を操作するには、同じ文字列を増やすという荒技をつかえます。
そうすると、「おやすみ」というごとに違う文字列を返してくれます。ご覧あれ。
はうううううううううう!!!!!みくりさん・・・・!!
(言わせてる感がすごい)
人工無能botで、random
の可能性は割と無限大なので、お楽しみください。
ちなみに追加実装しました。
もうこれ以上文章を書き進められそうにないです。
###自分の名前を呼ばせたい
module.exports = (robot) ->
robot.respond /お(しごと|仕事)(終|お)わりました(?|か?)/i, (msg) ->
msg.send "(#{msg.message.user.name}さんがかわいすぎる件についてーーーーー!!)"
変数として、msg.message.user.name
を使うと、トリガーを入力したユーザの情報を引き出すことができます。
つまり、こうなります。
もう言うことがないくらいに最高ですね。
##決まった日時に何か言わせる
続いて、トリガーコマンドがなくとも(話しかけずとも)決まった日時に何か言ってもらう処理を書きたいと思います。
決まった日時といえば、あれです。
お待ちかね、火曜はハグの日。
火曜の朝になると、「今日はハグの日ですよ」と言ってくれる機能を作ります。
###cron
まず、定期で実行するためには、npmで cron モジュールをインストールする必要があります。
また通常ではUTCで時間を設定しなくてはならず面倒なので、タイムゾーンを指定できる time モジュールというモジュールもインストールします。
ターミナルを開いてmikuriディレクトリに入ったら、以下を入力していきます。
$ npm install cron --save
$ npm install time --save
これで二つがインストールされればオッケーです。
続いて、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"
}
}
このようになっていて、cron
と time
がどこかに書いてあれば大丈夫です。
そこまで終わったら、ファイルを作って、TomDocを書いて、以下のようなコードをかきます。
# 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
ここの矢印のあと、しっかりインデントをずらして処理を書きます。
onTick: ->
robot.send {room: "#general"}, "今日はハグの日ですよ!"
初めて出てきましたが、send
や respond
の後に {room: "#@@@@@@"},
で指定すると指定された部屋にメッセージを飛ばしてくれます。
後はこれまでと同じです。
あいにく書いている日は火曜ではなかったので、適当な時間を設定し、テストでみくりさんに実行してもらいました。
"* 30 23 * * fri"
はぁああああ!!!!
早く帰ります!!!!
やってから思いましたが、メンションをつけても良いですね笑
##デフォルトスクリプトを読んでみる
デフォルトで入っているexample.coffee
にはいろいろなコードの使用例が書かれていてコメントアウトされています。もうなんとなく読めるはずです。
コメントアウトを解除して、デプロイすると実際に使ってみることができるようになるので、参考までに。
#おわりに
初心者が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