雑談APIを何個か試してみた(Hubot + Chatwork + UserLocal, CotoGoto, Docomo)


はじめに

ChatOpsの手始めとして、ChatBotで会話APIを実装してみました。
・UserLocal 人工知能ボットAPI
・CotoGoto::Noby API
・Docomo 雑談会話API
ChatworkでもSlackでも手順は同じで、hubotのオプションでどちらかを選べばよいだけです。

事前準備

Chat側

  • Chatwork
    bot用のユーザを新規作成し、API申請をする。 APIキーとルームIDを取得
  • Slack登録
    Apps & IntegrationでhubotをインストールしてAPIキーを取得

会話API側

  • UserLocal
    無料登録してAPI Key取得
  • CotoGoto
    無料登録してAPI Key取得
  • Docomo
    色々な機能があるので、最低限の会話APIであれば即API Key発行されますが、
    追加の機能によってはサイトの審査や法人登録が必要です。

サーバ設定(EC2で構築)

モジュール

// 最新のNode.jsを入れるためにNVMから
$ git clone https://github.com/creationix/nvm.git ~/.nvm
$ source ~/.nvm/nvm.sh
$ vi .bash_profile
  # nvm
    if [[ -s ~/.nvm/nvm.sh ]] ; then
      source ~/.nvm/nvm.sh ;
    fi
$ nvm ls-remote
$ nvm install 7.4.0
$ nvm use v7.4.0
$ node -v

$ sudo yum install -y npm redis
$ sudo npm install -g hubot coffee-script yo generator-hubot

$ cd /var/www/bot
$ mkdir hubot-cw
$ cd hubot-cw
$ yo hubot
  全てEnterでOK
$ npm install hubot-chatwork --save
$ npm install forever request --save

./scripts以下へ新規作成

  • hubot.coffee
module.exports = (robot) ->
    status  = {}

    robot.respond /(.*)/i, (res) ->
        message = res.match[1]
        if message.length == 0
            res.reply "What?"

    lunch = ['コンビニ', '中華', 'イタリアン', '和食', 'カレー', '昨日と同じ']
    robot.respond /お昼/i, (res) ->
        res.reply res.random lunch

    robot.respond /site (.*)/i, (msg) ->
      url = msg.match[1]
      options =
        url: url
        timeout: 2000
        headers: {'user-agent': 'node title fetcher'}

      request = require 'request'
      cheerio = require 'cheerio'

      request options, (error, response, body) ->
        $ = cheerio.load body
        title = $('title').text().replace(/\n/g, '')
        msg.reply(title)
  • api_userlocal.coffee
module.exports = (robot) ->
    robot.respond /ul (.*)/i, (res) ->
        url = "https://chatbot-api.userlocal.jp/api/chat"
        api_key = process.env.HUBOT_UL_API_KEY
        message = res.match[1]
        params = {
          "message": message,
          "key": api_key
        }
        robot.http(url).query(params).get() (err, response, body) ->
          return response.send "Encountered an error :( #{err}" if err
          body = JSON.parse(body)
          res.reply "#{body.result}"
  • api_cotogoto.coffee
module.exports = (robot) ->
      robot.respond /cg (.*)/i, (res) ->
          url = "https://www.cotogoto.ai/webapi/noby.json"
          api_key = process.env.HUBOT_CG_API_KEY
          message = res.match[1]
          params = {
            "text": message,
            "app_key": api_key
          }
          robot.http(url).query(params).get() (err, response, body) ->
            return response.send "Encountered an error :( #{err}" if err
            body = JSON.parse(body)
            res.reply "#{body.text}"
  • api_docomo.coffee
getTimeDiffAsMinutes = (old_msec) ->
  now = new Date()
  old = new Date(old_msec)
  diff_msec = now.getTime() - old.getTime()
  diff_minutes = parseInt( diff_msec / (60*1000), 10 )
  return diff_minutes

module.exports = (robot) ->
  robot.respond /d (.*)/i, (msg) ->
    HUBOT_D_API_KEY = process.env.HUBOT_D_API_KEY
    message = msg.match[1]
    return unless HUBOT_D_API_KEY && message

    ## ContextIDを読み込む
    KEY_DOCOMO_CONTEXT = 'docomo-talk-context'
    context = robot.brain.get KEY_DOCOMO_CONTEXT || ''

    ## 前回会話してからの経過時間調べる
    KEY_DOCOMO_CONTEXT_TTL = 'docomo-talk-context-ttl'
    TTL_MINUTES = 20
    old_msec = robot.brain.get KEY_DOCOMO_CONTEXT_TTL
    diff_minutes = getTimeDiffAsMinutes old_msec

    ## 前回会話してから一定時間経っていたらコンテキストを破棄
    if diff_minutes > TTL_MINUTES
      context = ''

    url = 'https://api.apigw.smt.docomo.ne.jp/dialogue/v1/dialogue?APIKEY=' + HUBOT_D_API_KEY
    user_name = msg.message.user.name

    request = require('request');
    request.post
      url: url
      json:
        utt: message
        nickname: user_name if user_name
        context: context if context
      , (err, response, body) ->
        ## ContextIDの保存
        robot.brain.set KEY_DOCOMO_CONTEXT, body.context

        ## 会話発生時間の保存
        now_msec = new Date().getTime()
        robot.brain.set KEY_DOCOMO_CONTEXT_TTL, now_msec

        msg.send body.utt

起動設定(常駐化、環境変数設定)

  • ./bin/hubotを編集
#!/bin/sh

set -e

ARG1=$1

export PATH="node_modules/.bin:node_modules/hubot/node_modules/.bin:$PATH"
export HUBOT_SLACK_TOKEN=xoxb-xxx
export HUBOT_CHATWORK_TOKEN="xxx"
export HUBOT_CHATWORK_ROOMS="xxx"
export HUBOT_CHATWORK_API_RATE="900" #時間あたりのポーリング回数(4秒に1回)
export HUBOT_UL_API_KEY=xxx
export HUBOT_CG_API_KEY=xxx
export HUBOT_D_API_KEY=xxx

#exec node_modules/.bin/hubot -a chatwork -n mohi

start() {
        forever start -c coffee node_modules/.bin/hubot -a chatwork -n mohi
}
stop () {
        forever stop -c coffee node_modules/.bin/hubot
}
status() {
        forever list
}
restart() {
        forever restartall
}

case "$ARG1" in
        "stop" )
                stop
                ;;
        "restart" )
                restart
                ;;
        "start" )
                start
                ;;
        "status" )
                status
                ;;
esac
  • 起動
$ bin/hubot start

最後に

使い方

「mohi APIオプション 会話内容」
・Coto Goto APIの場合(mohi cg 会話内容)
  下記を聞くと教えてくれます。
   ・ニュース
   ・おみくじ引く
   ・○○の天気
   ・○○の意味を知りたい
・User Localの場合(mohi ul 会話内容)
・Docomo雑談APIの場合(mohi d 会話内容)

今後

今後はDocomoのAPIや他社も利用して、画像認識や形態素解析も実装したあと、
独自で情報を取ってくるものを作りたいと思ってます。