技術

LINE BOTを無料で試してみました。heroku + Rails

投稿日:2016年4月25日 更新日:

こんばんは

たまにはエンジニアっぽいところを見せようと思い、遅ればせながら、流行りのLINE BOTを試してみました。

今回作ったBOTの返答のロジックはいたってシンプル。

会社名の由来である「タマゴが先か、ニワトリが先か」という言葉にちなみ、「(前の人が話した言葉)が先か、(送ったメッセージ)が先か」という言葉をBOTが返すものを作ってみました。

(会社名の由来はドリルを買う人はドリルが欲しい。を参照)

LINE BOTの仕組みとしてはこんな感じです。

bot_img001

LINE BOTにはSSLが必要なのと固定IPが必要なので、YOUR SYSEMの部分のサーバにはherokuを使います。
herokuは固定IPではないのですが、Fixieというアドオンを使うと、プロキシを経由してくれるのでIPを固定できます。

LINEへの登録

LINE BOT APIのトライアルはhttps://business.line.me から登録できます。
すでにトライアルの配布は上限に達してしまい終わっているのですが、私はこっそり登録してあったものを使います。

デベロッパー登録が終わると、以下の3つが取得できます。
Channel ID
Channel Secret
MID

後でアプリで利用しますのでメモっておきます。

スクリーンショット_2016-06-02_02_56_24

herokuにアプリ作成

herokuにアプリを作成します。
herokuはIPが固定ではないのですが、Fixieというアドオンを追加します。
Fixieを使うとプロキシを経由するようになるので、接続元IPを固定できます。

$ heroku login
$ heroku create your_app
$ heroku addons:create fixie:tricycle

Server IP Whitelistに固定IPを登録

LINE BOT APIを呼ぶにはLINE BOT APIにFixieで固定した接続元IP(Whitelist)を登録する必要があります。

heroku上で作ったアプリの設定画面から Fixie の画面を開くと次のような画面になるので、
Outbound IPs
の2つの値をメモっておきます。

さらに
Proxy URL
もあとで使うのでメモっておきます。

スクリーンショット_2016-06-02_02_26_01

https://business.line.meから
Channels → Server IP Whitelistを選び、「Server IP Whitelist」にメモったOutbound IPsを登録します。

スクリーンショット_2016-06-02_02_39_42

コールバックURLの登録

コールバックURLをLINE BOT APIに登録します。

今回はhttp://xxx.com:443/callbackというURLで後述のアプリを作成しようとおもいます。(xxx.comの部分はherokuのURL)
https://business.line.meから
Channels → Basic Infomationを選び、下の方にあるEDITボタンより「Callback URL」を登録します。
その際プロトコルはhttps、ポート番号は443を必ず指定します。(指定しないと正しく呼び出せないらしい。)

https://xxxx.com:443/callback

スクリーンショット_2016-06-02_02_48_24

これで事前準備は終わりです。
次からは本題のBOT用アプリ作成です。

Railsアプリの作成

次に、自分のマシンに新しいRailsプロジェクト作成します。

$ rails new your_app

herokuのDBにはPostgreSQL、LINE BOT APIとのHTTP接続にはFaradayを使うので、Gemfileに以下を追加。

gem "pg"
gem "faraday"
gem "faraday_middleware"

前の人が話した言葉や、ユーザの管理にはherokuで利用できるPostgresqlを使うので、database.ymlに以下を追加。
database、username、passwordに関してはherokuにログインすると確認できます。

production:
adapter: postgresql
encoding: utf8
database: your_database
port: 5432
username: your_username
password: your_password

route.rbにLINE BOT APIに登録するコールバックURLのルーティング設定

Rails.application.routes.draw do
  post '/callback' => 'webhook#callback'  
end

ユーザを管理するモデルの作成

$ rails generate model users mid:string display_name:string status:integer

前の人が話した言葉を管理するユーザの管理するモデルの作成

$ rails generate model messages user_id:integer text_message:string

次にコントローラ。

class WebhookController < ApplicationController
  protect_from_forgery with: :null_session

  CHANNEL_ID =  ENV['LINE_CHANNEL_ID']
  CHANNEL_SECRET = ENV['LINE_CHANNEL_SECRET']
  CHANNEL_MID = ENV['LINE_CHANNEL_MID']
  OUTBOUND_PROXY = ENV['LINE_OUTBOUND_PROXY']

  def callback
    unless is_validate_signature
      render :nothing => true, status: 470
    end
    result = params[:result][0]
    logger.info({from_line: result})

    if result['content']['opType'].present?

      mid = result['content']['params'][0]

      client = LineClient.new(CHANNEL_ID, CHANNEL_SECRET, CHANNEL_MID, OUTBOUND_PROXY)
      res = client.profile(mid)

      if res.status == 200
        logger.info({success: res})

        display_name = res.body['contacts'][0]['displayName']

        opType = result['content']['opType']
        case opType 
        when 4 then
          user = User.create_with(display_name: display_name, status: 4).find_or_create_by(mid: mid)
          user.display_name = display_name
          user.status = 4
          user.save!
        when 8 then
          user = User.create_with(display_name: display_name, status: 8).find_or_create_by(mid: mid)
          user.display_name = display_name
          user.status = 8
          user.save!
        else
        end
      else
        logger.info({fail: res})
      end
    else 
      text_message = result['content']['text']
      from_mid = result['content']['from']

      last_message = Message.last.text_message

      user = User.find_by(mid: from_mid)

      Message.create(user_id: user.id, text_message: text_message)

      message = last_message + "が先か、" + text_message + "が先か"

      client = LineClient.new(CHANNEL_ID, CHANNEL_SECRET, CHANNEL_MID, OUTBOUND_PROXY)
      res = client.send([from_mid], message)

      if res.status == 200
        logger.info({success: res})
      else
        logger.info({fail: res})
      end
    end

    render :nothing => true, status: :ok
  end

  private
  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

LINE BOT APIを呼ぶライブラリの作成。

require "faraday"
require "faraday_middleware"
require "json"
require "pp"

class LineClient
  module ContentType
    TEXT = 1
    IMAGE = 2
    VIDEO = 3
    AUDIO = 4
    LOCATION = 7
    STICKER = 8
    CONTACT = 10
  end
  module ToType
    USER = 1
  end

  END_POINT = "https://trialbot-api.line.me"
  TO_CHANNEL = "1383378250" # Fixed value
  EVENT_TYPE = "138311608800106203" # Fixed value

  def initialize(channel_id, channel_secret, channel_mid, proxy = nil)
    @channel_id = channel_id
    @channel_secret = channel_secret
    @channel_mid = channel_mid
    @proxy = proxy
  end

  def post(path, data)
    client = Faraday.new(:url => END_POINT) 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-Line-ChannelID' => @channel_id,
          'X-Line-ChannelSecret' => @channel_secret,
          'X-Line-Trusted-User-With-ACL' => @channel_mid
      }
      request.body = data
    end
    res
  end

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

    res = client.get do |request|
      request.url path, data
      request.headers = {
          'Content-type' => 'application/json; charset=UTF-8',
          'X-Line-ChannelID' => @channel_id,
          'X-Line-ChannelSecret' => @channel_secret,
          'X-Line-Trusted-User-With-ACL' => @channel_mid
      }
    end
    res
  end

  def send(line_ids, message)
    post('/v1/events', {
        to: line_ids,
        content: {
            contentType: ContentType::TEXT,
            toType: ToType::USER,
            text: message
        },
        toChannel: TO_CHANNEL,
        eventType: EVENT_TYPE
    })
  end

  def profile(mids)
    get('/v1/profiles', {
      mids: mids
    })
  end
end

heroku ENVの設定

LINE BOT APIの情報
Channel ID
Channel Secret
MID
とFIXIEのアカウント情報にあった
PROXY URL
を設定します。

このENVはRailsプログラムから参照します。

$ heroku config:add LINE_CHANNEL_ID="xxxxxx"
$ heroku config:add LINE_CHANNEL_SECRET="xxxxxx"
$ heroku config:add LINE_CHANNEL_MID="xxxxxx"
$ heroku config:add FIXIE_URL="http://xxxxxx"

herokuへデプロイ

最後にherokuへデプロイします

$ git add .
$ git commit -m 'xxxxxx'
$ git push heroku master

DBがあるのでマイグレートします。

$ heroku run rake db:migrate

これで準備完了です。

LINEから確認してみましょう。

LINEが入っているスマホから友達追加。

IMG_4100

うまく動いてそうです。

まだトライアルなので50人までしか友達増やせないみたいです。

今回はシンプルなロジックのBOTをつくりましたが、工夫次第で面白いものが作れそうです。

-技術
-, ,

執筆者:


  1. […] 前回、LINE BOTを無料で試してみました。heroku + RailsでLINE BOTを試してみたのですが、社内でロジックが単純すぎでくだらないという批判が多かったので、ロジックを少し改良しました。 […]

comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

関連記事

[Angular] has no exported member ‘XXXLoginComponent’.

どうも中田です Angularでコンポーネント名を階層構造にあった名前にしようと …

[ Ionic ] アプリの画面向きを指定する方法

こんにちは。たなかです。 Ionicフレームワークでモバイルアプリ開発しています …

[Rails] エラー unable to convert unpermitted parameters to hash について

こんにちは。佐藤です。 今回はRailsのpermit!についての備忘録です。 …

[Angular6] 一つのタグに複数の条件分岐を書く

こんにちは どうしても一つのタグに複数の条件分岐を書きたい時があるかと思います。 …

GooglePlayConsoleで「製品版として公開を開始」がグレーアウトして押せない!

こんにちは。田中です。 携わっていたアプリ開発案件ですが、ついに最初のリリースが …