Let's make a LINE Bot with Ruby + Sinatra --Part 1

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 --You can record the book and refer to it later.

I would like to make a LINE Bot called "Honmemo!"

0. What to make in this article

In this article, we will create a LINE Bot that returns parrots. The flow of the program is as follows. (** Service ** is the program created this time) スクリーンショット 2020-07-12 3.23.08.png

1. Prepare the project

First, let's create a template for the project. If you already have a template or want to incorporate it into your 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


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

After completing the description, install it with the bundle command.

Terminal


$ bundle

Next, we will 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 run it and test its operation.

Terminal


$ ruby app.rb -o 0.0.0.0

Enter the command to start the program. Sinatra's default port number is 4567, so let's go to http: // localhost: 4567 /. If "Hello world!" Is displayed, Sinatra's environment construction is complete! (The port numbers in the screenshots are different because you are biting Docker, so don't worry too much> <)

スクリーンショット 2020-07-12 1.53.41.png

2. Register with LINE Developers

2_1. Let's log in

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

When you log in, you should see a screen like the one below. For the time being, you can change the language from the button at the bottom right, so it's easier to use Japanese.

スクリーンショット 2020-07-12 1.56.50.png

2_2. Providers and channels?

Whenever you develop a LINE Bot, the words "provider" and "channel" come up. These can be roughly explained

--Provider: Developer account --Channel: Bot account

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

2_3. Let's make 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 provider name you like.

スクリーンショット 2020-07-12 2.03.05.png

The provider name you enter here will be ** published ** as the author of the bot. You should write down your real name. Press the create button to create the provider.

2_4. Let's make a channel

Next, let's create a channel that will be a bot account. The channel used by the bot is "Messaging API", so click the Messaging API.

スクリーンショット 2020-07-12 2.05.33.png

The setting screen for the newly created channel will appear. Let's fill in the necessary contents.

item Contents
Channel type Messaging API
Provider What I made earlier
Channel icon You don't have to set it now
Channel name Bot name
Channel description Bot description
Large industry Bot genre
Small industry Detailed genre of bot
mail address The email address where you will be contacted about the bot. It's a good idea to use an email address that you can contact.
Privacy policy URL You don't have to set it now
Terms of Service URL You don't have to set it now

After agreeing to the required terms, press the create button to create the channel.

3. Write the keys you need for your bot into an .env file

Now that you have the channels you need for your bot, let's write the keys and secrets to the .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=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
LINE_CHANNEL_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Channel ID and channel secret are "Channel basic settings" The channel token is listed in the "Messaging API Settings" under the name "Channel Access Token (Long Term)". If it is not displayed, press the publish button to publish it.

Write Dotenv.load 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 # <-Postscript

get '/' do
  "Hello world!"
end

4. Let's accept webhooks

4_1 What is a webhook?

Webhook is a mechanism to have the content of the event POSTed to the URL set in advance when the event occurs in the service of the other party (LINE in this article).

For example, if you set the URL / callback of your service to the LINE channel, you can have / callback POST the content of the message when a message arrives on 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 only be accessed from your own PC, so even if a message comes with localhost: 4567 / callback set, localhost: 4567 / callback will not be called.

Because of this specification, you basically need to deploy every time you develop a LINE Bot. If you open the port, you can save the trouble of deploying each time, but there is a security risk, so we will not introduce it here.

4_2 Receive and reply to Webhook

It's basically the same as the README on GitHub, but the image processing is omitted and the code is simple. https://github.com/line/line-bot-sdk-ruby

app.rb


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

# ======Postscript 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

# ======Postscript up to 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 to enable 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 like this because one client is enough for one service. ||=By using [email protected] the client is emptyLine::Bot::Client.newAnd pass it [email protected] the client is already in, pass it. 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 bit long, so I'll explain it separately. body = request.body.read just assigns the sent data to the body variable.

After the signature, we check whether the data sent is really from the LINE server. The data sent from the LINE server always contains something called "HTTP_X_LINE_SIGNATURE", and you can check whether it is the data sent from the LINE server by looking at the contents. Checking if it is a LINE server is implemented in line-bot-api and can be used through the client created earlier. The verification process is executed in the part called client.validate_signature (body, signature).

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

  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 sent data is converted into a form that is easy to handle with ruby. The result of the conversion is an array of events, as you can see from the name events.

events.each do |event|Is processing 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. Non-message events include "Add Friend" and "Unblock".

ʻIf event.type === Line :: Bot :: Event :: MessageType :: Text` confirms that the message type is text. Non-text message types include images, videos, and stamps.

In other words, the code from the top 4 lines is used to "analyze the transmitted data and narrow down only the text message".


Next, let's look at the code inside the if statement.

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

The top four lines assemble the message to send to the LINE server, and the last one line sends the reply. event ['replyToken'] is the reply token included in the event.


At the end, I wrote " OK ", but it follows the rule of LINE Bot API that it is necessary to return the correct response if the process succeeds normally. It's okay to return anything.

4_4 Let's deploy

Now that the code is complete, let's run it! Unfortunately, as I explained earlier, 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 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

After deploying, let's open the application. If "Hello wolrd!" Is displayed, the deployment is complete!

4_5 Let's set up a webhook

Go to the LINE Developers site and go to the channel settings screen. Open Messaging API Settings and click Edit Webhook URL. A box for entering the URL will appear, so enter URL + / callback you just deployed and press the update button.

For example, the deployed URL https://xxxxxxx-yyyyyy-zzzzz.herokuapp.com If so https://xxxxxxx-yyyyyy-zzzzz.herokuapp.com/callback` It will be.

After that, let's check the use of Webhook.

`![Screenshot 2020-07-12 4.14.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672451/d25d1f73-bcee-6424-c5f3 -0b65709b2414.png)

You can check the operation of the server by pressing the verification button. If it is displayed as successful, there is no problem.


Also, Bot messages cannot be sent at this time because auto-answer messages are enabled. So disable auto attendant.

スクリーンショット 2020-07-12 4.17.19.png

Click the Edit Response Message button in the Messaging API settings. Then, a page called response settings will be displayed, so you can see the detailed settings below.

--Turn response message ** off ** --Webhook ** on **

Set.

スクリーンショット 2020-07-12 4.18.33.png

Now you are ready to use the webhook.

5. Let's try

Well, everything is ready! Add your bot to your friends and send a message! There is a QR code in "Messaging API settings", so let's read it with LINE. You should be able to make friends with the bot you made.

If you can make friends, send a message. If you get the same message you sent, you're successful! Thank you for your support!

IMG_1505A6F48E6C-1.jpeg

6. Summary

In this article, I made a bot that returns a parrot of the sent message. It's very simple, but it contains a lot of important code that is essential for making bots, so it's a good idea to familiarize yourself with it!

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 parrots ← Imakoko
  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 --Let's record the searched books (planned)

Recommended Posts

Let's make a LINE Bot with Ruby + Sinatra --Part 1
line bot
I made a LINE bot with Rails + heroku
Easy to make LINE BOT with Java Servlet
Let's make a LINE Bot with Ruby + Sinatra --Part 1
Make a typing game with ruby
I made a LINE bot with Rails + heroku
Easy to make LINE BOT with Java Servlet
A note when the heroku command becomes unavailable
I made a LINE bot with Rails + heroku
Let's make a LINE Bot with Ruby + Sinatra --Part 2
Let's make a LINE Bot with Ruby + Sinatra --Part 1
Easy to make LINE BOT with Java Servlet
How to make a Discord bot (Java)
Let's make draw poker with ruby-Implementation 2 (role)-
Let's make an error screen with Rails
Let's make a LINE Bot with Ruby + Sinatra --Part 1
Let's make draw poker with ruby ~ Preparation ~
Let's scrape with Java! !!
Let's make draw poker with ruby ~ Preparation ~
Let's make draw poker with ruby-Implementation 4 (Deck)-
Let's make draw poker with ruby-Implementation 3 (player)-
Let's make draw poker with ruby-Implementation 2 (role)-
Let's make a LINE Bot with Ruby + Sinatra --Part 2
Let's make a LINE Bot with Ruby + Sinatra --Part 1
Make a typing game with ruby
Let's make an error screen with Rails
Let's make a search function with Rails (ransack)
[Java basics] Let's make a triangle with a for statement
Let's go with Watson Assistant (formerly Conversation) ② Make a hotel reservation chatbot
If you want to make a zip file with Ruby, it's rubyzip.
[LINE BOT] I made a ramen BOT with Java (Maven) + Heroku + Spring Boot (1)
Let's make draw poker with ruby-Implementation 2 (role)-
Let's make an error screen with Rails
line bot
Make System.out a Mock with Spock Test Framework
I made a portfolio with Ruby On Rails