[Ruby] Make a LINE Bot with Ruby + Sinatra-Part 1

9 minute read

Hello. In this series, I used Ruby and Sinatra multiple times

  • Send the ISBN code on the back of the book to search and display the image of the book
  • Keep a record of the book so you can refer to it later

I would like to make a LINE Bot called “Honmemo!”

0. What to make in this article

In this article, I will make a LINE Bot that returns a parrot. The flow of the program is as follows. (** Service ** is the program created this time) ![Screenshot 2020-07-12 3.23.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672451/263d98da-a8f5-85b5-9da2-(5c2e30c6b6ba.png)

1. Prepare the project

First, let’s make a template for the project. If you already have a template, or if you want to incorporate it into existing code, proceed to the next chapter.

terminal


$ bundle init

Generate a Gemfile with the bundle init command. And let’s add the following gem.

Gemfile


# Omitted
gem "sinatra"
gem "sinatra-contrib"
gem "dotenv"
gem "line-bot-api"

When the description is complete, let’s install with the bundle command.

terminal


$ bundle

Next, create the core file. This time, the file name is app.rb.

app.rb


require'bundler/setup'
Bundler.require
require'sinatra/reloader' if development?

get'/' do
    "Hello world!"
end

Now, let’s execute it and test the operation.

terminal


$ ruby app.rb -o 0.0.0.0

Enter the command to start the program. The default port number for Sinatra is 4567, so let’s access http://localhost:4567/. If “Hello world!” is displayed, the Sinatra environment construction is complete! (Don’t worry too much because the port number in the screenshot is different because you are biting Docker ><)

![Screenshot 2020-07-12 1.53.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672451/3226d9fb-fef6-54df-dab4-(2d6f32b67201.png)

2. Register for LINE Developers

2_1. Log in

https://developers.line.biz/en/ Go to and log in. If you usually use LINE, you can log in with a QR code.

After logging in, you should see the screen below. For the time being, you can change the language from the button on the bottom right, so it’s easier to set it to Japanese.

![Screenshot 2020-07-12 1.56.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672451/5d618efb-7df5-8cf4-4721-(8511bcc0831f.png)

2_2. Providers and channels?

Whenever we develop LINE Bot, the words “provider” and “channel” come up. These are roughly explained

  • Provider: Developer account
  • Channel: Bot account

It means that · · · Providers are created in units of companies, individuals, groups of developers, etc., and channels belong to one provider.

2_3. Create a provider

So let’s create a provider first. When you click “Create new provider”, a screen for entering the provider name will appear, so enter the name of your favorite provider. ‥

![Screenshot 2020-07-12 2.03.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672451/d1481d54-8311-d3ae-854e-(23d852ce24af.png)

The provider name entered here will be published as the author of the bot. You should refrain from your real name. Click the create button to create the provider.

2_4. Create a channel

Next, let’s create a channel that will be a Bot account. The channel used by Bot is “Messaging API”, so click on the Messaging API.

Screenshot 2020-07-12 2.05.33.png

The setting screen for the new channel will appear. Let’s fill in the necessary contents.

Item Content
Channel Type Messaging API
Provider What I made earlier
Channel icon OK without setting now
Channel name Bot name
Channel description Bot description
Large Industry Bot Genre
Small Business Bot Details Genre
Email Address Email address for contacting bots. It is a good idea to use an email address that you can contact.
Privacy Policy URL You do not have to set it now.
Terms of Service URL You do not have to set it now OK

After accepting the required rules, click the create button to create the channel.

3. Write the key required for the bot in the .env file

Now that we have the channels we need for our bot, let’s write the key and secret to our .env file. You can write it directly in the program, but it’s not so good considering security.

.env


LINE_CHANNEL_ID=xxxxxxxxxx
LINE_CHANNEL_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
LINE_CHANNEL_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

For the channel ID and channel secret, see “Channel Basic Settings”. The channel token is described as “Channel access token (long-term)” in “Messaging API settings”. If it is not displayed, press the issue button to issue it.

Dotenv.load is written under require so that the variables described in .env can be used in the program.

app.rb


require'bundler/setup'
Bundler.require
require'sinatra/reloader' if development?
Dotenv.load # <- added

get'/' do
  "Hello world!"
end

4. Let’s accept Webhook

4_1 What is Webhook?

A webhook is a mechanism that, when an event occurs on the other party’s service (LINE in this article), the content of the event is POSTed to the URL set in advance.

For example, if you set the URL /callback of the service you made to the LINE channel, you can have the contents of the message POSTed to the channel when a message comes to the channel.

However, since it needs to be a URL that can be accessed from the LINE server, it will not work properly when the service is not actually published. For example, localhost:4567 under development is a URL that can be accessed only from my PC, so even if a message comes with localhost:4567/callback set, localhost:4567/callback will not be called.

Because of this specification, when developing a LINE Bot, you basically have to deploy it every time. Opening the port saves you the trouble of deploying each time, but it is not introduced here because it poses a security risk.

4_2 Receive Webhook and reply

Basically it is the same as the README of GitHub, but I have omitted the image processing and made it simple code. https://github.com/line/line-bot-sdk-ruby

app.rb


require'bundler/setup'
Bundler.require
require'sinatra/reloader' if development?
Dotenv.load

# ====== Addendum from here ======

def client
  @client ||= Line::Bot::Client.new {|config|
    config.channel_id = ENV["LINE_CHANNEL_ID"]
    config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
    config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
  }
end

post'/callback' do
  body = request.body.read

  signature = request.env['HTTP_X_LINE_SIGNATURE']
  unless client.validate_signature(body, signature)
    error 400 do'Bad Request' end
  end

  events = client.parse_events_from(body)
  events.each do |event|
    if event.is_a?(Line::Bot::Event::Message)
      if event.type === Line::Bot::Event::MessageType::Text
        message = {
          type:'text',
          text: event.message['text']
        }
        client.reply_message(event['replyToken'], message)
      end
    end
  end

  "OK"
end# ====== Added here ======

get'/' do
  "Hello wolrd!"
end

I will explain the code below.

4_3 Code explanation

def client
  @client ||= Line::Bot::Client.new {|config|
    config.channel_id = ENV["LINE_CHANNEL_ID"]
    config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
    config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
  }
end

This is the code that allows you to use the “client” to operate the LINE Bot (this is a function of line-bot-api). You can create a client with Line::Bot::Client.new, but it is implemented because one client is enough for one service. By using ||= operator, when @client is empty, Line::Bot::Client.new and pass it. If @client is already included, pass it in. The processing is realized.

post'/callback' do
  body = request.body.read

  signature = request.env['HTTP_X_LINE_SIGNATURE']
  unless client.validate_signature(body, signature)
    error 400 do'Bad Request' end
  end

The post'/callback' do block is a little long, so we will explain it separately. body = request.body.read just assigns the sent data to the body variable.

After signature, we check whether the data sent is really from the LINE server. The data sent from the LINE server always contains “HTTP_X_LINE_SIGNATURE”, and if you look inside it, you can check whether it is the data sent from the LINE server. Confirmation of whether it is a LINE server is implemented in line-bot-api, and it can be used through the client created earlier. Confirmation process is executed in the part called client.validate_signature(body, signature).

This is an important code that checks if a malicious person is impersonating the LINE server and sending messages.

  events = client.parse_events_from(body)
  events.each do |event|
    if event.is_a?(Line::Bot::Event::Message)
      if event.type === Line::Bot::Event::MessageType::Text
        message = {
          type:'text',
          text: event.message['text']
        }
        client.reply_message(event['replyToken'], message)
      end
    end
  end

  "OK"
end

In events = client.parse_events_from(body), the data sent is converted to a ruby-friendly form. The result of conversion is an array of events, as you can see from the name of events.

events.each do |event| processes multiple events one by one. This is because multiple events may be sent at the same time.

if event.is_a?(Line::Bot::Event::Message) checks if the event type is Message. Events other than messages include “Add friend” and “Unblock”.

if event.type === Line::Bot::Event::MessageType::Text confirms that the message type is text. The types of messages other than text include images, videos, and stamps.

In other words, the code from the top 4 lines is “Parse the sent data and narrow down only the text message”.


Next, let’s see the code in the if statement

message = {
  type:'text',
  text: event.message['text']
}
client.reply_message(event['replyToken'], message)

The first 4 lines compose the message to send to the LINE server, and the last 1 line sends the reply. event[‘replyToken’] is the reply token contained in the event.


At the end, it says "OK", but it follows the rules of the LINE Bot API that a correct response must be returned if the processing is successful. You can return anything.

4_4 Let’s deploy

Now that the code is complete, let’s run it! As I said earlier, unfortunately it does not work when executed locally. So, this time I will deploy to Heroku.

I will omit the details of deploying to heroku, but let’s create only the Procfile.

Procfile


web: bundle exec ruby app.rb -o 0.0.0.0 -p $PORT

terminal


$ git init
$ git add -A
$ git commit -m "first commit"
$ heroku create
$ git push heroku master

Let’s open the application after deploying. If “Hello wolrd!” is displayed, deployment is complete!

4_5 Let’s set up a webhook

Go to the LINE Developers site and move to the channel setting screen. Open “Messaging API Settings” and click on “Edit Webhook URL”. A frame will appear for you to enter the URL, so let’s enter URL deployed earlier +/callback and press the update button.

For example, the deployed URL is https://xxxxxxx-yyyyyy-zzzzz.herokuapp.com Then https://xxxxxxx-yyyyyy-zzzzz.herokuapp.com/callback` Will be.

After that, check the use of Webhook.

`Screenshot 2020-07-12 4.14.21.png

You can check the operation of the server by pressing the verify button. If it shows success, there is no problem.


Also, at the moment, the auto-reply message is enabled, so the bot’s message cannot be sent. So, disable the auto answer.

Screenshot 2020-07-12 4.17.19.png

Click the edit button of “Response message” in the Messaging API settings. Then, the page called response setting is displayed, so the detailed setting below

  • Turn off response message OFF
  • Webhook on

Set.

![Screenshot 2020-07-12 4.18.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672451/5e9833e5-b061-ccf2-611b-(2d1cf7503106.png)

Now you are ready to use the webhook.

5. Let’s try

Now, everything is ready! Add a bot to your friends and send them a message! There is a QR code in “Messaging API settings”, so let’s read this with LINE. You should be able to make friends with your bot.

Send a message if you can be friends. If you get the same message you sent back, you’re successful! Thank you!

IMG_1505A6F48E6C-1.jpeg

6. Summary

In this article, I made a bot that returns the sent message as a parrot. It’s very simple, but it contains a lot of important code that is indispensable for making a bot, so I recommend you to understand it well!

The next article is here

List of serialized articles

  1. Let’s make a LINE Bot with Ruby + Sinatra-Part 1-Let’s make a bot that returns parrot ← Imakoco
  2. Let’s make a LINE Bot with Ruby + Sinatra-Part 2-Search for books and return information
  3. Let’s make a LINE Bot with Ruby + Sinatra-Part 3-Record the searched books (planned)