Skip to content

Instantly share code, notes, and snippets.

@adamu
Last active June 10, 2023 18:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save adamu/a37bd88962cbd072b4505a98458edaff to your computer and use it in GitHub Desktop.
Save adamu/a37bd88962cbd072b4505a98458edaff to your computer and use it in GitHub Desktop.
How to make a Line Chatbot with Elixir

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. 🧪🤖🧪

日本語版はこちらです。

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

  2. Create a new Elixir app with a supervision tree:

    mix new advent --sup

  3. cd advent

  4. 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.

  5. 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
    
  6. Add line_bot and related dependencies to mix.exs:

    defp deps do
      [
        {:line_bot, "~> 0.1.0"},
        {:plug_cowboy, "~> 2.0"}
      ]
    end
  7. Fetch the line_bot package with mix deps.get.

  8. 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 and client_secret are the Channel ID and Channel Secret are taken from the Line Developer Console.

  9. 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'},
    ...
    
  10. 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
  11. 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])
    ]
  12. 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.

  13. 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
    
  14. 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.

  15. Now let's start to implement our bot code. Replace lib/advent.ex with this:

    defmodule Advent do
      use LineBot
    end
  16. 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"}

  17. 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.

    image
  18. 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.

    image
  19. 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?

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment