いつかエンジニアになりたい

Nothing is too late to start

「botpress」でSlackBotを作成してみる

はじめに

  • どうも。業務では Javascript を利用しないので、SlackBotを作るときはもっぱら、無理矢理にでも Python で書くのは私です
  • 今日、たまたま会社の先輩が「Botpress」に反応していて、調べてみるとダッシュボードがかっこよかったので、どんな感じのツールか試してみました
  • 基本的にはほぼほぼチュートリアルのままなので、気になる方はドキュメントを読んでいただけると幸いです

botpressとは

f:id:berukann:20180219232139p:plain

インストール

  • 実行環境は「macOS High Sierra」になり、インストールしたツールは以下になります
    • nodebrew (version: 0.9.8
    • node.js (version: 9.5.0
    • ngrock (version: 2.2.8
    • botpress (verssion: 1.1.13

インストール(nodebrew+node.js)

  • 「botpress」をインストールするために node.js が必要なのでインストールする
  • 今後のことも考えて、node.js は nodebrew でインストールする
  • node.js のバージョンは 4.6 以上であればいいらしく、最新版をインストールしておく
$ curl -L git.io/nodebrew | perl - setup
$ echo PATH=$HOME/.nodebrew/current/bin:$PATH >> .zshrc
[daicho]% nodebrew list
not installed
current: none
[daicho]% nodebrew install-binary latest
Fetching: https://nodejs.org/dist/v9.5.0/node-v9.5.0-darwin-x64.tar.gz
######################################################################## 100.0%
Installed successfully
[daicho]% nodebrew use latest
use v9.5.0
[daicho]% nodebrew list
v9.5.0
current: v9.5.0
[daicho]% node -v
v9.5.0

インストール(botpress)

[daicho]% npm install -g botpress
npm WARN deprecated nodemailer@2.7.2: All versions below 4.0.1 of Nodemailer are deprecated. See https://nodemailer.com/status/
npm WARN deprecated email-templates@2.7.1: 📫  Please upgrade to v3.1.5 (v3.0.x had a core bug with attachments and v3.1.5 now supports Node v6.4.0+). Also read the breaking changes for upgrading from v2 to v3 at https://github.com/niftylettuce/email-templates#v3-breaking-changes 📫
/Users/daicho/.nodebrew/node/v9.5.0/bin/bp -> /Users/daicho/.nodebrew/node/v9.5.0/lib/node_modules/botpress/bin/botpress
/Users/daicho/.nodebrew/node/v9.5.0/bin/botpress -> /Users/daicho/.nodebrew/node/v9.5.0/lib/node_modules/botpress/bin/botpress
> fsevents@1.1.3 install /Users/daicho/.nodebrew/node/v9.5.0/lib/node_modules/botpress/node_modules/fsevents
> node install
[fsevents] Success: "/Users/daicho/.nodebrew/node/v9.5.0/lib/node_modules/botpress/node_modules/fsevents/lib/binding/Release/node-v59-darwin-x64/fse.node" is installed via remote
> sqlite3@3.1.13 install /Users/daicho/.nodebrew/node/v9.5.0/lib/node_modules/botpress/node_modules/sqlite3
> node-pre-gyp install --fallback-to-build       
[sqlite3] Success: "/Users/daicho/.nodebrew/node/v9.5.0/lib/node_modules/botpress/node_modules/sqlite3/lib/binding/node-v59-darwin-x64/node_sqlite3.node" is installed via remote       
> nodemon@1.15.0 postinstall /Users/daicho/.nodebrew/node/v9.5.0/lib/node_modules/botpress/node_modules/nodemon
> node -e "console.log('\u001b[32mLove nodemon? You can now support the project via the open collective:\u001b[22m\u001b[39m\n > \u001b[96m\u001b[1mhttps://opencollective.com/nodemon/donate\u001b[0m\n')" || exit 0     
Love nodemon? You can now support the project via the open collective:
> https://opencollective.com/nodemon/donate      
npm WARN react-jsonschema-form@0.49.0 requires a peer of react@^15.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN react-codemirror@1.0.0 requires a peer of react@>=15.5 <16 but none is installed. You must install peer dependencies yourself.
npm WARN react-codemirror@1.0.0 requires a peer of react-dom@>=15.5 <16 but none is installed. You must install peer dependencies yourself.       
+ botpress@1.1.13
added 957 packages in 35.529s
[daicho]% botpress --version
1.1.13
[daicho]% botpress init test-bot
[botpress]
Hey there, thanks for using botpress!
We'll walk you through the creation of your new bot.
For more information or help, please visit http://github.com/botpress/botpress
---------------
name: (test-bot)
description: Say hello World
author: daicho
version: (0.0.1)
[botpress]       please wait, we are installing everything for you...
npm WARN deprecated email-templates@2.7.1: 📫  Please upgrade to v3.1.5 (v3.0.x had a core bug with attachments and v3.1.5 now supports Node v6.4.0+). Also read the breaking changes for upgrading from v2 to v3 at https://github.com/niftylettuce/email-templates#v3-breaking-changes 📫
npm WARN deprecated nodemailer@2.7.2: All versions below 4.0.1 of Nodemailer are deprecated. See https://nodemailer.com/status/

> fsevents@1.1.3 install /Users/daicho/test-bot/node_modules/fsevents
> node install

[fsevents] Success: "/Users/daicho/test-bot/node_modules/fsevents/lib/binding/Release/node-v59-darwin-x64/fse.node" is installed via remote

> sqlite3@3.1.13 install /Users/daicho/test-bot/node_modules/sqlite3
> node-pre-gyp install --fallback-to-build

[sqlite3] Success: "/Users/daicho/test-bot/node_modules/sqlite3/lib/binding/node-v59-darwin-x64/node_sqlite3.node" is installed via remote

> nodemon@1.15.0 postinstall /Users/daicho/test-bot/node_modules/nodemon
> node -e "console.log('\u001b[32mLove nodemon? You can now support the project via the open collective:\u001b[22m\u001b[39m\n > \u001b[96m\u001b[1mhttps://opencollective.com/nodemon/donate\u001b[0m\n')" || exit 0

Love nodemon? You can now support the project via the open collective:
 > https://opencollective.com/nodemon/donate

npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN react-codemirror@1.0.0 requires a peer of react@>=15.5 <16 but none is installed. You must install peer dependencies yourself.
npm WARN react-codemirror@1.0.0 requires a peer of react-dom@>=15.5 <16 but none is installed. You must install peer dependencies yourself.
npm WARN react-jsonschema-form@0.49.0 requires a peer of react@^15.0.0 but none is installed. You must install peer dependencies yourself.
npm WARN test-bot@0.0.1 No repository field.
npm WARN test-bot@0.0.1 license should be a valid SPDX license expression

added 958 packages in 22.682s
[botpress]       OK installation has completed successfully
[botpress]       now run `bp start` in your terminal
[daicho]% cd test-bot
[daicho]% ls
LICENSE            content.yml        index.js           node_modules/      package.json
botfile.js         data/              modules_config/    package-lock.json
[daicho]% botpress start
20:34:32 - info: Starting botpress version 1.1.13
20:34:33 - verbose: [UMM] Enabled for slack
20:34:33 - info: Loaded botpress-slack, version 1.0.23
20:34:33 - verbose: [UMM] Enabled for web
20:34:33 - info: Loaded botpress-web, version 1.2.3
20:34:33 - info: Loaded 2 modules
20:34:33 - debug: Loading middleware: UMM.instrumentation
20:34:33 - debug: Loading middleware: hear
20:34:33 - debug: Loading middleware: conversations
20:34:33 - debug: Loading middleware: slack.sendMessages
20:34:33 - debug: Loading middleware: web.sendMessages
20:34:33 - debug: Loading middleware: fallback
20:34:33 - info: Bot launched. Visit: http://localhost:3000

「botpress」でSlackBotを作成する

  • botpressがインストールできたので、早速、SlackBotを作成してみる
  • 「botpress」でのSlackBotの作成方法は、「botpress」のSlackモジュールのドキュメントに記載の通りになります
  • 主に必要な作業は以下になりますが、今回はテストなので、ドキュメント通りにngrockを利用しています
    • (1)botpressにSlackモジュールをインストールする
    • (2)ngrockを利用して、ローカルホストを外部からアクセスできるようにする
    • (3)SlackBot用にSlack側でアプリを作成し、設定する
    • (4)作成した内容を元にbotpress側に登録する
    • (5)SlackBotの動作確認をする

(1)botpressにSlackモジュールをインストールする

  • ブラウザで「http://localhost:3000」にアクセスし、サイドバーの「Modules」を選択し、検索バーに「Slack」と入力して、Slackのコネクターをインストールする
  • CLIから実施したい場合は、以下のコマンドでも可能
botpress install slack

(2)ngrockを利用して、ローカルホストを外部からアクセスできるようにする

  • ngrock」は、ローカルで動作するアプリケーションをトンネリングして、外部からアクセスできるようにするサービス
  • 今回は、ローカルに立てたbotpress(http://localhost:3000)に外部からアクセスできるようにするために利用する
  • Basic認証が利用したかったので、アカウント登録を実施しました ※Basic認証とか利用しない場合はアカウント登録不要なので、そのままngrok http 3000を実行して下さい
  • ブラウザよりngrockにアクセスしアカウントを作成。ダッシュボードからAuthTokenを追加するコマンドを確認し、ターミナルにて実行する
[daicho]% brew cask install ngrok
==> Satisfying dependencies
==> Downloading https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-darwin-amd64.zip
######################################################################## 100.0%
==> No checksum defined for Cask ngrok, skipping verification
==> Installing Cask ngrok
==> Extracting nested container ngrok
==> Linking Binary 'ngrok' to '/usr/local/bin/ngrok'.
🍺  ngrok was successfully installed!
[daicho]% ngrok authtoken XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Authtoken saved to configuration file: /Users/daicho/.ngrok2/ngrok.yml
[daicho]% ngrok http --auth='admin:passw0rd' 3000 
  • 以下の画面が出力され、ForwardingされているURL(例: http://82aaf557.ngrok.io)にアクセスすると、http://localhost:3000と同様の画面が出力されることを確認する ※ Basic認証上記で入力したID/PASS f:id:berukann:20180220004553p:plain
  • 上記のコマンドは、実行毎に新しいURLになるため、一度、実行したらそのままにしておく

(3)SlackBot用にSlack側でアプリを作成し、設定する

  • Slackにログインした状態で「Create Slack App」にアクセスし、新規アプリを登録する
  • [App Name]にアプリ名、[Development Slack Workspace]にBotを作成したいSlackを選択する f:id:berukann:20180220005257p:plain
  • 左メニューより「Basic Information」を選択し、Client ID と Client Secret を確認する
  • 左メニューより「OAuth & Permissions」を選択し、Redirect URLsを追加する
  • 左メニューより「Bot Users」を選択し、Botユーザを作成する f:id:berukann:20180220010537p:plain
  • 左メニューより「Interactive Components」を選択し、Request URLを記載し、「Enable Interactive Components」を選択する

(4)作成した内容を元にbotpress側に登録する

  • ブラウザより「botpress」にアクセスし、(3)で確認した内容を元に記載し、「Authenticate & Connect」を選択する f:id:berukann:20180220012818p:plain
  • 正常に進むと、以下のような画面がでるので、Botがポストしたいチャンネルを選択し、「Authorize」を選択する ※今回はテスト用のチャンネル「testbot-room」を事前に作成 f:id:berukann:20180220013439p:plain
  • botpressの画面に戻されて、API token や Bot token が追加されていたら成功 f:id:berukann:20180220013829p:plain

(5)SlackBotの動作確認をする

  • (4)で認証したチャンネルを確認すると作成したintegrationが追加されている f:id:berukann:20180220014124p:plain
  • 対象のチャネルにボットユーザを招待して、hiというと返事してくれる f:id:berukann:20180220014414p:plain

SlackBotをカスタマイズする

  • はい。ここまでは、デフォルトのBotを起動しただけなので、カスタマイズしてみる
  • 「botpress」は、botpress init [bot-name] を実行するとファイルが自動されるが、中でも以下のファイルが重要
    • index.js
      • Botのロジックを記述するファイル
    • content.yaml
      • Botが話したい会話内容をすべて記載するファイル
      • UMM (Universal Message Markdown)と呼ばれ、Botpress特有のファイル
    • botfile.js
      • Botの設定ファイル。ファイルパスとか設定ファイル名とかを記載
  • 詳細は公式ドキュメントのfoundamentalsを確認して下さい

修造Botを作ってみる

  • 試しに hubot-syuzo みたいな修造Botを書いてみる
  •  一度、「botpress」をCtrl-C,Ctrl-Cで停止する ※ ngrockは停止しない
  • index.jsに以下のコードを記載する
/*
  CONGRATULATIONS on creating your first Botpress bot!
*/
module.exports = function(bp) {
  bp.hear({
        platform: 'slack',
        type: 'message',
        text: /(無理|むり|ムリ)|(でき|出来)(ない|ません|ん)/i } , (event, next) => {¬
        event.reply('#syuzo-img-link')
  })
}
  • content.ymlに以下のコードを記載後に、botpress startを再実行する
syuzo-img-link:
  - text:
    - "http://i.imgur.com/lCvSmkc.jpg"
    - "http://i.imgur.com/wRuO0T6.png"
    - "http://i.imgur.com/9pJcmPA.jpg"
    - "http://i.imgur.com/JoFiX17.jpg"
    - "http://i.imgur.com/0GJdUlW.jpg"
    - "http://i.imgur.com/cxLWi2Q.jpg"
    - "http://i.imgur.com/ht6Ng0J.jpg"
    - "http://i.imgur.com/p85gjPX.jpg"
    - "http://i.imgur.com/NDikSI4.jpg"
    - "http://i.imgur.com/UrFocgg.jpg"
    - "http://i.imgur.com/vLvuVqv.jpg"
    - "http://i.imgur.com/BCOFvKK.jpg"
    - "http://i.imgur.com/nNMuElm.png"
    - "http://i.imgur.com/pJP3lBn.jpg"
    - "http://i.imgur.com/5HhGuXB.jpg"
    - "http://i.imgur.com/RyLBvgs.jpg"
    - "http://i.imgur.com/MWEsCFG.jpg"
    - "http://i.imgur.com/3jEkEvI.jpg"
    - "http://i.imgur.com/ftZsjy3.jpg"
    - "http://i.imgur.com/QKQft3B.jpg"
    - "http://i.imgur.com/VNPx3B4.jpg"
    - "http://i.imgur.com/AD8tHQV.jpg"
    - "http://i.imgur.com/OMeB9Sf.png"
    - "http://i.imgur.com/3jcYzDR.jpg"
    - "http://i.imgur.com/E7BGbel.jpg"
    - "http://i.imgur.com/B7Op7MS.jpg"
    - "http://i.imgur.com/TbZkSrI.jpg"
    - "http://i.imgur.com/pDmMheZ.jpg"
    - "http://i.imgur.com/Mj81q08.jpg"
    - "http://i.imgur.com/xhOnNwd.jpg"
    - "http://i.imgur.com/HGpEk8E.jpg"
  • Slackで無理と発言すると反応する。Javascript力がなさすぎて、サンプルコードより劣化したコードになってしまった f:id:berukann:20180220104118p:plain

補足:他のモジュール「analytics」も試す

  • ChatbotのWordpressを目指しているだけあって、Connecter以外にもモジュールはありそう ※ npmのパッケージで検索すると全部で52個くらい?
  • 試しにUIから「analytics」をインストールしてみると、なんかかっこいいUIキタ━━━━(゚∀゚)━━━━!! f:id:berukann:20180220105236p:plain
  • ちなみに何か設定がいるのか、botpressでエラーを吐きまくってた
10:51:22 - info: slack connector is authenticated
10:51:22 - info: slack connector is connected
10:51:25 - error: Unhandled Rejection in Promise:  Promise {
  _bitField: 18087936,
  _fulfillmentHandler0: TypeError: Cannot read property 'startsWith' of undefined
    at Object.saveInteractionOut [as saveOutgoing] (/Users/daicho/test-bot/node_modules/botpress-analytics/bin/webpack:/src/db.js:68:15)
    at outgoingMiddleware (/Users/daicho/test-bot/node_modules/botpress-analytics/bin/webpack:/src/index.js:40:8)
    at exec (/Users/daicho/test-bot/node_modules/mware/index.js:50:23)
    at Function.run (/Users/daicho/test-bot/node_modules/mware/index.js:63:7)
    at Object.dispatch (webpack-internal:///54:74:10)
    at Object.eval [as sendOutgoing] (webpack-internal:///54:197:31)
    at eval (webpack-internal:///72:365:28)
    at tryCatcher (/Users/daicho/test-bot/node_modules/bluebird/js/release/util.js:16:23)
    at Object.gotValue (/Users/daicho/test-bot/node_modules/bluebird/js/release/reduce.js:155:18)
    at Object.gotAccum (/Users/daicho/test-bot/node_modules/bluebird/js/release/reduce.js:144:25)
    at Object.tryCatcher (/Users/daicho/test-bot/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/Users/daicho/test-bot/node_modules/bluebird/js/release/promise.js:512:31)
    at Promise._settlePromise (/Users/daicho/test-bot/node_modules/bluebird/js/release/promise.js:569:18)
    at Promise._settlePromiseCtx (/Users/daicho/test-bot/node_modules/bluebird/js/release/promise.js:606:10)
    at Async._drainQueue (/Users/daicho/test-bot/node_modules/bluebird/js/release/async.js:138:12)
    at Async._drainQueues (/Users/daicho/test-bot/node_modules/bluebird/js/release/async.js:143:10)
    at Immediate.Async.drainQueues [as _onImmediate] (/Users/daicho/test-bot/node_modules/bluebird/js/release/async.js:17:14)
    at runCallback (timers.js:756:18)
    at tryOnImmediate (timers.js:717:5)
    at processImmediate [as _immediateCallback] (timers.js:697:5),
  _rejectionHandler0: undefined,
  _promise0: undefined,
  _receiver0: undefined } Reason: TypeError: Cannot read property 'startsWith' of undefined
    at Object.saveInteractionOut [as saveOutgoing] (/Users/daicho/test-bot/node_modules/botpress-analytics/bin/webpack:/src/db.js:68:15)
    at outgoingMiddleware (/Users/daicho/test-bot/node_modules/botpress-analytics/bin/webpack:/src/index.js:40:8)
    at exec (/Users/daicho/test-bot/node_modules/mware/index.js:50:23)
    at Function.run (/Users/daicho/test-bot/node_modules/mware/index.js:63:7)
    at Object.dispatch (webpack-internal:///54:74:10)
    at Object.eval [as sendOutgoing] (webpack-internal:///54:197:31)
    at eval (webpack-internal:///72:365:28)
    at tryCatcher (/Users/daicho/test-bot/node_modules/bluebird/js/release/util.js:16:23)
    at Object.gotValue (/Users/daicho/test-bot/node_modules/bluebird/js/release/reduce.js:155:18)
    at Object.gotAccum (/Users/daicho/test-bot/node_modules/bluebird/js/release/reduce.js:144:25)
    at Object.tryCatcher (/Users/daicho/test-bot/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/Users/daicho/test-bot/node_modules/bluebird/js/release/promise.js:512:31)
    at Promise._settlePromise (/Users/daicho/test-bot/node_modules/bluebird/js/release/promise.js:569:18)
    at Promise._settlePromiseCtx (/Users/daicho/test-bot/node_modules/bluebird/js/release/promise.js:606:10)
    at Async._drainQueue (/Users/daicho/test-bot/node_modules/bluebird/js/release/async.js:138:12)
    at Async._drainQueues (/Users/daicho/test-bot/node_modules/bluebird/js/release/async.js:143:10)
    at Immediate.Async.drainQueues [as _onImmediate] (/Users/daicho/test-bot/node_modules/bluebird/js/release/async.js:17:14)
    at runCallback (timers.js:756:18)
    at tryOnImmediate (timers.js:717:5)
    at processImmediate [as _immediateCallback] (timers.js:697:5)
10:51:25 - error: TypeError: Cannot read property 'startsWith' of undefined
    at Object.saveInteractionOut [as saveOutgoing] (/Users/daicho/test-bot/node_modules/botpress-analytics/bin/webpack:/src/db.js:68:15)
    at outgoingMiddleware (/Users/daicho/test-bot/node_modules/botpress-analytics/bin/webpack:/src/index.js:40:8)
    at exec (/Users/daicho/test-bot/node_modules/mware/index.js:50:23)
    at Function.run (/Users/daicho/test-bot/node_modules/mware/index.js:63:7)
    at Object.dispatch (webpack-internal:///54:74:10)
    at Object.eval [as sendOutgoing] (webpack-internal:///54:197:31)
    at eval (webpack-internal:///72:365:28)
    at tryCatcher (/Users/daicho/test-bot/node_modules/bluebird/js/release/util.js:16:23)
    at Object.gotValue (/Users/daicho/test-bot/node_modules/bluebird/js/release/reduce.js:155:18)
    at Object.gotAccum (/Users/daicho/test-bot/node_modules/bluebird/js/release/reduce.js:144:25)
    at Object.tryCatcher (/Users/daicho/test-bot/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/Users/daicho/test-bot/node_modules/bluebird/js/release/promise.js:512:31)
    at Promise._settlePromise (/Users/daicho/test-bot/node_modules/bluebird/js/release/promise.js:569:18)
    at Promise._settlePromiseCtx (/Users/daicho/test-bot/node_modules/bluebird/js/release/promise.js:606:10)
    at Async._drainQueue (/Users/daicho/test-bot/node_modules/bluebird/js/release/async.js:138:12)
    at Async._drainQueues (/Users/daicho/test-bot/node_modules/bluebird/js/release/async.js:143:10)
    at Immediate.Async.drainQueues [as _onImmediate] (/Users/daicho/test-bot/node_modules/bluebird/js/release/async.js:17:14)
    at runCallback (timers.js:756:18)
    at tryOnImmediate (timers.js:717:5)
    at processImmediate [as _immediateCallback] (timers.js:697:5)
  • 気になってbotpress-analyticsのページにいったら、Slackは非対応でした・・・
Support connectors: ** botpress-messenger

まとめ

  • 会社で話題に出た「botpress」を試してみた
  • SaaSとかじゃなくて、オンプレ、ローカルで動かせるのがいい
  • Javascript好きな人にとったらとても良さそう ※それを言い出すと、そもそもhubotとかも・・・
  • まだまだモジュールは少ないけど、今後増えたらもっと便利になりそう
  • ただ、Botは増えていくので統合管理したかったんだけど、そういうツールではなさそう

参考記事: