2021/12/22(水)の回です。
前の日は、 @yukima77 さんの「Azure Logic Appsを使って、受信メールをLINE公式アカウントに投稿する」でした。
防災のためのアプリとのことで、地域の方にとって安心安全への貢献がハンパないです。
このアプリから発信される情報はクマの出没情報とのことなので、内容は「けっこうやべえ」投稿なのですが、アイコンのクマがかわいらしいというのがこれまたにくいです。
2022/02/11追記
この記事により、
$\huge{参加景品}$
を頂きました!!!
$\huge{ありがとうございます!}$
はじめに
Elixirを楽しんでいますか
Elixirを使ってLINEボットを作ってみます。
まずは基本のオウム返しです。
ソースコード
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 v2のGET /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"]))
- URLがあります
- HTTP GETします
- レスポンスから
body
を取り出します - JSONデコードします => 結果は要素がマップ1であるリストが得られます
- 各要素から
title
とurl
を取り出したマップに変換します
ということを行っています。
|>
は、パイプ演算子と呼ばれるものです。
前の計算結果を次の関数の第一引数にいれて実行してくれます。
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
それではプログラムをつくっていきましょう。
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
を編集します。
defp deps do
[
{:bandit, "~> 0.4.5"},
{:jason, "~> 1.2"},
{:httpoison, "~> 1.8"}
]
end
バージョンの指定は各Hexのドキュメントに従います。
次のコマンドで、依存関係を解決します。
$ mix deps.get
③プログラムを書く
Webhookイベントを受信するモジュールBanditLineBot.MyPlug
を作ります。
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。
def start(_type, _args) do
children = [
{Bandit, plug: BanditLineBot.MyPlug, scheme: :http, options: [port: 4000]} # add
]
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
を作ります。
ここでは単純に、イベントのtype
がtext
であるものを対象にして、そのままオウム返ししています。
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で実装します。
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
まずはローカルマシンでイゴかしてみます。
あー、そもそもLINE Developersコンソールでの設定の話を一切書いていません。
「Messaging APIを始めよう」をご参照ください
以下の環境変数に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.io
をWebhook 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でイゴかす」
どうぞご参照ください。
Wrapping up
Elixirを使って、まずは基本 のオウム返しを作りました。
ソースコードは、ここにあります。
Enjoy Elixir
Where to go next
返信内容を変えたい場合には「A3RTさんのTalk APIをElixirで使ってみます」を参考にTalk APIを組み込んでみたり、さまざまなAPIとの連携が考えられます。
Elixirに興味をもっていただけましたら幸いです。
以下、Elixirのお役立ち情報です。
オススメの書籍
- プログラミングElixir(第2版) -- オーム社
- Elixir実践ガイド -- インプレス
Webアプリケーションを楽しむなら
IoTを楽しむなら
AIを楽しむなら
コミュニティ
-
elixir.jp Slack workspaceに参加してみてください
- マヂ、やさしい人ばっかりのコミュニティ
- あなたの困ったをきっと解決してくれるでしょう
- NervesJP Slack workspaceでは、NervesやIoTが好きな愉快なfolksたちがあなたの訪れを歓迎します
- たくさんのコミュニティがあります
- @kn339264 さん作の素敵な資料をご紹介します
- Elixirコミュニティ の歩き方〜国内オンライン編〜
@piacerex さん作
-
@kikuyuta 先生の「世俗派関数型言語 Elixir を関数型言語風に使ってみたらやっぱり関数型言語みたいだった」より。Elixirはコワくないですよ〜。 ↩ ↩2
-
「動き始めます」の意。おそらく西日本の方言、たぶん。NervesJPではおなじみ。少し古いオートレースの映像ですが、実況アナウンサーが「針3イゴきます」とはっきり言っています。https://autorace.jp/netstadium/SearchMovie/Movie/iizuka?date=2017-01-04&p=5&race_number=11&pg= ↩ ↩2
-
大時計の針のこと。針がイゴいてある地点まで到達すると選手はスタートを切って良い発走の合図。針がイゴきはじめると(おそらく)選手は緊張するし、スタートはその後のレース展開に大きく影響するので、車券を握りしめている観客たちがもっとも緊張する瞬間であるため、先の尖った鋭いものを連想させる針は緊張の暗喩としても言い得て妙。 ↩