Rails Heroku × linebot Repl-AIで対話できるbotを作ってみた

ハッカソンで動くものができたので、メモ。 
全ソースは以下です。 
https://github.com/hiranoi/kuidaore_bot

# INDEX
1. この記事のゴール
1. Rails Heroku × lineapiでオウム返しするbotを作る
1. Rails Heroku × Repl-AIで会話できるbotを作る
1. Repl-AIの会話を大阪弁に変換すして返すbotを作る

# この記事のゴール
* これを作る(参加者募集時に作った資料)
![Cm0IepEVYAAaci1.jpg](https://qiita-image-store.s3.amazonaws.com/0/115082/ed1c593c-71b1-97da-cfe5-5e82eb854512.jpeg)
  
* こうなる(会話が成り立っていないのは、Repl-AIのチューニングをしていないから、、、)
![slack_for_ios_upload_1024-1.jpg](https://qiita-image-store.s3.amazonaws.com/0/115082/352da4d6-822a-d99a-846c-777ad0ced6d5.jpeg "slack_for_ios_upload_1024-1.jpg")


# Rails Heroku × lineapiでオウム返しするbotを作る
* 以下の記事を全て参考にさせていただきました。本当にありがとうございます。  
http://qiita.com/Arahabica/items/98e3d0d5b65269386dc4

# Rails Heroku × Repl-AIで会話できるbotを作る
* こちらは本家リファレンスを参考にライブラリ作成  
  https://repl-ai.jp/references/registration.html  
  https://repl-ai.jp/references/dialogue.html
  

```ruby:/lib/repl_ai_client.rb
require "faraday"
require "faraday_middleware"
require "json"
require "pp"

class ReplAiClient
  URL_HEAD = 'https://api.repl-ai.jp'

  def initialize(api_key, bot_id = 'normal', scenario ='greeting')
    @api_key = api_key
    @bot_id  = bot_id
    @scenario = scenario
  end

  def get_user_id
    res = post('/v1/registration',{
      botId: @bot_id
      })
    res.body
  end

  def get_message(user_id, message, flag = true)
    res = post('/v1/dialogue',{
        appUserId: user_id,
        botId: @bot_id,
        voiceText: message,
        initTalkingFlag: flag,
        initTopicId: @scenario,
        appRecvTime: Time.now.strftime('%Y/%m/%d %H:%M:%S'),
        appSendTime: Time.now.strftime('%Y/%m/%d %H:%M:%S'),
      })
    res.body
  end

  private

  def post(path, data)
    client = Faraday.new(:url => URL_HEAD) do |conn|
      conn.request :json
      conn.response :json, :content_type => /\bjson$/
      conn.adapter Faraday.default_adapter
      # conn.proxy @proxy
    end

    res = client.post do |request|
      request.url path
      request.headers = {
          'Content-type' => 'application/json; charset=UTF-8',
          'x-api-key' => @api_key
      }
      request.body = data
    end
    res
  end
end
```	

```ruby:/app/controllers/webhook_controller.rb
class WebhookController < ApplicationController
  protect_from_forgery except: :callback # CSRF対策無効化

  CHANNEL_ID = ENV['LINE_CHANNEL_ID']
  CHANNEL_SECRET = ENV['LINE_CHANNEL_SECRET']
  CHANNEL_MID = ENV['LINE_CHANNEL_MID']
  OUTBOUND_PROXY = ENV['LINE_OUTBOUND_PROXY']
  REPL_API_KEY = ENV['REPL_API_KEY']
  
  def callback
    unless is_validate_signature
      render :nothing => true, status: 470
    end
    result = params[:result][0]
    logger.info({from_line: result})

    req_message = result['content']['text']
    from_mid = result['content']['from']

    repl_client = ReplAiClient.new(REPL_API_KEY)
    res_message = repl_client.get_message('TODO:USER_IDを入れる', req_message)

    client = LineClient.new(CHANNEL_ID, CHANNEL_SECRET, CHANNEL_MID, OUTBOUND_PROXY)
    res = client.send([from_mid], res_message['systemText']['expression'])

    if res.status == 200
      logger.info({success: res})
    else
      logger.info({fail: res})
    end
    render :nothing => true, status: :ok
  end

  private
  # LINEからのアクセスか確認.
  # 認証に成功すればtrueを返す。
  # ref) https://developers.line.me/bot-api/getting-started-with-bot-api-trial#signature_validation
  def is_validate_signature
    signature = request.headers["X-LINE-ChannelSignature"]
    http_request_body = request.raw_post
    hash = OpenSSL::HMAC::digest(OpenSSL::Digest::SHA256.new, CHANNEL_SECRET, http_request_body)
    signature_answer = Base64.strict_encode64(hash)
    signature == signature_answer
  end
end
```

```bash:terminal上で
# 定数を設定する
$ heroku config:add REPL_API_KEY="XXXXXXXXXXXXXXXXXXX"
```

※注意

* ハッカソンの時間の都合上、ユーザごとにID払い出す処理を行っていません。(LINEのIDとRepl-AIのIDを紐付けるのが面倒だった)そのため、ユーザAの会話情報がユーザBにも引き継がれます。

# Repl-AIの会話を大阪弁に変換すして返すbotを作る
* ここを参考にRubyに変換する
http://www.geocities.co.jp/Hollywood/4078/osaka/osakajs.html

```ruby:/lib/convert_to_osaka.rb
require 'yaml'

class ConvertToOsaka
  def initialize(tm)
    @tm = tm
  end

  def convert
    d = YAML.load_file("#{Rails.root}/lib/osaka.yml")
    # 文章にhashのキーが含まれているかを確認する
    d.each do |k, v|
      # 一致すればvalueに置換する
      @tm.gsub!(k, v)
    end
    @tm
  end
end
```
* センスはないが、標準語→大阪弁の対応表はHashで持つ

```yaml:/lib/osaka.yml
ありがとうございました: おおきに
あなた: あんさん
あんな: あないな
りますので: るさかいに
りますから: るさかいに
あります: あるんや
あるいは: せやなかったら
或いは: せやなかったら
ありません: おまへん
ありました: おました
いない: おらへん
いまどき: きょうび
いわゆる: なんちうか，ようみなはんいわはるとこの
・
・
・ # 長いので割愛
```
``` ruby:/app/controllers/webhook_controller.rb
# 以下の部分を編集
res = client.send([from_mid], ConvertToOsaka.new(res_message['systemText']['expression']).convert)
```

こんな感じです。まだまだ改善の余地はありますが、一旦これで動きます。