【Ruby】LINEbotの作り方をまとめてみた

# はじめに
RubyでLINEbotを制作した際、「RubyでLINEbotを作る」という記事やサイトが少なく非常に困りました。そこで、これからRubyでLINEbotを動かしたいという人に少しでも参考になればと思い、この記事を書くことにしました。
その中で「もっと簡単に書けるよ」というのがあるかもしれません。
そのときは教えていただけると嬉しいです。

# 前提
LINEbotのサンプルコード(Ruby)を動かせることが前提です。
以前の記事で詳しく書いているので[こちら](https://qiita.com/Yuzu_Ginger/items/18edc58bd039fc19ad53)をご覧ください。

https://qiita.com/Yuzu_Ginger/items/18edc58bd039fc19ad53

# 目次
[応答メッセージについて](#応答メッセージについて)
[サンプルコード](#サンプルコード)

[メッセージタイプの種類](#メッセージタイプの種類)
　　[1. テキストメッセージ](#1-テキストメッセージ)
　　[2. スタンプメッセージ](#2-スタンプメッセージ)
　　[3. 画像メッセージ](#3-画像メッセージ)
　　[4. 位置情報メッセージ](#4-位置情報メッセージ)
　　[5. テンプレートメッセージ](#5-テンプレートメッセージ)
　　　　[ボタンテンプレート](#ボタンテンプレート)
　　　　[確認テンプレート](#確認テンプレート)
　　　　[カルーセルテンプレート](#カルーセルテンプレート)

[クイックリプライ](#クイックリプライ)

[アクション](#アクション)
　　[1. ポストバックアクション](#1-ポストバックアクション)
　　[2. メッセージアクション](#2-メッセージアクション)
　　[3. URIアクション](#3-uriアクション)
　　[4. 日付選択アクション](#4-日付選択アクション)
　　[5. 位置情報アクション](#5-位置情報アクション)


# 応答メッセージについて
プッシュメッセージの場合、一定数を超えると有料になるのですべて応答メッセージで返すことを前提とします。応答メッセージとは、ユーザから何か送られてきたら返す、というメッセージのことです。
応答メッセージは、以下のように書くことでトーク画面に表示することができます。
```ruby:reply
client.reply_message(event['replyToken'], message)
```
このうち、返す内容によって変えなければならないのは、最後の引数の`message`の部分です。`message`の変数の中身を変えることで任意のテキストを送ることができます。

また、[メッセージタイプの種類](#メッセージタイプの種類)で紹介しているタイプのうち、[テンプレートメッセージ](#5-テンプレートメッセージ)はそれなりに長いテンプレートを用意する必要があります(テンプレートメッセージとはボタンの選択肢などが扱えるメッセージです)。そのため、ここではメソッドにしています。そのメソッドも、この`message`の部分に記述します。そうすることでテンプレートメッセージをトーク画面に表示することができます。
たとえば、`button(x, y)`というテンプレートメッセージのメソッドがあったとします。その場合は、`client.reply_message(event['replyToken'], button(x, y))`と記述します。

さらに、2つ目の引数(`message`の部分)を配列にすることで、吹き出しを複数出して応答メッセージを送ることができます。上限は3つまでです。`client.reply_message(event['replyToken'], [m1, m2, m3])`
<br><br>
自作メソッド内で`client.reply_message(event['replyToken'], message)`を記述する場合は、メソッドを呼び出すときに引数として`event`を渡してください。なお、1つのメッセージに対する応答メッセージは`client.reply_message`1個までです(応答したとみなされる)。この記述以降に何か書いていても実行されません。
```ruby
def reply(event, x)
    message = {
        "type": "text",
        "text": x       # Helloと表示される
    }
    client.reply_message(event['replyToken'], message)
end

:
 # 省略
:
x = "Hello"
reply(event, x)  # メソッド呼び出し
```

# サンプルコード
以下のサンプルコードのうち、`require`の部分とコメントアウトしている部分のみを編集してください(他の場所はいじらないでください)。自作メソッドなどは、別のファイルなどにまとめておくとわかりやすいです。
```ruby:sample.rb
require 'bundler/setup'
require 'sinatra'
require 'line/bot'

get '/' do
  'hello world!'
end

def client
    @client ||= Line::Bot::Client.new { |config|
      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)
      halt 400, {'Content-Type' => 'text/plain'}, 'Bad Request'
    end
  
    events = client.parse_events_from(body)
  
    events.each do |event|
        case event
        when Line::Bot::Event::Message
            case event.type
            when Line::Bot::Event::MessageType::Text
                # 1. ユーザからメッセージが送られた時の処理
            end
        when Line::Bot::Event::Postback
            # 2. ユーザからのメッセージ以外が送られた時の処理
        end
    end
  
    "OK"
end
```
`# 1. ユーザからメッセージが送られた時の処理`の部分には、文字通りユーザからメッセージが送られてきた時の処理を書きます。
というのも、LINEbotでは、ユーザがメッセージを送ったときもボタンを選択したときも、いちいち`post '/callback' do~end`が実行されます。そこで、ユーザからメッセージが送られてきた時と、ボタンなどが押された時の処理を場合分けします。
`# 2. ユーザからのメッセージ以外が送られた時の処理`にはボタンを選択したときなどの処理を書きます。

<strong><font color="red">※この際、インテンドに注意してください。間違っていると動きません。エラーではないので原因特定が難しいです。</font></strong>
<br>
ユーザから送られてきたテキストの内容によって処理を変えたい場合は、`1`の中で分岐を書きます。
`if event.message['text'] == "xx"`

同じように、ユーザからのメッセージ以外が送られてきた時に場合分けしたいときは、`2`の中で分岐を追記します。たとえば、ボタンの選択肢によって処理を変えるなどです。
`if event['postback']['data'].split(",").first == "xx"`
[テンプレートメッセージ](#5-テンプレートメッセージ)で詳しく説明しますが、ボタンの選択によって処理を変えたい場合、データを配列にしておくとやりやすいです。

[日付選択アクション](#4-日付選択アクション)のみ以下のように書いてください。
`if event['postback']['params']['date'] == "20xx-xx-xx"`

# メッセージタイプの種類
LINEbotでは以下のメッセージタイプがあります。
- [テキストメッセージ](#1-テキストメッセージ)
- [スタンプメッセージ](#2-スタンプメッセージ)
- [画像メッセージ](#3-画像メッセージ)
- 動画メッセージ
- 音声メッセージ
- [位置情報メッセージ](#4-位置情報メッセージ)
- イメージマップメッセージ
- [テンプレートメッセージ](#5-テンプレートメッセージ)
- Flex Message

[メッセージタイプ-MessagingAPIリファレンス](https://developers.line.biz/ja/docs/messaging-api/message-types/)

## 1. テキストメッセージ
[サンプルコード](サンプルコード)の`when Line::Bot::Event::MessageType::Text`の下に追記してください。
以下のように書くと、ユーザが「こんにちは、世界」を含む文字列を送ったとき、「Hello, World!」と返します。`if event.message['text'] == "こんにちは、世界"`のように書くと、送られてきた文字列が「こんにちは、世界」に一致した場合の分岐になります。
```ruby:text.rb
if event.message['text'] =~ /こんにちは、世界/
    message = {
        "type": 'text',
        "text": "Hello, World!"
    }
client.reply_message(event['replyToken'], message)
```
`,`を忘れないようにしてください。絵文字も含めることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2549439/9c54d7fa-465d-991b-eaba-57bbef51eaf8.png)
[テキストメッセージ-MessagingAPIリファレンス](https://developers.line.biz/ja/reference/messaging-api/#text-message)

## 2. スタンプメッセージ
スタンプを送るには、送りたいスタンプのパッケージIDとスタンプIDをメッセージオブジェクトに含めます。
以下のように書くと、ユーザが「スタンプ」と送ると、bot はスタンプを返します。
```ruby:stamp.rb
if event.message['text'] == "スタンプ"
    stamp = {
        "type": "sticker",
        "packageId": "446",
        "stickerId": "1988"
    }
client.reply_message(event['replyToken'], stamp)
```
| 項目 | 内容 |
|:-:|:-:|
| `type` | `"sticker"` |
| `packegeId` | スタンプセットのパッケージID |
| `stickerId` | スタンプID |

なお、パッケージIDとスタンプIDは[送信可能なスタンプリスト](https://developers.line.biz/ja/docs/messaging-api/sticker-list/)を参考にしてください。送信できるスタンプの一覧を見ることができます。
公式リファレンスにあるこちらの画像がわかりやすいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2549439/aa69ef3b-9f56-70cf-f079-940d7adf34d4.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2549439/504ebd01-4890-ff3b-fcef-cb38486e60da.png)
[スタンプメッセージ-MessagingAPIリファレンス](https://developers.line.biz/ja/reference/messaging-api/#sticker-message)

## 3. 画像メッセージ
画像を送るには、画像と、その画像よりも小さなプレビュー画像のURLをメッセージオブジェクトに含めます。プレビュー画像がトーク画面に表示され、画像をタップするとフルサイズの画像が表示されます。なお、URLにはHTTPS（TLS 1.2以降）のURLのみ指定できます。
画像のURLとプレビュー画像のURLが同じでも大丈夫です(ただし、プレビュー画像のサイズは1MB以下)。
```ruby:image.rb
if event.message['text'] == "画像"
    image = {
        "type": "image",
        "originalContentUrl": "https://example.com/original.jpg",
        "previewImageUrl": "https://example.com/preview.jpg"
    }
client.reply_message(event['replyToken'], image)
```
| 項目 | 内容 |
|:-:|:-:|
| `type` | `"image"` |
| `originalContentUrl` | 画像のURL(httpsから始まる)<br>PNGまたはJPEG<br>最大10MB |
| `previewImageUrl` | プレビュー画像のURL(httpsから始まる)<br>PNGまたはJPEG<br>最大1MB |

画像URLは必ず`https`から始まる必要があります。そのため、ローカルにある画像をそのまま使うことはできません。一度クラウド上に上げる必要があります。
今回は、Googleドライブを使った方法を紹介します。

まずは、ご自身のGoogleドライブに画像をアップします。「共有」→「リンクを知っている全員」がアクセスできるようにします。生成されたリンクのうち、スラッシュ`/`で囲まれた部分が一番長い文字列のみをコピーします。
`https://drive.google.com/file/d/この部分/view?usp=sharing`
コピーした文字列の前に、`https://drive.google.com/uc?id=`をつけます。そのURLを画像URLとすれば、LINEbotでGoogleドライブにアップした画像を表示できるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2549439/af25fa44-59d1-a637-aa6f-5e7b50396cf3.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2549439/72b5669a-836b-e250-0b93-ad49f6c728e6.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2549439/2755ad03-247d-8b40-5e23-b1648bba1b09.png)
[画像メッセージ-MessagingAPIリファレンス](https://developers.line.biz/ja/reference/messaging-api/#image-message)

## 4. 位置情報メッセージ
位置情報を送るには、タイトルと住所、緯度と経度の座標をメッセージオブジェクトに含めます。
以下のように書くと、ユーザが「スカイツリー」と送ると、東京スカイツリーの位置情報が返ってきます。
```ruby:location.rb
if event.message['text'] == "スカイツリー"
    location = {
        "type": "location",
        "title": "東京スカイツリー",
        "address": "〒131-0045 東京都墨田区押上1丁目1−2",
        "latitude": 35.71017593105232,
        "longitude": 139.81066821137946
    }
client.reply_message(event['replyToken'], location)
```
| 項目 | 内容 |
|:-:|:-:|
| `type` | `location` |
| `title` | 地図の下に表示されるタイトル |
| `address` | 住所 |
| `latitude` | 緯度 |
| `longitude` | 経度 |

緯度と経度は Googleマップで赤いピンを右クリックすると確かめることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2549439/6f4f6e12-2bfd-39ce-1993-245ade108ca9.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2549439/62d1c3cb-5012-6e23-dd5c-69ea5ec0bc0f.png)
[位置情報メッセージ-MessagingAPIリファレンス](https://developers.line.biz/ja/reference/messaging-api/#location-message)

## 5. テンプレートメッセージ
テンプレートメッセージには、事前定義されたレイアウトが用意されています。ユーザーが手動でメッセージを入力する代わりに、1回のタップで特定の[アクション](#アクション)を実行できます。

それぞれのテンプレートはそれなりに長い記述になるので、1つのテンプレートごとにメソッドを作り、それをmain.rb(メインのファイル)とは別ファイルに記述することをおすすめします。別ファイルに記述した場合は、メインファイルに`require_relative "ファイル名(.rbなし)"`を追記してください。

テンプレートメッセージには以下の4つのタイプを使うことができます。
- [ボタン](#ボタンテンプレート)
- [確認](#確認テンプレート)
- [カルーセル](#カルーセル)
- 画像カルーセル

<br>
以下の項目はどのテンプレートにも必須のものです。必ず記述してください。

#### 必須項目

| 項目 | 内容 |
|:-:|:-:|
| `type` | `"template"`  |
| `altText` | 代替テキスト<br>テンプレートメッセージに非対応のデバイスで表示されるほか、ユーザーがメッセージを受信した際に、端末の通知やトークリストでも表示されます。  |
| `template` | [ボタン](#ボタンテンプレート)、[確認](#確認テンプレート)、[カルーセル](#カルーセル)、画像カルーセルのうちいずれか |

[テンプレートメッセージ-MessagingAPIリファレンス](https://developers.line.biz/ja/reference/messaging-api/#template-messages)

### ボタンテンプレート
ボタンテンプレートでは、画像、タイトル、テキストに加えて複数の[アクション](#アクション)ボタンが含まれたメッセージを送信できます。ボタンのほかに、画像、タイトル、テキストの領域全体に対しても、ユーザーがタップすると実行されるアクションを1つ指定できます。
ボタンの数は最大<strong>4つ</strong>までです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2549439/03c7038e-64d0-143b-0477-e26cdf83f761.png)
```ruby:button.rb
def button
  {
    "type": "template",    # 必須
    "altText": "This is a buttons template",   # 必須
    "template": {          # 必須
        "type": "buttons", # 必須
        "thumbnailImageUrl": "https://example.com/bot/images/image.jpg",
        "imageAspectRatio": "rectangle",
        "imageSize": "cover",
        "imageBackgroundColor": "#FFFFFF",
        "title": "Menu",
        "text": "Please select",   # 必須
        "defaultAction": {
            "type": "uri",
            "label": "View detail",
            "uri": "http://example.com/page/123"
        },
        "actions": [      # 必須
            # 1つ目のボタン
            {
              "type": "postback",
              "label": "Buy",
              "data": "action=buy&itemid=123"
            },
            # 2つ目のボタン
            {
              "type": "postback",
              "label": "Add to cart",
              "data": "action=add&itemid=123"
            },
            # 3つ目のボタン
            {
              "type": "uri",
              "label": "View detail",
              "uri": "http://example.com/page/123"
            }
        ]
    }
  }
end
```
他のメッセージタイプ同様、`,`忘れには注意してください。
`"actions"`の中に選択ボタンを記述します。`{}`で1つの選択肢です。これらは[アクション](#アクション)オブジェクトのため、[アクション](#アクション)から選んで記述してください。

テンプレートには、必須の項目と任意の項目があります。

#### 必須項目
| 項目 | 内容 |
|:-:|:-:|
| `type` | `"buttons"` |
| `text` | メッセージテキスト<br>「選んでください」など |
| `actions` | タップされた時のアクション(選択肢)<br>[アクション](#アクション)オブジェクトから選ぶ<br>最大4つまで |

#### 任意項目
| 項目 | 内容 |
|:-:|:-:|
| `thumbnailImageUrl` | 画像のURL(httpsから始まる)<br>PNGまたはJPEG<br>最大横幅サイズ1024px<br>最大10MB<br>推奨ファイルサイズ1MB|
| `imageAspectRatio` | 画像のアスペクト比(以下のいずれか)<br>`rectangle`：1.51:1<br>`square`：1:1<br>デフォルト値は`rectangle`|
| `imageSize` | 画像の表示形式(以下のいずれか)<br>`cover`：画像領域全体に画像を表示。画像領域に収まらない部分は切り詰められる。<br>`contain`：画像領域に画像全体を表示。縦長の画像では左右に、横長の画像では上下に余白が表示される。<br>デフォルト値は`cover` |
| `imageBackgroundColor` | 画像の背景色(RGB)<br>デフォルト値は`#FFFFFF`(白) |
| `title` | ボタンのタイトル |
| `defaultAction` | 画像、タイトル、テキストの領域全体に対して設定できるタップされたときのアクション<br>[アクション](#アクション)オブジェクトから選ぶ |

[ボタンテンプレート-MessagingAPIリファレンス](https://developers.line.biz/ja/reference/messaging-api/#buttons)

### 確認テンプレート
確認テンプレートを使うと、ボタンが2つ含まれたメッセージを送信できます。
```ruby:confirm.rb
def confirm
  {
    "type": "template",    # 必須
    "altText": "this is a confirm template",   # 必須
    "template": {          # 必須
        "type": "confirm",         # 必須
        "text": "Are you sure?",   # 必須
        "actions": [               # 必須
            {
              "type": "message",
              "label": "Yes",
              "text": "yes"
            },
            {
              "type": "message",
              "label": "No",
              "text": "no"
            }
        ]
    }
  }
end
```
| 項目 | 内容 |
|:-:|:-:|
| `type` | `"confirm"` |
| `text` | メッセージテキスト<br>「選んでください」など<br>最大20文字 |
| `actions` | タップされた時のアクション(2つの選択肢)<br>[アクション](#アクション)オブジェクトから選ぶ |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2549439/227c78fb-59a5-42e1-fbf6-44ea0ab86814.png)
[確認テンプレート-MessagingAPIリファレンス](https://developers.line.biz/ja/reference/messaging-api/#confirm)

### カルーセルテンプレート
カルーセルテンプレートは、複数のカラムオブジェクトをユーザーがスクロールして閲覧することができます。各カラムオブジェクトにはボタンのほかに、画像、タイトル、テキストの領域全体に対しても、ユーザーがタップすると実行される[アクション](#アクション)を1つ指定できます。
```ruby:carousel.rb
def carousel
  {
    "type": "template",   # 必須
    "altText": "this is a carousel template",   # 必須
    "template": {         # 必須
        "type": "carousel",  # 必須
        "columns": [      # 必須
            # 1つ目のカラム
            {
              "thumbnailImageUrl": "https://example.com/bot/images/item1.jpg",
              "imageBackgroundColor": "#FFFFFF",
              "title": "this is menu",
              "text": "description",   # 必須
              "defaultAction": {
                  "type": "uri",
                  "label": "View detail",
                  "uri": "http://example.com/page/123"
              },
              "actions": [    # 必須
                  # 1つ目のボタン(カラム1)
                  {
                      "type": "postback",
                      "label": "Buy",
                      "data": "action=buy&itemid=111"
                  },
                  # 2つ目のボタン(カラム1)
                  {
                      "type": "postback",
                      "label": "Add to cart",
                      "data": "action=add&itemid=111"
                  },
                  # 3つ目のボタン(カラム1)
                  {
                      "type": "uri",
                      "label": "View detail",
                      "uri": "http://example.com/page/111"
                  }
              ]
            },
            # 2つ目のカラム
            {
              "thumbnailImageUrl": "https://example.com/bot/images/item2.jpg",
              "imageBackgroundColor": "#000000",
              "title": "this is menu",
              "text": "description",   # 必須
              "defaultAction": {
                  "type": "uri",
                  "label": "View detail",
                  "uri": "http://example.com/page/222"
              },
              "actions": [   # 必須
                  # 1つ目のボタン(カラム2)
                  {
                      "type": "postback",
                      "label": "Buy",
                      "data": "action=buy&itemid=222"
                  },
                  # 2つ目のボタン(カラム2)
                  {
                      "type": "postback",
                      "label": "Add to cart",
                      "data": "action=add&itemid=222"
                  },
                  # 3つ目のボタン(カラム2)
                  {
                      "type": "uri",
                      "label": "View detail",
                      "uri": "http://example.com/page/222"
                  }
              ]
            }
        ],
        "imageAspectRatio": "rectangle",
        "imageSize": "cover"
    }
  }
end
```
カルーセルテンプレートは[ボタンテンプレート](#ボタンテンプレート)の集まりだと思ってください。
1つのボタンテンプレートが1つのカラムに相当します。
1つのカラムには選択ボタン<strong>3つ</strong>までです(ボタンテンプレートと異なる点なので注意してください)。
カラムは<strong>最大10個</strong>までです。

#### 必須項目
| 項目 | 内容 |
|:-:|:-:|
| `type` | `"carousel"` |
| `columns` | カラムオブジェクトの配列<br>最大10個まで |
| `text` | メッセージテキスト<br>「選んでください」など |
| `actions` | タップされた時のアクション(選択肢)<br>[アクション](#アクション)オブジェクトから選ぶ<br>最大3つまで |

#### 任意項目
| 項目 | 内容 |
|:-:|:-:|
| `thumbnailImageUrl` | 画像のURL(httpsから始まる)<br>PNGまたはJPEG<br>最大横幅サイズ1024px<br>最大10MB<br>推奨ファイルサイズ1MB|
| `imageBackgroundColor` | 画像の背景色(RGB)<br>デフォルト値は`#FFFFFF`(白) |
| `title` | ボタンのタイトル |
| `defaultAction` | 画像、タイトル、テキストの領域全体に対して設定できるタップされたときのアクション<br>[アクション](#アクション)オブジェクトから選ぶ |
| `imageAspectRatio` | 画像のアスペクト比(以下のいずれか)<br>`rectangle`：1.51:1<br>`square`：1:1<br>デフォルト値は`rectangle`|
| `imageSize` | 画像の表示形式(以下のいずれか)<br>`cover`：画像領域全体に画像を表示。画像領域に収まらない部分は切り詰められる。<br>`contain`：画像領域に画像全体を表示。縦長の画像では左右に、横長の画像では上下に余白が表示される。<br>デフォルト値は`cover` |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2549439/67ad0b54-4fee-0343-3ac3-5cd139ab1906.png)
[カルーセルテンプレート-MessagingAPIリファレンス](https://developers.line.biz/ja/reference/messaging-api/#carousel)

# クイックリプライ
クイックリプライ機能では、メッセージの他にトーク画面の下部にそれらのボタンが表示されます。ボタンをタップするだけで、簡単に返信できます。[ボタンテンプレート](#ボタンテンプレート)の簡易版と思ってください。
ボタンの数は<strong>最大13個</strong>です。
※ Android版、iOS版のみ表示されます
```ruby:quickReply.rb
def quick_reply
  "type": "text",    # メッセージをつけるために必要
  "text": "横にスクロールして時間を選んでください",   # メッセージをつける
  "quickReply": {    # 必須
    "items": [       # 必須
      # 1つ目のボタン
      {
        "type": "action",  # 必須
        "imageUrl": "https://example.com/sushi.png",
        "action": {        # 必須
          "type": "cameraRoll",
          "label": "Send photo"
        }
      },
      # 2つ目のボタン
      {
        "type": "action",
        "action": {
          "type": "camera",
          "label": "Open camera"
        }
      }
    ]
  }
end
```
| 項目 | 内容 |
|:-:|:-:|
| `quickReply` | クイックリプライ機能を使うのに必須 |
| `items` | クイックリプライボタンの配列 |
| `type` | `"action"` |
| `action` | タップされた時のアクション(選択肢)<br>[アクション](#アクション)オブジェクトから選ぶ(リッチメニューの切り替えアクションは除く)<br>最大13個まで |
| `imageUrl` | ボタンの先頭に表示するアイコンの画像URL(httpsから始まる)<br>PNG<br>アスペクト比 1：1<br>最大データサイズ1MB|

`imageUrl`のみ任意です。
actionプロパティに指定するアクションがカメラアクション、カメラロールアクション、または位置情報アクションで、imageUrlプロパティが未指定の場合、デフォルトのアイコンが表示されます。
他のアクションで、imageUrlプロパティが未指定の場合、アイコンなしの文字だけのボタンになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2549439/0d2e99d9-cc0f-5fc4-5996-48d57daf75b6.png)
[クイックリプライを使う-MessagingAPIリファレンス](https://developers.line.biz/ja/docs/messaging-api/using-quick-reply/#using-quick-reply-introduction)
[クイックリプライの記述方法-MessagingAPIリファレンス](https://developers.line.biz/ja/reference/messaging-api/#quick-reply)

# アクション
ユーザーがメッセージ内のボタンまたは画像をタップしたときに、ボットが実行できるアクションのタイプは以下の8個です。
- [ポストバックアクション](#1-ポストバックアクション)
- [メッセージアクション](#2-メッセージアクション)
- [URIアクション](#3-uriアクション)
- [日付選択アクション](#4-日付選択アクション)
- カメラアクション
- カメラロールアクション
- [位置情報アクション](#5-位置情報アクション)
- リッチメニュー切り替えアクション

### ボタンを連続で使いたい場合
ボタンで、例えばまず好きなくだものを選び、次に嫌いなくだものを選び、最後に選択された好きなくだものと嫌いなくだものをテキストで返したい場合、ポストバックアクションの`data`プロパティを工夫する必要があります。
なぜなら、MessagingAPIでは、ユーザがテキストを送る、ボタンを押すといったすべてのアクションにおいて、1回1回Webhookを通すため、データの引継ぎができないからです。
これは、上の例や予約システムなどを作成する場合、とても厄介です。

そこで、テンプレートメッセージが含まれるのメソッドに引数として前までの`data`プロパティの情報を格納し、`deta`プロパティにもその情報を追加します。

先ほどのくだものの例をプログラムすると以下のようになります。
```ruby:main.rb
post '/callback' do
    body = request.body.read
  
    signature = request.env['HTTP_X_LINE_SIGNATURE']
    unless client.validate_signature(body, signature)
      halt 400, {'Content-Type' => 'text/plain'}, 'Bad Request'
    end
  
    events = client.parse_events_from(body)
  
    events.each do |event|
        case event
        when Line::Bot::Event::Message
            case event.type
            when Line::Bot::Event::MessageType::Text     # ユーザがテキストを送信したときの処理
                if event.message['text'] == "くだもの"
                  client.reply_message(event['replyToken'], like_fruits)
                end
            end
        when Line::Bot::Event::Postback           # ボタンが押されたあとの処理
            ar = event['postback']['data'].split(",")
            if ar.first == "like"
              if ar[2] == "hate"
                message = {
                  type: "text"
                  text: "あなたの好きなくだものは#{ar[1]}で、嫌いなくだものは#{ar[3]}です"
                }
                client.reply_message(event['replyToken'], message)
              else
                client.reply_message(event['replyToken'], hate_fruits(event['postback']['data']))
              end
            end
        end
    end
end
```
```ruby:button.rb
def like_fruits
  {
    "type": "template",    # 必須
    "altText": "This is a buttons template",   # 必須
    "template": {          # 必須
        "type": "buttons", # 必須
        "text": "Please select",   # 必須
        "actions": [      # 必須
            # 1つ目のボタン
            {
              "type": "postback",
              "label": "りんご",
              "data": "like,りんご"
            },
            # 2つ目のボタン
            {
              "type": "postback",
              "label": "みかん",
              "data": "like,みかん"
            },
            # 3つ目のボタン
            {
              "type": "postback",
              "label": "ぶどう",
              "data": "like,ぶどう"
            }
        ]
    }
  }
end


def hate_fruits(data)
  {
    "type": "template",    # 必須
    "altText": "This is a buttons template",   # 必須
    "template": {          # 必須
        "type": "buttons", # 必須
        "text": "Please select",   # 必須
        "actions": [      # 必須
            # 1つ目のボタン
            {
              "type": "postback",
              "label": "りんご",
              "displayText": "りんご",
              "data": "#{data},hate,りんご"
            },
            # 2つ目のボタン
            {
              "type": "postback",
              "label": "みかん",
              "displayText": "みかん",
              "data": "#{data},hate,みかん"
            },
            # 3つ目のボタン
            {
              "type": "postback",
              "label": "ぶどう",
              "displayText": "ぶどう",
              "data": "#{data},hate,ぶどう"
            }
        ]
    }
  }
end
```

このプログラムでは、まずユーザから`"くだもの"`と送られてくると、次に`like_fruits`のボタンテンプレートが返されます。
`like_fruits`のボタンテンプレートでは、「りんご」「みかん」「ぶどう」の3つのボタンが表示されます。

いずれかをユーザがタップすると、次に`main.rb`の`when Line::Bot::Event::Postback`に返ってきます。ここで、`data`プロパティを`","`ごとに区切って配列にします。このときの配列の中身は、`ar = {"like", "選択したくだもの"}`になっています。
`ar[2]`は空なので、次に`v client.reply_message(event['replyToken'], hate_fruits(event['postback']['data']))`が実行され、`hate_fruits`のボタンテンプレートが表示されます。ここで、引数として`ar`(選択したボタンの情報が入った配列)を渡します。

`hate_fruits`のボタンテンプレートの`data`プロパティは、引数として渡された`ar`と、`"hate","選択肢のくだもの"`とします。

そこでボタンがタップされると、同じように`when Line::Bot::Event::Postback`に返り、`","`ごとに区切って配列に入れられます。この時の配列は、`ar = {"like", "好きなくだもの", "hate", "嫌いなくだもの"}`になっています。

`ar[2]`には`"hate"`が入っているので、最後にメッセージテキストとして、「あなたの好きなくだものは`#{ar[1]}`で、嫌いなくだものは`#{ar[3]}`です」と表示されます。好きなくだものと嫌いな食べ物には、それぞれユーザが選択したくだものの名前が入っています。
<br>
このように、`data`プロパティを`","`で区切って配列にすることで、データの引継ぎができます。

[アクションオブジェクト-MessagingAPIリファレンス](https://developers.line.biz/ja/reference/messaging-api/#action-objects)

## 1. ポストバックアクション
このアクションが関連づけられたコントロールがタップされると、dataプロパティに指定された文字列を含むポストバックイベントが、Webhookを介して返されます。
[テンプレートメッセージ](#テンプレートメッセージ)内よび[クイックリプライ](#クイックリプライ)内の`actions`で指定できるアクションの1つです。

タップされたあとは、[サンプルコード](#サンプルコード)の`when Line::Bot::Event::Postback`に返ってきます。
その中でdataプロパティを指定するには`event['postback']['data']`となります。
```ruby:postback.rb
{
  "type": "postback",   # 必須
  "label": "Buy",       # 必須または任意
  "data": "1",          # 必須
  "displayText": "Buy", # 必須または任意
  "inputOption": "openKeyboard",  # 任意
  "fillInText": "---\nName: \nPhone: \nBirthday: \n---"
}  # 任意
```

| 項目 | 内容 |
|:-:|:-:|
| `type` | `"postback"` |
| `label` | アクションのラベル(例:ボタンに表示されるテキスト)<br>画像カルーセル以外の[テンプレートメッセージ](#5-テンプレートメッセージ)、[クイックリプライ](#クイックリプライ)には必須 |
| `data` | 返ってくる文字列<br>`event['postback']['data']`に格納されている |
| `displayText` | アクション実行時にユーザのメッセージとしてトーク画面に表示されるテキスト<br>[クイックリプライ]'#クイックリプライ)では必須 |
| `inputOption` | いずれかの値を指定する<br>・`closeRichMenu`:リッチメニューを閉じる<br>・`openRichMenu`:リッチメニューを開く<br>・`openKeyboard`:キーボードを開く<br>・`openVoise`:ボイスメッセージ入力モードを開く |
| `fillInText` | キーボードを開いた時に入力欄にあらかじめ入力しておく文字列<br>`inputOption`が`openKeyboard`の場合にのみ有効 |

[ポストバックアクション-MessagingAPIリファレンス](https://developers.line.biz/ja/reference/messaging-api/#postback-action)

## 2. メッセージアクション
このアクションが関連づけられたコントロールがタップされると、textプロパティの文字列がユーザーからのメッセージとして送信されます。
[テンプレートメッセージ](#テンプレートメッセージ)内よび[クイックリプライ](#クイックリプライ)内の`actions`で指定できるアクションの1つです。

タップされたあとは、ユーザからメッセージが送られてきたときと同じ処理をします。つまり、`postback`ではなく`when Line::Bot::Event::MessageType::Text`の分岐の処理が行われます。

```ruby:message.rb
{
   "type":"message",   # 必須
   "label":"Yes",      # 必須または任意
   "text":"Yes"        # 必須
}
```
| 項目 | 内容 |
|:-:|:-:|
| `type` | `"message` |
| `label` | アクションのラベル(例:ボタンに表示されるテキスト)<br>画像カルーセル以外の[テンプレートメッセージ](#5-テンプレートメッセージ)、[クイックリプライ](#クイックリプライ)には必須 |
| `text` | アクションの実行時に送信されるテキスト|

[メッセージアクション-MessagingAPIリファレンス](https://developers.line.biz/ja/reference/messaging-api/#message-action)

## 3. URIアクション
このアクションが関連づけられたコントロールがタップされると、LINE内ブラウザで`uri`プロパティの URL が開きます。
[テンプレートメッセージ](#テンプレートメッセージ)内および[クイックリプライ](#クイックリプライ)内の`actions`で指定できるアクションの1つです。

URIアクションはタップされてもMessagingAPIに返ってくるデータはありません。ユーザのLINE画面上で指定の URL のページが開かれるのみです。

```ruby:uri.rb
{
    "type": "uri",   # 必須
    "label": "メニューを見る",   # 必須または任意
    "uri": "https://example.com/menu"  # 必須
}
```
| 項目 | 内容 |
|:-:|:-:|
| `type` | `"uri"` |
| `label` | アクションのラベル(例:ボタンに表示されるテキスト)<br>画像カルーセル以外の[テンプレートメッセージ](#5-テンプレートメッセージ)、[クイックリプライ](#クイックリプライ)には必須 |
| `uri` | アクションの実行時に送開かれるURI |

[URLアクション-MessagingAPIリファレンス](https://developers.line.biz/ja/reference/messaging-api/#uri-action)

## 4. 日付選択アクション
このアクションが関連づけられたコントロールがタップされると、日時選択ダイアログでユーザーが選択した日時を含むポストバックイベントが、Webhookを介して返されます。
日時選択アクションはタイムゾーンの違いに対応していないことに注意してください。

タップされたあとは、[サンプルコード](#サンプルコード)の`when Line::Bot::Event::Postback`に返ってきます。
その中でdataプロパティを指定するには`event['postback']['params']['date']`となります。
```ruby:date.rb
{
   "type":"datetimepicker",  # 必須
   "label":"Select date",    # 必須または任意
   "data":"storeId=12345",   # 必須
   "mode":"datetime",        # 必須
   "initial":"2017-12-25t00:00",  # 任意
   "max":"2018-01-24t23:59",      # 任意
   "min":"2017-12-25t00:00"       # 任意
}
```

| 項目 | 内容 |
|:-:|:-:|
| `type` | `"datetimepicker"` |
| `label` | アクションのラベル(例:ボタンに表示されるテキスト)<br>画像カルーセル以外の[テンプレートメッセージ](#5-テンプレートメッセージ)、[クイックリプライ](#クイックリプライ)には必須 |
| `data` | 返ってくる文字列<br>`event['postback']['data']`に格納されている |
| `mode` | アクションモード<br>`date`:日付を選択<br>`time`:時刻を選択<br>`datetime`:日付と日時を選択|
|`initial`| 日付または時刻の初期値|
| `max` | 選択可能な日付または時刻の最大値 |
| `min` | 選択可能な日付または時刻の最小値 |

[日付選択アクション-MessagingAPIリファレンス](https://developers.line.biz/ja/reference/messaging-api/#datetime-picker-action)

## 5. 位置情報アクション
クイックリプライボタンにのみ設定できるアクションです。
このアクションが関連づけられたボタンがタップされると、LINEの位置情報画面が開きます。
位置情報アクションは、タップされてもMessagingAPIに返ってくるデータはありません。ユーザのLINE画面上で指定の URL のページが開かれるのみです。
```ruby:locate.rb
{
   "type":"location",   # 必須
   "label":"Location"   # 必須
}
```
| 項目 | 内容 |
|:-:|:-:|
| `type` | `"location"` |
| `label` | アクションのラベル<br>(例:ボタンに表示されるテキスト) |

[位置情報アクション](https://developers.line.biz/ja/reference/messaging-api/#location-action)


# さいごに
コードを変更した際は、Herokuへのデプロイを忘れずに行ってください。
```bash
$ git add .
$ git commit -m "write your commit message"
$ git push heroku master
```
もしも、エラーが出てしまったり、トーク画面が動かない場合は、
```bash
$ heroku logs --tail
```
で確かめてみてください。たいていの場合、エラーになっています。`p`メソッドの出力もここで見ることができます。
<br>
以上、RubyでLINEbotを作る際の記述の仕方をまとめてみました。わかりにくいところなどあると思いますが、少しでも読んでいただいた方の参考になれば幸いです。
また、ここでは書ききれなかった機能もまだまだたくさんあります。しかし、基本的にはここで紹介した書き方と大きく変わることはありません。公式のリファレンスを参考にしていただけたらと思います。