20
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

LINE botを Phoenix + Elixirで試してみる

Posted at

とりあえずオウム返しをするだけのbotを作ってみようと思います。
PHPやRubyでは、すでに色々な人が記事あげてくれていて、同じようにやればできるかなと思ってやってみました。

Phoenix+Elixirはちょっと触ったことがある程度。ライブラリのHTTPoison、Poisonは初めて触ります。herokuもそんなに詳しくないし、LINEのBOT APIも初見の状態です。

必要なもの

  • LINE BOT APIのアカウント
  • Callbackを受け付けるサーバ(https必須)
  • bot

LINEのアカウント登録

1万人限定だったかと思いますが、すでに一杯になってしまったようです。。。
https://business.line.me/ja/products/4/introduction

登録すると以下の情報が発行されますので控えておきましょう。

  • Channel ID
  • Channel Secret
  • MID

Callbackを受け付けるサーバ(https必須)

サーバー

レンタルサーバもないし、AWSに構築とか面倒だなぁと思ったらherokuでしかも無料でいけるっぽい。
こちらを参考にさせていただきました。

LINE BOT をとりあえずタダで Heroku で動かす
http://qiita.com/yuya_takeyama/items/0660a59d13e2cd0b2516

APIの呼出すために、接続元 IPをLINEに登録する必要があるけれど、
Fixieというaddonを使うことでIPを固定できるとのことです。

Phoenix + Elixirをherokuで使う

ついでにDockerも使ってみようということで以下を参考にさせていただきました。

Dockerを使ってHerokuへPhoenixアプリをデプロイする
http://tech.misoca.jp/entry/2015/06/26/145324

アプリの作成
% mix phoenix.new phoenix_heroku_line_bot
% cd phoenix_heroku_line_bot

dockerプラグインのインストール
% heroku plugins:install heroku-docker

Procfileの作成
% echo "web: MIX_ENV=prod NODE_ENV=production BRUNCH_ENV=production mix phoenix.server" > Procfile

app.jsonの作成
% cat << EOF > app.json
{
  "name": "phoenix_heroku_line_bot",
  "description": "line bot sample",
  "image": "joshwlewis/docker-heroku-phoenix:latest",
  "addons": [
    "heroku-postgresql"
  ]
}
EOF

Dockerfileの作成
% heroku docker:init
% cat << EOF >> Dockerfile

# Compile elixir files for production
ENV MIX_ENV prod

# This prevents us from installing devDependencies
ENV NODE_ENV production

# This causes brunch to build minified and hashed assets
ENV BRUNCH_ENV production

# We add manifests first, to cache deps on successive rebuilds
COPY ["mix.exs", "mix.lock", "/app/user/"]
RUN mix deps.get

# Again, we're caching node_modules if you don't change package.json
ADD package.json /app/user/
RUN npm install

# Add the rest of your app, and compile for production
ADD . /app/user/
RUN mix compile && brunch build && mix phoenix.digest
EOF

一旦、DBは使わないので設定せずにこのままデプロイしてみる。

% heroku create phoenix-heroku-line-bot
% heroku docker:release --app phoenix-heroku-line-bot
% heroku open --app phoenix-heroku-line-bot

この --app APPって毎回指定しないといけない?調べたらgit remote追加すればいい。

% git init
% git remote add heroku git@heroku.com:phoenix-heroku-line-bot.git

https://phoenix-heroku-line-bot.herokuapp.com/
が開いて、phoenixのページが出ればとりあえずデプロイできてる。

fixieのアドオン追加

% heroku addons:create fixie:tricycle

https://dashboard.usefixie.com/#/account
herokuのページから追加されたfixieのアドオンのページを開く。

  • Proxy URL
  • Outbound IPs
    を控えておく

LINEの設定(callback, white IP list)

callback

https://developers.line.me/
右上のchanells > 右下のEdit

Callback URLを指定する。ポート番号も指定する必要があることに注意。
https://phoenix-heroku-line-bot.herokuapp.com:443/linebot/callback

これで、/linebot/callbackというパスでアプリを作ればよい。

Server IP Whitelist

左のメニューから、Server IP whitelist
先ほどfixieのアドオン追加時に控えたIPアドレス2つを指定する。

Phoenixアプリの実装

ライブラリの指定

HTTPクライアントとしてHTTPoison、JSONを扱うPoisonを使う。

  • depsにhttpoisonとpoisonを追加。
  • applicationにhttpoisonを追加
mix.exs

  def application do
    [
      mod: { HerokuPhoenix, [] },
      applications: [
        :phoenix, :phoenix_html, :cowboy, :logger, :gettext,
        :phoenix_ecto, :postgrex, 
        :httpoison #add
      ]
    ]
  end

  defp deps do
    [
      {:phoenix, "~> 1.1.3"},
      {:phoenix_ecto, "~> 2.0"},
      {:postgrex, ">= 0.0.0"},
      {:phoenix_html, "~> 2.3"},
      {:phoenix_live_reload, "~> 1.0", only: :dev},
      {:gettext, "~> 0.9"},
      {:cowboy, "~> 1.0"},
      {:httpoison, "~> 0.8.0"}, #add
      {:poison, "~> 1.3"} #add
    ]
  end
% mix deps.get

callbackパスの設定

/linebot/callbackというURLでLINE APIサーバからのリクエストを受け付ける。

router.ex

  scope "/linebot", PhoenixHerokuLineBot do
    pipe_through :api

    post "/callback", LinebotController, :callback
  end

オウム返しAPIの実装

web/controllers/linebot_controller.ex
  def callback(conn, %{"result" => result}) do
    %{"content" => content} = List.first(result)

    endpoint_uri = "https://trialbot-api.line.me/v1/events"

    json_data = %{
      to: [content["from"]], # 送信先リスト
      toChannel: 1383378250, # 固定値
      eventType: "138311608800106203", # 固定値
      content: %{
        contentType: 1,
        toType: 1,
        text: content["text"] # 受信したメッセージをそのまま返す
      }
    } |> Poison.encode!

    headers = %{   
      "Content-Type" => "application/json; charset=UTF-8",
      "X-Line-ChannelID" => "1814639909", # 自分のLINE Channel IDに変更
      "X-Line-ChannelSecret" => "ef7f43a2fd8a3293dac37183bd0c37b0", # 自分のSecretに変更
      "X-Line-Trusted-User-With-ACL" => "9cac6ca1f4832ebu6d04c1772bd06680e" # 自分のMIDに変更
    }

    options = [
      {:proxy, "http://velodrome.usefixie.com:80"},
      {:proxy_auth, {"fixie", "IelXgZzaM1kyJdS"}}
    ]

    case HTTPoison.post(endpoint_uri, json_data, headers, options) do
      {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
        IO.puts body
      {:ok, %HTTPoison.Response{status_code: 404}} ->
        IO.puts "Not found :("
      {:error, %HTTPoison.Error{reason: reason}} ->
        IO.inspect reason
    end

    send_resp(conn, :no_content, "")
  end

受け取るメッセージと送信するメッセージの仕様はここから確認。
https://developers.line.me/bot-api/getting-started-with-bot-api-trial

まず、パターンマッチングでresultを取り出して、配列で返ってきてるので、List.first(result)で一つ目だけ取り出してます。resultが複数返ってくるのがどんな時なのかはわかりませんでした。

resultの中から、送信者のIDとメッセージを取り出してそのままメッセージ送信APIを叩くという処理です。

送信APIを叩くには、HTTPoisonを使って以下の仕様で呼び出せば良い。

ElixirのHTTPoisonライブラリの使い方
http://qiita.com/ColdFreak/items/4beb6cacfbb39baa955f

ここを参考にendpoint_uri、json_data、headersを指定してHTTPoison.postを呼び出す。

  • プロトコルはHTTPS
  • エンドポイントホスト
    • trialbot-api.line.me
  • リクエストヘッダーに以下の3つを指定する
    • X-Line-ChannelID: Channel ID
    • X-Line-ChannelSecret: Channel secret
    • X-Line-Trusted-User-With-ACL: MID (of Channel)
  • POST
  • エンドポイントURL
    • /v1/events
  • BODY
    • to ※ 配列なので注意
    • toChannel ※固定値
    • eventType ※固定値
    • content

APIのリファレンス
https://developers.line.me/bot-api/api-reference

送信元がServer IP whitelistに指定したIPアドレスになるように、proxyを指定する。
HTTPoison.postの第4引数に指定する。

HTTPoisonでプロキシを使用する
http://qiita.com/Kazunori_Sada/items/a8e42545a9b51bcf3bf9#_reference-1d0180aea7b858c9c335

FixieのProxy URLはこんな感じに書いてあるんですけど、ユーザとパスワードが入っていることにしばらく気づかずハマりました。
http://fixie:IelXgZzaM1kyJdS@velodrome.usefixie.com:80

デプロイして動作確認

% heroku docker:release
% heroku logs --tail

LINEのページのURLから友達登録して、メッセージ送信してみると、/linebot/callbackが呼びだされ、入力したメッセージが返ってくるはず。

ハマったところまとめ

自分がハマったところをまとめると、

  • herokuでDockerを使う方法
     今回の内容とは関係ないけどやってみたくなってしまった。ローカルではdockerをどう使うべき??
  • 送信データのtoは配列。
     他のページを参考にして、そちらでは配列になってたけど見落としてた。
  • FixieのProxy URLにユーザ・パスが入ってる。
     なんか、ランダムな文字列のURLが生成されてるんだなぁと思ってたらユーザ・パスだった。
     Proxyのエラーだと、エラーの内容がclosedとしか出てなくて、原因を特定する方法がわからなかった。Proxyの指定を外すとこのIPは許可されていない的なログが出たのでなんとか特定することができた。

他のサイトで注意しろと書いてあるもの

  • callbackのURLにはポート番号をつける。
  • callbackやIP whitelistは反映にちょっとかかる。
20
18
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?