【Pyhon】Line-bot-sdkによるRich Menuやメッセージ応答の実装備忘録

### LINE Botの始め方
LINE Botの始め方は以下の記事を参照ください。

https://qiita.com/grapefruit_0514/items/f7c64a51b3d85eb620ed

## RichMenuの追加
以下は、RichMenuを作成して特定の画像ファイル(png or jpeg)をメニュー画面にする。RichMenuの追加はBotサーバーではなく、LocalのPCから下記を実行することで追加される。RichMenuは作成して画像を正しくuploadされれば、deploy無しでもすぐに反映される。

```python
from linebot.models import (
    RichMenu, RichMenuArea, RichMenuBounds, RichMenuSize,
    MessageEvent, TextMessage, TextSendMessage,
    SourceUser, SourceGroup, SourceRoom,
    TemplateSendMessage, ConfirmTemplate, MessageTemplateAction,
    ButtonsTemplate, ImageCarouselTemplate, ImageCarouselColumn, URITemplateAction,
    PostbackTemplateAction, DatetimePickerTemplateAction,
    CarouselTemplate, CarouselColumn, PostbackEvent,
    StickerMessage, StickerSendMessage, LocationMessage, LocationSendMessage, ImageSendMessage, VideoSendMessage,
    ImageMessage, VideoMessage, AudioMessage, FileMessage,
    UnfollowEvent, FollowEvent, JoinEvent, LeaveEvent, BeaconEvent,
    CameraAction, CameraRollAction, URIAction
)

from linebot.models.actions import PostbackAction

YOUR_CHANNEL_ACCESS_TOKEN = アクセストークン
YOUR_CHANNEL_SECRET = シークレットキー
line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(YOUR_CHANNEL_SECRET)

# json item/RichMenuのサイズや位置、押すとどのようなactionをするか決める。
rich_menu_to_create = RichMenu(
    size=RichMenuSize(width=2500, height=843),
    selected=False,
    name="Main menu",
    chat_bar_text="Tap here",
    areas=[RichMenuArea(
        bounds=RichMenuBounds(x=0, y=0, width=2500, height=843),
        action=URIAction(label='Main Menu', uri='https://line.me'))]
)

# 上記のjson形式の定義を用いてRichMenuを作成する
rich_menu_id = line_bot_api.create_rich_menu(rich_menu=rich_menu_to_create)

# RichMenuで表示する画像をLineのサーバーにuplaodする
path = 'sample.png'
with open(path, 'rb') as f:
    line_bot_api.set_rich_menu_image(rich_menu_id, "image/png", f)

# 上記のRichMenuをデフォルトに設定する
line_bot_api.set_default_rich_menu(rich_menu_id)

# 今までuploadしたRichMenuのリストを取る
rich_menu_list = line_bot_api.get_rich_menu_list()

# 作成済RichMenuを削除する
#line_bot_api.delete_rich_menu(rich_menu_id)
```

x,y座標の指定はLine Bot designerというアプリを使えば簡単に確認できる。

https://developers.line.biz/ja/services/bot-designer/

以下の箇所を修正することで```size=RichMenuSize(width=2500, height=843),```で指定した範囲のどの箇所でどの機能を割り当てるか決めることができる。以下は指定範囲を6分割にしてURLを開く機能とメッセージを送る機能を追加した。


```python:python
        areas=[RichMenuArea(
            bounds=RichMenuBounds(x=8, y=0, width=810, height=835),
            action=URIAction(label='label1', uri='https://line.me')),
            RichMenuArea(
            bounds=RichMenuBounds(x=848, y=0, width=810, height=835),
            action=MessageAction(label='label2',text='label2')),
            RichMenuArea(
            bounds=RichMenuBounds(x=1690, y=0, width=810, height=835),
            action=MessageAction(label='label3',text='label3')),
            RichMenuArea(
            bounds=RichMenuBounds(x=0, y=850, width=810, height=835),
            action=MessageAction(label='label4',text='label4')),
            RichMenuArea(
            bounds=RichMenuBounds(x=840, y=850, width=810, height=835),
            action=MessageAction(label='label5',text='label5')),        
            RichMenuArea(
            bounds=RichMenuBounds(x=1690, y=850, width=810, height=835),
            action=URIAction(label='label6', uri='https://www.google.com/maps/search/%E7%9A%87%E5%B1%85/@35.6861593,139.7491146,16z/data=!3m1!4b1'))
            ]
```


### 特定のメッセージに応答して、メッセージと画像を返す方法
相手が送ってきたものがメッセージか画像か動画か絵文字なのかを判断するのは、```@handler.add(MessageEvent, message=TextMessage)```の```TextMessage```で判断する。この場合、送られてきたメッセージがテキストメッセージの場合は以下を実行する。

画像を返送するには、ImageSendMessageを使って、以下のように使う。

```python:example
(ImageSendMessage(original_content_url="https://hoge/hoge.png",
                  preview_image_url="https://hoge/hoge.png"))
```

```python:python

@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    send_message = event.message.text
    display_name = 'None'
    # 送ってきたユーザーのユーザーidや表示名を取得する。
    if isinstance(event.source, SourceUser):
        profile = line_bot_api.get_profile(event.source.user_id)
        user_id = event.source.user_id
        display_name = profile.display_name
    else: print("user profile can't not use")
    if send_message == "画像を見せて" and isinstance(event.source, SourceUser):
        profile = line_bot_api.get_profile(event.source.user_id)
        tmpname = profile.display_name
    # テキストと画像を送る。
        line_bot_api.reply_message(
            event.reply_token,
            ((TextSendMessage(text="Thank you %s! Here is seat plan on reception."%tmpname)),
                (ImageSendMessage(original_content_url="https://mybucket.s3.ap-northeast-1.amazonaws.com/table1.PNG",
                               preview_image_url="https://mybucket.s3.ap-northeast-1.amazonaws.com/table1.PNG"))))
    else:
        line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text='登録していないメッセージです。'))
```

### 送られてきた画像を保存する方法
送られてきた画像をバイナリデータとして、Heroku使用時はHerokuサーバーに一時保存して、Amazon S3へ送る。


```python:python
@handler.add(MessageEvent, message=ImageMessage)
def handle_image_message(event):
    display_name = 'None'
    if isinstance(event.source, SourceUser):
        profile = line_bot_api.get_profile(event.source.user_id)
        user_id = event.source.user_id
        display_name = profile.display_name
    else: print("user profile can't not use")

    # 送られてきた画像の情報を取得
    message_content = line_bot_api.get_message_content(event.message.id)

    # 送られてきた画像を1024byte分割でバイナリデータとして取得
    img_data = line_bot_api.get_message_content(event.message.id).iter_content()

    # 送られてきた画像を保存する(Heroku使用時はHeroku サーバーの一時保存場所に保存される)
    src_img_path = "./image/sample.png"
    with open(src_img_path, "wb") as f:
        for chunk in message_content.iter_content():
            f.write(chunk)

    # 一時保存した画像をAmazon S3のデポジトリに保存する
    client = boto3.client(
        's3',
        aws_access_key_id=AWS_ACCESS_KEY_ID,
        aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
        region_name=AWS_DEFAULT_REGION
    )
    Bucket = 'mybucketname'
    Key = '%s_%s.png'%(display_name,event.message.id)
    try:
        client.upload_file(src_img_path, Bucket, Key)
        line_bot_api.reply_message(event.reply_token,TextSendMessage(text='Correctly uploaded!!!'))
    except: line_bot_api.reply_message(event.reply_token,TextSendMessage(text='Failure uploaded!!!'))
```

### 送られてきた動画を保存する方法
送られてきた動画をバイナリデータとして、Heroku使用時はHerokuサーバーに一時保存して、Amazon S3へ送る。

```python:python
@handler.add(MessageEvent, message=VideoMessage)
def handle_image_message(event):
    display_name = 'None'
    if isinstance(event.source, SourceUser):
        profile = line_bot_api.get_profile(event.source.user_id)
        user_id = event.source.user_id
        display_name = profile.display_name
    else: print("user profile can't not use")
    message_content = line_bot_api.get_message_content(event.message.id)
    Video_data = line_bot_api.get_message_content(event.message.id).iter_content()
    src_img_path = "./image/sample.mp4"
    with open(src_img_path, "wb") as f:
        for chunk in message_content.iter_content():
            f.write(chunk)
    client = boto3.client(
        's3',
        aws_access_key_id=AWS_ACCESS_KEY_ID,
        aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
        region_name=AWS_DEFAULT_REGION
    )
    Bucket = 'mybucketname'
    Key = '%s_%s.mp4'%(display_name,event.message.id)
    try:
        client.upload_file(src_img_path, Bucket, Key)
        line_bot_api.reply_message(event.reply_token,TextSendMessage(text='Correctly uploaded!!!'))
    except: line_bot_api.reply_message(event.reply_token,TextSendMessage(text='Failure uploaded!!!'))     
```

### LINE公式アカウントを登録時にメッセージを送る方法
```FollowEvent```を使って、以下のように記述する。

```python:python
@handler.add(FollowEvent)
def handle_follow(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text='Hello'))
```

### requirements.txtに追加したmoduleを記述し忘れないで
新しいpython moduleをHelokuで使う場合、requirements.txtに追加したmoduleを記述しないと動かないことがある。


### まとめ
今回はLINE Botの諸々の機能の追加方法を紹介した。