みらいテックラボ

音声・画像認識や機械学習など, 週末プログラマである管理人が興味のある技術の紹介や実際にトライしてみた様子などメモしていく.

「Kingyo AI Navi」のアプリ化を考える (4)

2019.3.16にUrban Data Challenge 2018のファイナルが行われ, CODE for YAMATOKORIYAMAが金魚愛(AI)育成プロジェクトとして取り組んでいる「Kingyo AI Navi」が, アイデア部門の金賞[1]を受賞した.

f:id:moonlight-aska:20190324195724p:plain:w400

CODE for YAMATOKORIYAMAでは, 今年度このアイデアをアプリ化したいと考えており, LINEのMessaging API + サービスでの実現に向けて検討を進めている.
今回は, LINE Botから送信するメッセージについて, どのようなメッセージ表示が可能なのか少し調べてみた.
技術的なことは, Messaging APIリファレンス[2]に記載されているので, 実際にどのように表示されるかなどを中心に紹介していく.


関連記事:
「Kingyo AI Navi」のアプリ化を考える (1)
「Kingyo AI Navi」のアプリ化を考える (2)
「Kingyo AI Navi」のアプリ化を考える (3)
・「Kingyo AI Navi」のアプリ化を考える (4)
「Kingyo AI Navi」のアプリ化を考える (5)
「Kingyo AI Navi」のアプリ化を考える (6)


1. メッセージオブジェクト
メッセージオブジェクトは, 送信するメッセージの内容を表すJSONオブジェクトである.

・テキストメッセージ
・画像メッセージ
・動画メッセージ
・音声メッセージ
・位置情報メッセージ
・スタンプメッセージ
・イメージマップメッセージ
・テンプレートメッセージ
Flex Message

1.1 応答メッセージ送信[3]
応答メッセージ表示を試すにあたり, LINE Messaging APIからイベントを受け, 応答を返す部分のコードを以下に載せておく.
簡単に説明すると,
(1) LINE Messaging APIがWebhookすると, callback()関数が呼び出される. ここでは, MessageEventがTextMessageかImageMessageかを判断し, 処理を振り分けるようになっている.
(2) MessageEventがTextMessageの場合handle_text_message()関数が呼び出されるので, 今回はここでメッセージオブジェクトをいろいろと変更して, どのように表示されるか試している.
[コード]

import os
import ssl
from argparse import ArgumentParser
import datetime
import json
import settings

# Import from Flask Module                                                               
from flask import Flask, request, abort

# Import from Line Bot SDK                                                               
from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, ImageMessage, TextMessage, TextSendMessage
)

context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
context.load_cert_chain('/etc/letsencrypt/live/{Domain Name}/fullchain.pem',
                        '/etc/letsencrypt/live/{Domain Name}/privkey.pem')

# Instance of Flask                                                                      
app = Flask(__name__)

# Message App ID                                                                         
line_bot_api = LineBotApi(os.environ['LINE_CHANNEL_ACCESS_TOKEN'])
handler = WebhookHandler(os.environ['LINE_CHANNEL_SECRET'])

@app.route("/callback", methods=['POST'])
def callback():
    print('callback : ', datetime.datetime.today())
    signature = request.headers['X-Line-Signature']

    # get request body as text                                                           
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    # handle webhook body                                                                
    try:
        handler.handle(body, signature)

    except InvalidSignatureError as e:
        print('InvalidSignatureError : ', e)
        abort(400)

    return 'OK'

@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):                                              
        messages = TextSendMessage(text='こんにちは. Kingyo AI Naviで~す.')
        reply_message(event, messages)

def reply_message(event, messages):
    line_bot_api.reply_message(
        event.reply_token,
        messages = messages,
    )

if __name__ == '__main__':
    arg_parser = ArgumentParser(
	usage = 'Usage: python ' + __file__ + ' [--help]'
    )
    arg_parser.add_argument('-d', '--debug', default=False, help='debug')
    options = arg_parser.parse_args()

    app.run(host=os.environ['HOST_ADDRESS'], port=os.environ['HTTPS_PORT'], ssl_context=context, threaded=True, debug=options.debug)


2. メッセージ表示例
「Kingyo AI Navi」で利用しそうなメッセージオブジェクトについて, どのような表示が行われるか順に紹介していく.

2.1 テキストメッセージ
シンプルにテキストメッセージを返す. 最大文字数:2000.
LINEが独自に定義した絵文字なども, 利用可能.
[コード]

from linebot.models import (
    TextSendMessage
)

@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):                                              
    messages = TextSendMessage(text='こんにちは. '+chr(0x100078)+'\nKingyo AI Naviで〜す. '+chr(0x10002D))
    reply_message(event, messages)

[表示]
f:id:moonlight-aska:20190506104437p:plain:w400

2.2 画像メッセージ
画像のURL指定により, 画像メッセージを返す. 画像URLは”HTTPS", かつJPEGのみ.

オリジナル画像 プレビュー画像
最大画像サイズ 1024x1024 240x240
最大ファイルサイズ 1MB 1MB

[コード]

from linebot.models import (
    ImageSendMessage
)

@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):                                              
    messages = ImageSendMessage(
        original_content_url='https://storage.googleapis.com/{XXXX}/img/category/chakin.jpg',
        preview_image_url='https://storage.googleapis.com/{XXXX}/img/preview/goldfish.jpg'
    )
    reply_message(event, messages)

[表示]
f:id:moonlight-aska:20190506182350p:plain:w400

プレビュー画像が表示され, タッチするとオリジナル画像が表示される.
ただ, 一度画像を表示すると画像URLを変更しても反映されるまでに時間がかかった. (LINE側でキャッシュが効いているのか???)

2.3 動画メッセージ
動画のURL指定により, 動画メッセージを返す. 動画URLは”HTTPS", かつmp4のみ.

オリジナル動画 プレビュー画像
最大長 1分  
最大画像サイズ 240x240
最大ファイルサイズ 10MB 1MB

[コード]

from linebot.models import (
    VideoSendMessage
)

@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):                                              
    messages = ImageSendMessage(
        original_content_url='https://storage.googleapis.com/{XXXX}/video/video.mp4',
        preview_image_url='https://storage.googleapis.com/{XXXX}/img/preview/goldfish.jpg'
    )
    reply_message(event, messages)

[表示]
f:id:moonlight-aska:20190506182433p:plain:w400

2.4 位置情報メッセージ
位置情報付きメッセージを返す.
タイトル, 住所とともに, 緯度・経度の地図も表示される.
[コード]

from linebot.models import (
    LocationSendMessage
)

@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):                                                                
    messages = LocationSendMessage(
        title = u'みやげ処こちくや',
        address = u'大和郡山市紺屋町',
        latitude = 34.647799,
        longitude = 135.784753
    )
    reply_message(event, messages)

[表示]
f:id:moonlight-aska:20190506184445p:plain:w400

2.5 スタンプメッセージ
スタンプリストを参照して, パッケージID, スタンプIDを指定することで, スタンプメッセージを返す.
スタンプリストに載っているもの以外も表示できるようだ. ただ, 何が表示されるかは試してみないとわからない.
[コード]

from linebot.models import (
    StickerSendMessage
)

@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):                                                                
    messages = StickerSendMessage(                                                    
        package_id = '1',
        sticker_id = '1'
    )
    reply_message(event, messages)

[表示]
f:id:moonlight-aska:20190506190120p:plain:w400

2.6 テンプレートメッセージ - ボタンテンプレート
画像, タイトル, テキストに加えて, 複数のアクションボタンが含まれたテンプレートによるメッセージを返す.
[コード]

from linebot.models import (
    TemplateSendMessage, ButtonsTemplate, URIAction
)

@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):
    template = ButtonsTemplate(
        title = u'金魚スポット',
        text = u'金魚スポット情報への近道',
        actions = [
            # 大和郡山市観光協会公式ウェブサイト                             
            URIAction(
                uri = 'http://www.yk-kankou.jp/spot.html',
                label = u'観光案内(市観光協会)'
            ),
            # LINEトラベルjp                                                 
            URIAction(
                uri = 'https://www.travel.co.jp/guide/article/37658/',
                label = u'金魚スポットを歩く'
	    )
        ]
    )
    messages = TemplateSendMessage(
        alt_text = 'Buttons Template',
        template = template
    )
    reply_message(event, messages)

[表示]
f:id:moonlight-aska:20190507222633p:plain:w400
アクション部分をタッチすると, URLのページが開く.

2.7 テンプレートメッセージ - 確認テンプレート
2つのアクションボタンを表示するテンプレートによるメッセージを返す.
[コード]

from linebot.models import (
    TemplateSendMessage, ConfirmTemplate, MessageAction
)

@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):                                       
    template = ConfirmTemplate(
        text = '金魚好きですか?',
        actions = [
            MessageAction(
                label = 'はい',
                text = 'はい'
            ),
            MessageAction(
                label = 'いいえ',
                text = 'いいえ'
            )
        ]
    )
    messages = TemplateSendMessage(
        alt_text = 'Confirm Template',
        template = template
    )
    reply_message(event, messages)

[表示]
f:id:moonlight-aska:20190507224242p:plain:w400

2.8 テンプレートメッセージ - カルーセルテンプレート
複数のカラムを表示するテンプレートによるメッセージを返す.
[コード]

from linebot.models import (
    TemplateSendMessage, CarouselTemplate, CarouselColumn, URIAction
)

@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):                                       
    template = CarouselTemplate(
        columns = [
            CarouselColumn(
                thumbnail_image_url = 'https://storage.googleapis.com/{XXXX}/img/category/azumanishiki.jpg',
                title = u'東錦',
                text = u'(あずまにしき)',
                actions = [
                    URIAction(
                        uri = 'https://www.kingyoen.com/',
                        label = u'関連サイト'
                    )
                ]
            ),
            CarouselColumn(
                thumbnail_image_url = 'https://storage.googleapis.com/{XXXX}/img/category/chobi.jpg',
                title = u'蝶尾',
                text = u'(ちょうび)',
                actions = [
                    URIAction(
                        uri = 'https://www.kingyoen.com/',
                        label = u'関連サイト'
                    )
                ]
            )
        ]
    )
    messages = TemplateSendMessage(
        alt_text = 'Carousel Template',
        template = template
    )
    reply_message(event, messages)

[表示]
f:id:moonlight-aska:20190508215946p:plain:w400

2.9 テンプレートメッセージ - 画像カルーセルテンプレートTemplateSendMessage ー ImageCarouselTemplate
複数の画像を表示するテンプレートによるメッセージを返す.
[コード]

from linebot.models import (
    TemplateSendMessage, ImageCarouselTemplate, ImageCarouselColumn, PostbackAction
)

@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):                                      
    template = ImageCarouselTemplate(
	columns = [
            ImageCarouselColumn(
                image_url = 'https://storage.googleapis.com/{XXXX}/img/category/azumanishiki.jpg',
	        action = PostbackAction(
                    label = u'東錦',
                    text = u'あずまにしき',
                    data = 'action=buy&itemid=1'
                )
            ),
            ImageCarouselColumn(
                image_url = 'https://storage.googleapis.com/{XXXX}/img/category/chobi.jpg',
                action = PostbackAction(
                    label = u'蝶尾',
                    text = u'ちょうび',
                    data = 'action=buy&itemid=2'
                )
            )
        ]
    )
    messages = TemplateSendMessage(
        alt_text = 'Image Carousel Template',
        template = template
    )
    reply_message(event, messages)

[表示]
f:id:moonlight-aska:20190508222422p:plain:w400

今回は, LINE Messaging APIの応答メッセージの表示方法について調査してみた.
これらの表示方法をうまく使って, 金魚や大和郡山に関する情報に簡単にアクセスできるような見せ方を考えていきたい.

----
参照URL:
[1] UDC2018審査結果 | アーバンデータチャレンジ
[2] Messaging APIリファレンス | LINE Developers
[3] SDK of the LINE Messaging API for Python




LINE BOTを作ろう!  Messaging APIを使ったチャットボットの基礎と利用例

LINE BOTを作ろう! Messaging APIを使ったチャットボットの基礎と利用例

  • 作者:立花 翔
  • 発売日: 2017/05/12
  • メディア: 単行本(ソフトカバー)