LINE bot作成チュートリアルをやってみたPart3~開発編~

f:id:hedgehogweeklyreport:20190203004820p:plain

引っ越しが一段落して漸く重い腰が上がったけど色々と忘れてる感が拭えない…
前回LINE bot作成チュートリアルをやってみたPart2~開発編~の続きから

結論から言うとコケてまだ完成してない…

Part2の復習

前回やったことを改めてざっくり振り返る

  • rails new を実行して新規Railsプロジェクトの作成
  • LINEのMessaging APIを使う為にチャネルの作成
  • AmazonProduct Advertising APIを使う為に開発者登録
  • Gemfile編集
  • bundle installを実行して依存ライブラリのインストール
  • controllerについてさらっとお勉強

Part3でやりたいこと

  • 実装したい処理を整理する
  • 処理を実現させる為に必要なネタをリファレンス等を参考に探す
  • Controllerに記述する

実装したい処理を整理する

今回構想しているBotはLINEを使ってAmazonから商品データを引っ張ってくる本当にシンプルな物

処理

  1. LINEで利用者よりメッセージを受け取る
  2. メッセージの内容からAmazonAPIを使って商品検索して商品の[概要]、[画像]、[価格]、[リンク]を取得する
  3. LINEのAPI機能のFlex Messageを使用してメッセージとして返信する

こんな感じ

APIで何処までやれるか分からんけど特定カテゴリーの新刊とか、ベストセラーとか、☆4以上でフィルタリングとか色々出来るようになったら楽しそう
応用は慣れたらやりたいな〜

処理を実現させる為に必要なコードをリファレンス等を参考に探す

参考にするリファレンス

Ruby on Railsドキュメント
Product Advertising API開発者ガイド
Messaging API
LINE公式line-bot-sdk-ruby
こんな感じ?

[LINEで利用者よりメッセージを受け取る]為のネタ集め

EchobotのサンプルらしいけどLINE公式line-bot-sdk-rubyに良さげなのがあるので流用できそう

def client
  @client ||= Line::Bot::Client.new { |config|
    config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
    config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
  }
end

post '/callback' do
  body = request.body.read

  signature = request.env['HTTP_X_LINE_SIGNATURE']
  unless client.validate_signature(body, signature)
    error 400 do 'Bad Request' end
  end

  events = client.parse_events_from(body)

  events.each { |event|
    case event
    when Line::Bot::Event::Message
      case event.type
      when Line::Bot::Event::MessageType::Text
        message = {
          type: 'text',
          text: event.message['text']
        }
        client.reply_message(event['replyToken'], message)
      end
    end
  }

  "OK"
end
[メッセージの内容からAmazonAPIを使って商品検索する]為のネタ集め

Product Advertising API開発者ガイドのSearchIndex-ItemSearch パラメータの組合せ (JP)あたりで色々出来そう?

例えば

SearchIndex: Books ItemSearch リクエストで、SearchIndexパラメータが"Books" の場合、リクエスト内で使用できるのは次のパラメータのみです。

f:id:hedgehogweeklyreport:20190202152422p:plain:w350

この場合、LINEbotで入力したキーワードをベースに商品検索できればいいから、SearchIndexの中のbooksを使ってitemSearchとしてリクエストを投げれば本が検索できるよって事と認識。

更にJP サイトの並べ替え値ではAmazonで買い物する際に使用するように値を指定すれば並び替えも出来そう

booksの場合は↓

説明
salesrank 売れている順番
pricerank 価格:低〜高
inverse-pricerank 価格:高〜低
detarank 出版日:新しい日付〜古い日付
titlerank アルファベット順:A〜Z
-taitlerank アルファベット順:Z〜A

レスポンスグループではリクエスト結果に対して返ってくる情報を絞り込めるとのこと

使いそうなレスポンスグループ

レスポンスグループ 概要
ItemAttributes 商品を説明する多数の属性を返すことができる
Images 商品について利用できる全ての画像の URL を返す
Reviews カスタマーレビューを含むiFrameのURLを返す
[LINEのAPI機能のFlex Messageを使用してメッセージとして返信する]為のネタ集め

LINE公式のFlex Message Simulatorを使う

いくつかサンプルの種類があるようですがAmazonに相応しい[Shopping]を選択して[作成する]を押下する

f:id:hedgehogweeklyreport:20190202161307p:plain:w350

作成結果

{
  "type": "carousel",
  "contents": [
    {
      "type": "bubble",
      "hero": {
        "type": "image",
        "size": "full",
        "aspectRatio": "20:13",
        "aspectMode": "cover",
        "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_5_carousel.png"
      },
      "body": {
        "type": "box",
        "layout": "vertical",
        "spacing": "sm",
        "contents": [
          {
            "type": "text",
            "text": "Arm Chair, White",
            "wrap": true,
            "weight": "bold",
            "size": "xl"
          },
          {
            "type": "box",
            "layout": "baseline",
            "contents": [
              {
                "type": "text",
                "text": "$49",
                "wrap": true,
                "weight": "bold",
                "size": "xl",
                "flex": 0
              },
              {
                "type": "text",
                "text": ".99",
                "wrap": true,
                "weight": "bold",
                "size": "sm",
                "flex": 0
              }
            ]
          }
        ]
      },
      "footer": {
        "type": "box",
        "layout": "vertical",
        "spacing": "sm",
        "contents": [
          {
            "type": "button",
            "style": "primary",
            "action": {
              "type": "uri",
              "label": "Add to Cart",
              "uri": "https://linecorp.com"
            }
          },
          {
            "type": "button",
            "action": {
              "type": "uri",
              "label": "Add to wishlist",
              "uri": "https://linecorp.com"
            }
          }
        ]
      }
    },
    {
      "type": "bubble",
      "hero": {
        "type": "image",
        "size": "full",
        "aspectRatio": "20:13",
        "aspectMode": "cover",
        "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_6_carousel.png"
      },
      "body": {
        "type": "box",
        "layout": "vertical",
        "spacing": "sm",
        "contents": [
          {
            "type": "text",
            "text": "Metal Desk Lamp",
            "wrap": true,
            "weight": "bold",
            "size": "xl"
          },
          {
            "type": "box",
            "layout": "baseline",
            "flex": 1,
            "contents": [
              {
                "type": "text",
                "text": "$11",
                "wrap": true,
                "weight": "bold",
                "size": "xl",
                "flex": 0
              },
              {
                "type": "text",
                "text": ".99",
                "wrap": true,
                "weight": "bold",
                "size": "sm",
                "flex": 0
              }
            ]
          },
          {
            "type": "text",
            "text": "Temporarily out of stock",
            "wrap": true,
            "size": "xxs",
            "margin": "md",
            "color": "#ff5551",
            "flex": 0
          }
        ]
      },
      "footer": {
        "type": "box",
        "layout": "vertical",
        "spacing": "sm",
        "contents": [
          {
            "type": "button",
            "flex": 2,
            "style": "primary",
            "color": "#aaaaaa",
            "action": {
              "type": "uri",
              "label": "Add to Cart",
              "uri": "https://linecorp.com"
            }
          },
          {
            "type": "button",
            "action": {
              "type": "uri",
              "label": "Add to wish list",
              "uri": "https://linecorp.com"
            }
          }
        ]
      }
    },
    {
      "type": "bubble",
      "body": {
        "type": "box",
        "layout": "vertical",
        "spacing": "sm",
        "contents": [
          {
            "type": "button",
            "flex": 1,
            "gravity": "center",
            "action": {
              "type": "uri",
              "label": "See more",
              "uri": "https://linecorp.com"
            }
          }
        ]
      }
    }
  ]
}

JSON形式で出力されました。
あくまでサンプルなのでこのままでは使えませんが流用していきます。 因みに各要素の説明はこちら

集めたネタを使ってcontroller.rbに落とし込む

class LinebotsController < ApplicationController
  require 'line/bot'

def client
  @client ||= Line::Bot::Client.new { |config|
    config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
    config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
  }
end

post '/callback' do
  body = request.body.read

  signature = request.env['HTTP_X_LINE_SIGNATURE']
  unless client.validate_signature(body, signature)
    error 400 do 'Bad Request' end
  end

  events = client.parse_events_from(body)

  events.each { |event|
    case event
    when Line::Bot::Event::Message
      case event.type
      when Line::Bot::Event::MessageType::Text
        message = {
          type: 'text',
          text: event.message['text']
        }
         messages = amazon_search(input)
        client.reply_message(event['replyToken'], message)
      end
    end
  }

  "OK"
end

  def amazon_search(input)
    
    Amazon::Ecs.debug = true

    request = Amazon::Ecs.item_search(
      input, 
      search_index: 'books', 
      response_group: 'ItemAttributes, Images, Reviews',
      country: 'jp',
      sort: 'salesrank'
    )
    bot_reply(request)
  end
 
  def bot_reply(request)
    title = item.get('ItemAttributes/Title')
    price = item.get('ItemAttributes/ListPrice/FormattedPrice') 
    url = bitly_shorten(item.get('DetailPageURL'))
    image = item.get('LargeImage/URL')
    review = item.get('CustomerReviews/Review')
    {
      "type": "bubble",
      "hero": {
        "type": "image",
        "size": "md",
        "aspectRatio": "20:13",
        "aspectMode": "fit",
        "url": image
      },
      "body":
      {
        "type": "box",
        "layout": "vertical",
        "spacing": "none",
        "contents": [
          {
            "type": "text",
            "text": title,
            "wrap": true,
            "weight": "bold",
            "size": "lg"
          },
          {
            "type": "box",
            "layout": "baseline",
            "contents": [
              {
                "type": "text",
                "text": price,
                "wrap": true,
                "weight": "bold",
                "flex": 0
              }
               {
                  "type": "box",
                  "layout": "baseline",
                  "contents": [
                 {
                  "type": "text",
                  "text": review,
                  "wrap": true,
                  "weight": "bold",
                  "flex": 0
                }
            ]
          }
        ]
      },
      "footer": {
        "type": "box",
        "layout": "vertical",
        "spacing": "none",
        "contents": [
          {
            "type": "button",
            "style": "primary",
            "action": {
              "type": "uri",
              "label": "Go to Amazon",
              "uri": url
            }
          }
        ]
      }
    }
end

ん〜取り敢えずこれで行ってみる

ルーティングの設定

routes.rbを編集する

ルーティング(routes)とは

URLとアプリケーションのパラメータを結びつける仕組み

デフォルトのroutes.rb

Rails.application.routes.draw do
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

コメント箇所を削除して編集

GitHubリポジトリへ登録

GitHubにログインして新規リポジトリを作成する

アカウントページより[repositories]を選択して、[New]を押下

f:id:hedgehogweeklyreport:20190202174634p:plain:w350

Create New Repository画面に遷移するので、[repository name]に任意のリポジトリ名を入力してリポジトリのpublic(公開)/private(非公開)設定を選択したら[Create repository]を押下

f:id:hedgehogweeklyreport:20190202175613p:plain:w350

作成後[Code]タブに表示されている[Quick setup]のSSHコマンドを控えておく 例:git@github.com:アカウント名/リポジトリ名.git

f:id:hedgehogweeklyreport:20190202180106p:plain:w350

ターミナルにてLINEbot用のディレクトリに移動して、パスが合っている事を確認する

pwd
ls

f:id:hedgehogweeklyreport:20190202180429p:plain:w350

GitHub 初心者の最初のプッシュを参考にpushする

git init
git add .
git commit -m "first commit"
git remote add origin <URLアドレス>
git remote -v
git push origin master
gitコマンド/オプション 概要
init ディレクトリにリポジトリを作成
add ファイルやディレクトリをインデックスに登録
commit -m インデックスに追加されたファイルをコミット
-mでコミットメッセージを指定
remote リモートリポジトリの操作
-vで一覧表示
push リモートリポジトリブランチにプッシュする

コマンドの実行結果

before

f:id:hedgehogweeklyreport:20190202224822p:plain:w350

after

f:id:hedgehogweeklyreport:20190202225143p:plain:w350

ローカルのLINEbotディレクトリらがGitHubのリモートリポジトリへpushされたことが分かりますね。

Herokuにデプロイ

ここでアカウント登録

The Heroku CLIから[Download the Installer]をクリックしてインストーラに従って端末にツールをインストールする

f:id:hedgehogweeklyreport:20190202231703p:plain:w350

Herokuにログイン

ターミナルよりログインコマンドを実行する

heroku login

コマンド実行後、キー入力を求められるのでq以外のキーを入力する

ログイン画面が表示されるのでLog inをクリック

f:id:hedgehogweeklyreport:20190202232255p:plain:w350

ログインが成功しました

f:id:hedgehogweeklyreport:20190202232329p:plain:w350

アプリケーションの作成

heroku create linebot-amz

f:id:hedgehogweeklyreport:20190202232705p:plain:w350

Herokuにデプロイ

git push heroku master

f:id:hedgehogweeklyreport:20190202234113p:plain:w350

なんかコケた…

You must use bundle 2 or greater with this lockfile.で調べる

heroku buildpacks:set https://github.com/bundler/heroku-buildpack-bundler2

↑を実行するといいらしい

結果

f:id:hedgehogweeklyreport:20190202234432p:plain:w350

改めてデプロイ

git push heroku master

f:id:hedgehogweeklyreport:20190202234701p:plain:w350

いけた…?

やっぱりいけてなかった

f:id:hedgehogweeklyreport:20190203003742p:plain:w350

もう一つだけ進めて調べる…

環境変数の設定

heroku config:set LINE_CHANNEL_SECRET=<LINEチャンネルシークレット>
heroku config:set LINE_CHANNEL_TOKEN=<LINEチャンネルトークン>
heroku config:set ASSOCIATE_TAG=<Amazonアソシエイトタグ>
heroku config:set AWS_ACCESS_KEY_ID=<AWSアクセスキー>
heroku config:set AWS_SECRET_KEY=<AWSシークレットキー>

今日は疲れたので、一旦仕切り直して調査する!