This is my entry for the [fukuoka.ex Elixir/Phoenix Advent Calendar 2019](https://qiita.com/advent-calendar/2019/fukuokaex) day 13, and it's a guide for how to write a Line bot using Elixir. 🧪🤖🧪 [日本語版はこちらです。](https://gist.github.com/adamu/6155478ca7a4791877e871907e618f0a) 1. First you need to create a **Messaging API Channel** on the [Line Developers Console](https://developers.line.biz). You will also need to add your bot as a friend, and know the generated *Channel ID* and *Channel Secret*. 1. Create a new Elixir app with a supervision tree: `mix new advent --sup` 1. `cd advent` 1. 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. 1. 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 ``` 1. Add `line_bot` and related dependencies to `mix.exs`: ```elixir defp deps do [ {:line_bot, "~> 0.1.0"}, {:plug_cowboy, "~> 2.0"} ] end ``` 1. Fetch the `line_bot` package with `mix deps.get`. 1. Define a config file in `config/config.exs`: ```elixir 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` and `client_secret` are the Channel ID and Channel Secret are taken from the Line Developer Console. 1. 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'}, ... ``` 1. So we can handle the incoming webhook request, define a Plug router in `lib/advent/router.ex`: ```elixir defmodule Advent.Router do use Plug.Router plug :match plug :dispatch forward "/bot", to: LineBot.Webhook, callback: Advent end ``` 1. And set our application to start it in `lib/advent/application.ex`: ```elixir children = [ Plug.Cowboy.child_spec(scheme: :http, plug: Advent.Router, options: [port: 4000]) ] ``` 1. 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. 1. I like to use [ngrok](https://ngrok.com/) 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 ``` 1. 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. 1. Now let's start to implement our bot code. Replace `lib/advent.ex` with this: ```elixir defmodule Advent do use LineBot end ``` 1. 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"}` 1. Let's modify our bot to reply to the message. We'll just echo the message: ```elixir 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. image 1. 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!": ```elixir 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. image 1. 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`](https://hexdocs.pm/line_bot/LineBot.html). There is also a fully functional sample app available. Why not copy the sample code from [`line_bot_sample.ex`](https://github.com/adamu/line_bot/blob/master/sample/lib/line_bot_sample.ex) to `lib/advent.ex` and see if you can get it running in your bot? image