PythonでLinebot作成＆ファイル共有

##はじめに
PythonでDropboxAPIの利用，Linebotにファイル送信機能を追加する方法を紹介したいと思います。
今回は，月ごとのカレンダーの画像をDropbox経由で送ってくれるLinebotを作成します。
<div align="center">
↓こんな感じ
<img width="375" alt="Linebot参照.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617024/6073b6c3-79e3-51ed-e0a1-f0ec5f5df0a4.jpeg"></div>

Linebotの作成そのものに関しては，参考になるサイトが沢山あるので，ここでは省略します。

[PythonでLINEBotはじめました] (https://uepon.hatenadiary.com/entry/2018/06/19/222537)
[FlaskでLINE botを実装し，herokuにdeployするまで]
 (https://qiita.com/suigin/items/0deb9451f45e351acf92)



##何が必要か？
言語:python 3.7.4
ライブラリ（Linebot）:Flask 1.1.1, line-bot-sdk 1.16.0, 
ライブラリ（Dropbox）:dropbox 9.5.0


##ライブラリインスト―ル
下記の３つpipコマンドでインストールします。

```
pip install Flask
pip install line-bot-sdk
pip install dropbox
```
インストールが完了したら実践です。

##実践(Dropbox使用)
まず，DropboxAPIの簡単な使用方法について紹介いたします。
Dropboxの開発者登録，アクセストークンの取得は，[Dropbox API app 開発者登録の手順](https://mushikago.com/i/?p=3874)を参考に各自で行って下さい。

以下では，DropboxAPIを利用した画像のアップロード，共有リンクの作成までを行います。
好きなカレンダーの画像をインターネットからダウンロードして，それらを順に1.jpg, 2.jpg, ...というように名前を変更しておいて下さい。

```
import dropbox
import os

#1.DropboxAPIの利用
DBX_ACCESS_TOKEN = ***********************
dbx = dropbox.Dropbox(DBX_ACCESS_TOKEN)

#2.Dropboxへのファイルアップロードと共有リンクの作成
for month in range(1,13):
  local_path = '%s.jpg' %month
  dbx_path = "/" + local_path
　
  #2-1.ファイルアップロード（解説１）
  f = open(local_path, 'rb')
  dbx.files_upload(f.read(),dbx_path, mode=dropbox.files.WriteMode.overwrite)
  f.close()
  
  #2-2.共有リンク作成（解説２）
  setting = dropbox.sharing.SharedLinkSettings(requested_visibility=dropbox.sharing.RequestedVisibility.public)
  try:       
    link = dbx.sharing_create_shared_link_with_settings(path=dbx_path, settings=setting)
  except:
    print("既に共有されています。")
　else:
    url = link[0].url
    print(url)
```
解説１
files_uploadのmodeは，デフォルトでは，WriteMode.addとなっています。
その場合には，アップロードするファイルと同じ名前のファイルが既に存在する場合，ファイル名(1).拡張子という名前で保存されます。
上書きしたい場合は，modeをoverwirteとして下さい。

解説２
SharedLinkSettingsでは，public, team_only, password（アクセスにパスワードが必要）の3種類の共有方法があります。他には，共有期限，パスワードなどを決定することができます。

より詳しく知りたい方は，[Dropbox for Python]
(https://dropbox-sdk-python.readthedocs.io/en/latest/index.html)をご覧下さい。

##Linebotへの組み込み
上記の手順で作成した共有リンクを使い，メッセージに応じて共有リンクを返すというLinebotを作成します。
下記のコードのほとんどは，[GitHub line-bot-sdk-python examples](https://github.com/line/line-bot-sdk-python/blob/master/examples/flask-echo/app_with_handler.py)に記載されているものと同じです。

```
from flask import Flask, request, abort
import os
import dropbox
from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)

app = Flask(__name__)

DBX_ACCESS_TOKEN = ***********************
dbx = dropbox.Dropbox(DBX_ACCESS_TOKEN)
#----------------------------ここから解説１
values = []
for month in range(1,13):
  local_path = '%s.jpg' %month
  dbx_path = "/" + local_path

  link = dbx.sharing_list_shared_links(path=dbx_path).links
  url = link[0].url
  url = url.replace('www.dropbox','dl.dropboxusercontent').replace('?dl=0','')
  values.append(url)

keys = [1,2,3,4,5,6,7,8,9,10,11,12]
pic_id = dict(zip(keys, values))
#----------------------------ここまで解説１

#環境変数取得
YOUR_CHANNEL_ACCESS_TOKEN = ***********************
YOUR_CHANNEL_SECRET = ***********************

line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(YOUR_CHANNEL_SECRET)

@app.route("/")
def hello_world():
    return "hello world!"

@app.route("/callback", methods=['POST'])
def callback():
    # get X-Line-Signature header value
    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:
        abort(400)

    return 'OK'

#----------------------------ここから解説２
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    words = [12,11,10,9,8,7,6,5,4,3,2,1]
    text = event.message.text
    for word in words:
        msg = 'カレンダー' + str(word)
        if msg in text:
            a = word - 1
            key = keys[a]
            line_bot_api.reply_message(
            event.reply_token,[
            TextSendMessage(text = key　+ '月のカレンダーだよ！'),
            TextSendMessage(text = pic_id[key])
            ])
#----------------------------ここまで解説２

if __name__ == "__main__":
#    app.run()
    port = int(os.getenv("PORT"))
    app.run(host="0.0.0.0", port=port)
```

解説１
カレンダーの月とURLを紐付けした辞書を作成します。
sharing_list_shared_links(path=dbx_path).linksでは，該当パスのリンク以外にも，ファイル情報を含んだ配列が返されます。（リンクは，一番最初に格納されている。）
url.replacedは，共有リンク内の`www.dropbox`を`dl.dropboxusercontent`に変更，`?dl=0`を消去しています。ブラウザ上で，共有リンクを表示するために必要な処理です。


解説２
Linebotがメッセージに応じて画像リンクを送信するようにif文で条件分岐させています。（ホームページ上部の画像参照）
wordsに，1月から順に[1,2,...12]というように格納してしまうと，「カレンダー12月」というメッセージを送った際に，「カレンダー１」が先に読み込まれてしまい1月のカレンダーがLinebotから帰ってきます。そのため，wordsには，[12,11,...1]というように格納しています。

より詳しく知りたい方は，[Dropbox for Python]
(https://dropbox-sdk-python.readthedocs.io/en/latest/index.html)，[line-bot-sdk-python]
(https://line-bot-sdk-python.readthedocs.io/en/stable/index.html)をご覧下さい。


##参考

**Linebot**
[PythonでLINEBotはじめました] (https://uepon.hatenadiary.com/entry/2018/06/19/222537)
[FlaskでLINE botを実装し，herokuにdeployするまで]
 (https://qiita.com/suigin/items/0deb9451f45e351acf92)

**Dropbox** 
[Dropbox API app 開発者登録の手順](https://mushikago.com/i/?p=3874)
[DropboxのAPIを利用して、ファイルアップロードと共有リンクの作成と共有リンクの取得を行う]
(https://qiita.com/Fortinbras/items/f77984e5a7c2b3045473)


**公式ドキュメント**
[Dropbox for Python]
(https://dropbox-sdk-python.readthedocs.io/en/latest/index.html)
[line-bot-sdk-python]
(https://line-bot-sdk-python.readthedocs.io/en/stable/index.html)