LINE Bot を Bot Framework Composer でローコード開発する : その 4 応答で文字以外を返す

[前回](https://qiita.com/kenakamu/items/22b452a0fb545535addb) は自然言語処理の追加や高度なダイアログの使い方を見ていきました。今回は文字列以外の方法でユーザーに応答する方法を見ていきます。

# Bot Connector と LINE Messaging API

Bot Framework Composer はボットチャンネル登録に含まれる Bot Connector の機能で、LINE をはじめ Facebook メッセンジャーや Microsoft Teams、Skype や Slack など複数のチャネルに接続できます。

当然チャネルごとにメッセージや画像表示などの仕様は異なりますが、Bot Connector がその違いを吸収すべく、Bot Framework Composer で作ったメッセージを自動的に変換してくれています。この記事では以下の方法で画像を返します。

- 自動変換の機能で画像を返す
- LINE Messaging API 独自の方式返す

# 画像の用意

LINE クライアントからアクセスできる外部 URL に画像があれば良いため、GitHub や Azure Blob ストレージなどに配置できます。既に Azure ストレージアカウントがあるため、今回はこちらを使います。

1\. [Azure ポータル](https://portal.azure.com) をアクセスして「ストレージアカウント」を開く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/63aa550b-3ba1-524b-0729-be6b627f7824.png)

2\. コンテナーを選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/ded27eba-b918-51fe-d2fd-0da0f714e153.png)

3\.「＋コンテナー」をクリック。名前を付けて「パブリック アクセス レベル」で「BLOB」を選択。これで画像のみ匿名でアクセスできるようになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/be2a5d5c-665c-3ed5-fbe1-84f137462634.png)

4\. 作成したコンテナーを選択し、画像をアップロード。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/52a4d064-f80f-31f6-af87-340e95b5da19.png)

5\. アップロードした画像を選択して、アドレスを取得。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/1bd71be4-e511-3fd3-1a48-d07416d4c31d.png)

6\. ブラウザなどで画像にアクセスできることを確認。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/08d0da03-ad8d-ac8a-8298-b0a3d996ecf3.png"/>

# 自動変換で画像を返す

Bot Framework Composer でよく使うリッチな応答に、ヒーローカードがあります。ヒーローカードとは以下のように、画像と説明、複数のボタンを備えたリッチなカードです。
![](https://docs.microsoft.com/ja-jp/composer/media/cards/hero-card-explained.png)

LINE Messaging API でもこれと似た機能としてボタンテンプレートがあります。こちらも画像があり、タイトルや説明、そしてボタンがあります。
<img width="350" src="https://developers.line.biz/assets/img/buttons.5b4297f2.png"/>

Bot Framework は画像だけを送るカードがないため、今回はヒーローカードを送り、自動的にボタンテンプレートに変換させます。


## ダイアログとトリガーの追加

Language Generation には [Structured response template](https://github.com/microsoft/BotBuilder-Samples/blob/master/experimental/language-generation/docs/structured-response-template.md) という機能があり、単純な文字列ではなく画像カードやヒーローカードなどリッチなコンテンツを返す際に使える機能があります。今回はこちらの機能を使って画像を返します。

1\.「New Dialog」より「GetCatImageDialog」を追加。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/990b2ba0-8b1e-57d0-2318-02a587aa08a3.png)

2\. 追加したダイアログの BeginDialog を選択し「Send a response」を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/054d41e7-b55f-475b-07e6-534ce7f8c57c.png)

3\.「Language Generation」に以下の応答を追加。

```text
[Herocard   
    title = '猫'
    images = https://myfirstlinebottest.blob.core.windows.net/images/cat1.jpg
]
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/7215a984-ab74-62cd-0b89-b41721884b4b.png)

4\. メインダイアログより以下のようにトリガーを追加。例文をいくつか追加。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/cbbb4af6-51e9-16b9-3da2-73e58612885f.png)

5\. 閾値を設定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/53b7fe1e-5f6e-8c9b-a7bf-a05f56aad555.png)

6\. フローの中で、ダイアログの新規作成アクションを選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/b8bb5bc8-3887-676b-3bf6-bdf788c338a3.png)

7\. GetCatImageDialog を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/b6ecc486-b6cf-112a-8a89-2ce294506787.png)

8\. ボットを再起動。LUIS に新しいインテントを追加するため少し時間がかかる。

## テスト

LINE デスクトップまたはモバイルクライアントから「猫」と送って画像が送られてくることを確認してください。
<img width="350" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/3fabfd03-1a89-3964-0173-d45cb36f9176.png"/>

# LINE Messaging API 独自の方式返す

次に LINE Messaging API 独自方式で返す方法を見ていきます。Bot Framework はチャンネル固有のデータをやり取りできるよう、**ChannelData** プロパティを提供します。

## Messaging API の方式

まず Messaging API でどのように画像を送れるか確認します。

[API リファレンス: 画像メッセージ](https://developers.line.biz/ja/reference/messaging-api/#image-message) によると、以下形式の JSON が必要です。画像の指定は https で JPEG のみをサポートしますが、今回は要件を満たしているためこのまま実装してきます。

```json
{
    "type": "image",
    "originalContentUrl": "https://example.com/original.jpg",
    "previewImageUrl": "https://example.com/preview.jpg"
}
```

1\. GetCatImageDialog の BeginDialog トリガーより応答メッセージを以下のように変更。

```text
[Activity
    Text='猫の画像'
    ChannelData=${GetCatUrl()}
]
```

2\. この時点では ```GetCatUrl``` という関数はないためエラーが出ている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/fbb7f347-004c-9bed-3d9c-a52f4a8d9ef7.png)

3\. 関数を追加するために、メニューより「Bot Responses」を選択し、GetCatImageDialog を選択。「Edit Mode」をクリックして編集を有効にする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/c556c04c-1a42-8b26-c3c3-cd4283c5b326.png)

4\. GetCatUrl 関数を追加。

- # に続けて関数名を指定
- json(): Common Expresson で提供される関数で文字列を JSON オブジェクトにする

```text
# GetCatUrl
- ${json(GetImageMessage())}
```

5\. GetCatUrl 関数が追加されたが、GetImageMessage 関数がないとエラーになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/7d835195-d0d7-b146-3cad-68e6f1317209.png)

6\. GetImageMessage 関数を追加。エラーが無くなることを確認。

- ``` で囲うことで複数行のテキストを返せる
- LINE に対して文字列を改行して送る場合、\n\n で改行ができるとコメントをいただきました。

```text
# GetImageMessage
- ```{
    "type": "image",
    "originalContentUrl": "https://myfirstlinebottest.blob.core.windows.net/images/cat1.jpg",
    "previewImageUrl": "https://myfirstlinebottest.blob.core.windows.net/images/cat1.jpg"
}```
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/988ab117-ab27-e132-fd4a-5e7fc4b2a0ce.png)

## テスト

LINE クライアントからテスト。先ほど少し違うフォーマットで表示れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/d3ff2f39-3d54-e30d-ff5c-f01166c2c808.png)

## 関数を分けた理由

今回 3 つの関数で応答を作成しましたが、以下のコードでも同じ動作をしますが、json 関数に改行した文字を渡せないことや、機能を勉強するためにあえて 3 つに分けています。

```text
[Activity
    Text='猫の画像'
    ChannelData=${json('{"type": "image","originalContentUrl":"https://myfirstlinebottest.blob.core.windows.net/images/cat1.jpg","previewImageUrl":"https://myfirstlinebottest.blob.core.windows.net/images/cat1.jpg"}')}
]
```
# スタンプの返信

次に代表的な LINE 固有の機能としてスタンプを返します。ボットから返せるスタンプは決まっています。
[ボットから送れるスタンプリスト](https://developers.line.biz/media/messaging-api/sticker_list.pdf)
また以下のフォーマットでデータを返します。

```json
{
  "type": "sticker",
  "packageId": "1",
  "stickerId": "1"
}
```

今回はスタンプが送られてきたらスタンプを返すようにしていきます。

## スタンプを返すダイアログの作成

1\.「New Dialog」より「StickerDialog」を追加。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/81c8fe2b-404a-c941-3805-74a2f344385f.png)

2\. BeginDialog トリガーより応答を返すアクションを追加。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/c2fc1187-34fa-4edd-4635-f8e875152ad7.png)

3\. Language Generation に以下を設定。今回は一度にすべてを定義。

```text
[Activity
    Text='スタンプ'
    ChannelData=${json('{"type": "sticker","packageId":"11539","stickerId":"52114133"}')}
]
```
## ユーザー入力情報の確認

ユーザーからメッセージが送られてきた場合、```turn.activity``` 変数にデータが入ります。Messaging API リファレンスにはどのようなデータが入ってくるかの情報がありますが、実際に見た方が分かりやすいため、以下の手順でステッカーを送ってきた場合のデータを見てみます。

1\. メインダイアログの「Unknown intent」の応答メッセージを変更。

```text
- '${turn.activity}'
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/dc7ed666-d959-3da5-f7a8-e94264f0434b.png)


2\. ボットを再起動してから、LINE クライアントから任意のスタンプを送信。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/195be498-281f-5da6-0093-02fd89352d12.png"/>

3\. JSON を Visual Studio Code などでフォーマットして確認。ChannelData の message としてスタンプ情報が渡されていることを確認
<img width="550" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/5ae6d223-1a56-33cb-4d34-2300ee477582.png"/>

## トリガーの作成

現在 LUIS を使った自然言語解析をしているため、スタンプが送られてくるとユーザー入力がなく、上記のように「unknown intent」に振り分けられます。そこでダイアログのトリガーは「unknown intent」に追加します。

1\. メインダイアログの「Unknown intent」の応答メッセージの前に if/else ブランチを追加。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/708149ec-3e5d-73ef-dfbd-4a30a50d0691.png)

2\. 条件に ```turn.activity.channeldata.message.type=='sticker'``` を追加してメッセージがスタンプかを判定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/d14f1b3c-0052-3c1f-37a1-eefe4b4be2dc.png)

3\. True ブランチに新規ダイアログの開始アクションを追加し、StickerDialog を開始。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/8eede469-9ff5-a48e-fd3e-3302d0fed856.png)

4\. False ブランチに応答送信アクションを追加し、```- '${turn.activity.text}'```　を指定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/5ec37d81-7a28-0045-8289-568028835fb6.png)

5\. 最後に一番下にある、もともとあったアクションを削除。

## テスト

ボットを再起動して、LINE クライアントからスタンプを送信し、スタンプが返ってくるかテストします。
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214116/d885e5d6-730a-1055-060a-017c1ce141e4.png"/>


# まとめ

今回は応答の方法として、文字列以外に画像やスタンプを返す方法を見ていきました。またユーザーからスタンプが送られてきた場合の判定方法も確認しました。

基本的には同じ手段で、位置情報やポストバックなど、LINE の他のメッセージを確認したり、様々な応答を返すことができます。色々試してみてください。

次回はユーザーから画像が送られてきた時の処理を見ていきます。

[次の記事へ](https://qiita.com/kenakamu/items/201038ab1ae43d35cce1)