This is my entry for the fukuoka.ex Elixir/Phoenix Advent Calendar 2019 day 13, and it's a guide for how to write a Line bot using Elixir. 🧪🤖🧪
-
First you need to create a Messaging API Channel on the Line Developers Console. You will also need to add your bot as a friend, and know the generated Channel ID and Channel Secret.
-
Create a new Elixir app with a supervision tree:
mix new advent --sup
-
cd advent
-
If you want to use git, now we can create a repository (optional):
git init && git add . && git commit -m 'Initial commit'
I will not talk anymore about git in this guide.
-
Test the app works:
$ iex -S mix Erlang/OTP 22 [erts-10.5] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [hipe] Interactive Elixir (1.9.2) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> Advent.hello :world
-
Add
line_bot
and related dependencies tomix.exs
:defp deps do [ {:line_bot, "~> 0.1.0"}, {:plug_cowboy, "~> 2.0"} ] end
-
Fetch the
line_bot
package withmix deps.get
. -
Define a config file in
config/config.exs
:import Config config :line_bot, client_id: "YOUR_CLIENT_ID", client_secret: "YOUR_CLIENT_SECRET", # TODO remove this before deployment! skip_validation: true
The
client_id
andclient_secret
are the Channel ID and Channel Secret are taken from the Line Developer Console. -
Check that our
:advent
and the:line_bot
applications are started automatically.$ iex -S mix Erlang/OTP 22 [erts-10.5] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [hipe] Interactive Elixir (1.9.2) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> Application.started_applications [ {:advent, 'advent', '0.1.0'}, {:line_bot, 'A package for creating chatbots with the Line messenger', '0.1.0'}, ...
-
So we can handle the incoming webhook request, define a Plug router in
lib/advent/router.ex
:defmodule Advent.Router do use Plug.Router plug :match plug :dispatch forward "/bot", to: LineBot.Webhook, callback: Advent end
-
And set our application to start it in
lib/advent/application.ex
:children = [ Plug.Cowboy.child_spec(scheme: :http, plug: Advent.Router, options: [port: 4000]) ]
-
Now start the application again with
iex -S mix
, and see if it responds to an example request:curl http://localhost:4000/bot
The output should be something like:
19:31:18.051 [warn] Skipping signature validation because :skip_validation is enabled. 19:31:18.051 [debug] Webhook Request:
Now we have a server that is running and receiving webhook events. Let's connect it to the internet.
-
I like to use ngrok to give us a publicly accessible HTTPS proxy to our local environment. Install it and start it on port
4000
:$ ngrok http 4000 ngrok by @inconshreveable (Ctrl+C to quit) Session Status online Account Adam Millerchip (Plan: Free) Version 2.3.35 Region United States (us) Web Interface http://127.0.0.1:4040 Forwarding http://xxxxxxxx.ngrok.io -> http://localhost:4000 Forwarding https://xxxxxxxx.ngrok.io -> http://localhost:4000
-
Take the URL generated by ngrok, and add
/bot
:https://xxxxxxxx.ngrok.io/bot
, then set it as your bot's Webhook URL in the Line Developer Console. -
Now let's start to implement our bot code. Replace
lib/advent.ex
with this:defmodule Advent do use LineBot end
-
Now we should be able to check that our bot responds to events. In Line, open a chat window to your bot, and send it a message. You should see it arrive in the iex console. For example, if I send "Hello, bot!":
19:34:48.510 [debug] Webhook Request: {"events":[{"type":"message","replyToken":"xxxxxxxxx","source":{"userId":"U123456789abcdefghijllmnopqrstuvw","type":"user"},"timestamp":1575801287666,"message":{"type":"text","id":"0","text":"Hello, Bot!"}}],"destination":"Uyyyyyyyyyy"}
-
Let's modify our bot to reply to the message. We'll just echo the message:
defmodule Advent do use LineBot def handle_message(%{"type" => "text", "text" => message}, _info, reply_token) do reply = %LineBot.Message.Text{text: message} LineBot.send_reply(reply_token, reply) end end
If all goes well, now our bot will repeat whatever is said to it.
-
You can also send messages directly from iex. You'll need to know your your personal Line
userId
for this bot. You can find it from an earlier Webhook Request log. Let's send ourselves the message "Hello from iex!":iex> me = "U123456789abcdefghijllmnopqrstuvw" "U123456789abcdefghijllmnopqrstuvw" iex> message = %LineBot.Message.Text{text: "Hello from iex!"} %LineBot.Message.Text{quickReply: nil, text: "Hello from iex!", type: :text} iex> LineBot.send_push(me, message) 19:55:02.919 [debug] API Response: %HTTPoison.Response{status_code: 200, request_url: "https://api.line.me/v2/bot/message/reply", ...} {:ok, %{}}
Hopefully the message should arrive in your Line account.
-
Sometimes it's convenient to send HTTP messages directly to our application for testing, but now that we have configured the Line server to send us messages, let's turn on signature validation to make sure our bot only responds to messages signed by Line, and refuses all others. In
config/config.exs
:import Config config :line_bot, client_id: "YOUR_CLIENT_ID", client_secret: "YOUR_CLIENT_SECRET", skip_validation: false
And that's it! We made a simple line bot that can handle requests from the line server and send reply messages. We also demonstrated how to send messages directly from the command line while developing.
You can see all the API commands you can use, and all the type of events that can be handled in the documentation for LineBot
.
There is also a fully functional sample app available. Why not copy the sample code from line_bot_sample.ex
to lib/advent.ex
and see if you can get it running in your bot?