18
1

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 1 year has passed since last update.

2021/12/22(水)の回です。

前の日は、 @yukima77 さんの「Azure Logic Appsを使って、受信メールをLINE公式アカウントに投稿する」でした。

防災のためのアプリとのことで、地域の方にとって安心安全への貢献がハンパないです。
このアプリから発信される情報はクマの出没情報とのことなので、内容は「けっこうやべえ」投稿なのですが、アイコンのクマがかわいらしいというのがこれまたにくいです。


2022/02/11追記
この記事により、
$\huge{参加景品}$
を頂きました!!!

Clova Friends miniです。

$\huge{ありがとうございます!}$

IMG_20220211_110946.jpg


はじめに

Elixirを楽しんでいますか :bangbang::bangbang::bangbang:
Elixirを使ってLINEボットを作ってみます。
まずは基本の:interrobang:オウム返しです。

ソースコード

What is Elixir?

Elixirは、関数型言語に分類されます。
安心してください。
コワくない関数型言語です。
世俗派関数型言語1と言われています。

Elixirの作者はJosé Valimさんです。
Railsのご経験がある方ならご存知のあのdevise最初のコミッターは、José Valimさんです。
José Valimさんは、"The main, the top three influences are Erlang, Ruby, and Clojure."とおっしゃっています。
私はなんとなくRubyに似ていると感じています。

ここでElixirのプログラム例をお示しします。
Qiita API v2GET /api/v2/itemsの使用例です。
Elixirは1.12 or laterを使ってください。

Mix.install([{:jason, "~> 1.2"}, {:httpoison, "~> 1.8"}])

"https://qiita.com/api/v2/items?query=tag:elixir"
|> HTTPoison.get!([], [timeout: 50_000, recv_timeout: 50_000])
|> Map.get(:body)
|> Jason.decode!()
|> Enum.map(& Map.take(&1, ["title", "url"]))
  1. URLがあります
  2. HTTP GETします
  3. レスポンスからbodyを取り出します
  4. JSONデコードします => 結果は要素がマップ1であるリストが得られます
  5. 各要素からtitleurlを取り出したマップに変換します

ということを行っています。
|>は、パイプ演算子と呼ばれるものです。
前の計算結果を次の関数の第一引数にいれて実行してくれます。
Elixirのプログラムではよく使います。

What is Bandit?

Bandit is an HTTP server for Plug apps.

Elixirのライブラリは、Hexと呼ばれます。
Banditは、Hexの一種で、「Bandit is an HTTP server for Plug apps.」です。
banditという英単語をあえて日本語にしてみると、山賊のことです。

banditをイゴかしてみる」という記事で、ごくごく簡単な使い方をまとめました。

Let's get started:rocket::rocket::rocket:

それではプログラムをつくっていきましょう。
Elixirはインストール済であるとします。
Elixirをインストールすると、mixというコマンドが使えるようになります。

①プロジェクトの作成

次のコマンドでプロジェクトを作成します。

$ mix new bandit_line_bot --sup

A --sup option can be given to generate an OTP application skeleton including a
supervision tree. Normally an app is generated without a supervisor and without
the app callback.

です。

②ライブラリの追加

プロジェクトのルートにあるmix.exsを編集します。

mix.exs
  defp deps do
    [
      {:bandit, "~> 0.4.5"},
      {:jason, "~> 1.2"},
      {:httpoison, "~> 1.8"}
    ]
  end

バージョンの指定は各Hexのドキュメントに従います。

Bandit
Jason
HTTPoison

次のコマンドで、依存関係を解決します。

$ mix deps.get

③プログラムを書く:fire:

Webhookイベントを受信するモジュールBanditLineBot.MyPlugを作ります。

lib/bandit_line_bot/my_plug.ex
defmodule BanditLineBot.MyPlug do
  import Plug.Conn

  def init(options) do
    # initialize options
    options
  end

  def call(conn, opts) do
    {:ok, body, _conn} = Plug.Conn.read_body(conn, opts)

    if BanditLineBot.Line.verify(conn, body) do
      Jason.decode!(body)
      |> Map.get("events")
      |> do_something()

      conn
      |> put_resp_content_type("application/json")
      |> send_resp(200, Jason.encode!(%{}))
    end
  end

  defp do_something(events) do
    spawn(fn -> BanditLineBot.Handler.handle_events(events) end)
  end
end

BanditLineBot.MyPlugモジュールをchildrenに追加しておきます。
これでElixirアプリの開始と同時に、BanditLineBot.MyPlugモジュールがイゴき始めます2

lib/bandit_line_bot/application.ex
  def start(_type, _args) do
    children = [
      {Bandit, plug: BanditLineBot.MyPlug, scheme: :http, options: [port: 4000]} # add
    ]

署名を検証する」を参考にElixirで実装してみます。

lib/bandit_line_bot/line.ex
defmodule BanditLineBot.Line do

  # https://developers.line.biz/ja/reference/messaging-api/#signature-validation
  def verify(conn, body) do
    x_line_signature =
      conn.req_headers
      |> Enum.find(fn {key, _} -> key == "x-line-signature" end)
      |> elem(1)

    my_signature =
      :crypto.mac(:hmac, :sha256, channel_secret(), body)
      |> Base.encode64()

    my_signature == x_line_signature
  end

  defp channel_secret do
    System.get_env("CHANNEL_SECRET")
  end
end

LINEから届いたデータを煮るなり焼くなりするモジュールBanditLineBot.Handlerを作ります。
ここでは単純に、イベントのtypetextであるものを対象にして、そのままオウム返ししています。

lib/bandit_line_bot/handler.ex
defmodule BanditLineBot.Handler do
  def handle_events(events) do
    events
    |> Enum.filter(fn event ->
      Map.get(event, "type") == "message"
    end)
    |> Enum.filter(fn event ->
      Map.get(event, "message")
      |> Map.get("type")
      |> Kernel.==("text")
    end)
    |> Enum.map(fn %{"message" => %{"text" => text}, "replyToken" => replyToken} ->
      {text, replyToken}
    end)
    |> Enum.each(fn {text, replyToken} ->
      BanditLineBot.Line.Api.reply(text, replyToken)
    end)
  end
end

応答メッセージを送る」を参考に、Elixirで実装します。

lib/bandit_line_bot/line/api.ex
defmodule BanditLineBot.Line.Api do

  # https://developers.line.biz/ja/reference/messaging-api/#send-reply-message
  def reply(text, reply_token) do
    json =
      %{
        replyToken: reply_token,
        messages: [
          %{
            type: "text",
            text: text
          }
        ]
      }
      |> Jason.encode!()

    url = "https://api.line.me/v2/bot/message/reply"

    headers = [
      "Content-type": "application/json",
      Authorization: "Bearer #{channel_access_token()}"
    ]

    {:ok, _response} = HTTPoison.post(url, json, headers)
  end

  defp channel_access_token do
    System.get_env("CHANNEL_ACCESS_TOKEN")
  end
end

以上です。

Run :rocket::rocket::rocket:

まずはローカルマシンでイゴかしてみます。

あー、そもそもLINE Developersコンソールでの設定の話を一切書いていません。
Messaging APIを始めよう」をご参照ください :pray::pray_tone1::pray_tone2::pray_tone3::pray_tone4::pray_tone5:

以下の環境変数にLINE Developersコンソールにて払い出していただいた値を設定しておいてください。

  • CHANNEL_SECRET
  • CHANNEL_ACCESS_TOKEN

次のコマンドでElixirアプリケーションつまりここでは、LINEボットをイゴかします。

$ iex -S mix

ローカルマシンでイゴかしている場合にはngrokなどを使ってHTTPSにしてください。
@hyodoblog さんの「画像・音声・動画を文字起こしするLINE BotをNode.jsでつくってみよう! 〜ngrokによる高速Bot開発〜」でもご紹介されています。

こんな感じです。
以下の例では、https://95fa-163-49-206-28.ngrok.ioWebhook URLに設定するとよいです。

$ ./ngrok authtoken <token>

$ ./ngrok http 4000
ngrok by @inconshreveable                                                                                                      (Ctrl+C to quit)

Session Status                online                                                                                                           
Account                       TORIFUKUKaiou (Plan: Free)                                                                                       
Version                       2.3.40                                                                                                           
Region                        United States (us)                                                                                               
Web Interface                 http://127.0.0.1:4040                                                                                            
Forwarding                    https://95fa-163-49-206-28.ngrok.io -> http://localhost:4000                                                     
Forwarding                    https://95fa-163-49-206-28.ngrok.io -> http://localhost:4000 

これでイゴきます!


ずっとローカルマシンでイゴかし続けるのはいろいろ辛いとおもいます。
Dockerfileをつくるだけでデプロイできる!、しかも2021/12/17現在今なら無料で使えるさくらインターネットさんのHacobuneをご紹介します。

これは以下の記事にて書いております。
Elixirで作ったLINEボットをHacobuneでイゴかす
どうぞご参照ください。

Screenshot_20211217_003322_jp.naver.line.android.jpg

:tada::tada::tada:

Wrapping up :lgtm::lgtm::lgtm::lgtm::lgtm:

Elixirを使って、まずは基本:interrobang: のオウム返しを作りました。
ソースコードは、ここにあります。

Enjoy Elixir:bangbang::bangbang::bangbang:

Where to go next :rocket:

返信内容を変えたい場合には「A3RTさんのTalk APIをElixirで使ってみます」を参考にTalk APIを組み込んでみたり、さまざまなAPIとの連携が考えられます。

Nervesでイゴかすこともできます2


Elixirに興味をもっていただけましたら幸いです。
以下、Elixirのお役立ち情報です。

オススメの書籍 :books:

Webアプリケーションを楽しむなら

IoTを楽しむなら

AIを楽しむなら

コミュニティ

FCOvBkAUYAE6mL8.jpeg
@piacerex さん作 :pray::pray_tone1::pray_tone2::pray_tone3::pray_tone4::pray_tone5:

  1. @kikuyuta 先生の「世俗派関数型言語 Elixir を関数型言語風に使ってみたらやっぱり関数型言語みたいだった」より。Elixirはコワくないですよ〜。 2

  2. 「動き始めます」の意。おそらく西日本の方言、たぶん。NervesJPではおなじみ。少し古いオートレースの映像ですが、実況アナウンサーが「針3イゴきます」とはっきり言っています。https://autorace.jp/netstadium/SearchMovie/Movie/iizuka?date=2017-01-04&p=5&race_number=11&pg= 2

  3. 大時計の針のこと。針がイゴいてある地点まで到達すると選手はスタートを切って良い発走の合図。針がイゴきはじめると(おそらく)選手は緊張するし、スタートはその後のレース展開に大きく影響するので、車券を握りしめている観客たちがもっとも緊張する瞬間であるため、先の尖った鋭いものを連想させる針は緊張の暗喩としても言い得て妙。

18
1
0

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
18
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?