【Python】Line APIを使う [第一回 美女Botの作成]

# モチベーション
最近女の子とラインをしていない...
すぐに返信してくれてかつ可愛い子はいないものか。
linebotを活用することで実現できるのではないか？

このようなしょうもない理由でlinebotの作成をしようと思う。

# 環境
・mac Catalina
・Docker version 19.03.13
・Flask 1.1.2
・Python 3.8.3
・docker-compose version 1.27.4
・heroku　7.45.0
・bash

# 今回作成するもの
まずは環境構築として、dockerからherokuにコンテナを移動し起動するまでをする。
LINEBOTはおはようと言ったらおはようと返してくれる美人のbotを作成する。
あくまで非リアだからこんなことしてるわけでないことに注意！

# 流れ
第一回の流れを記す。

**1.LINE Developersの登録**
**2.pythonファイルの作成**
**3.Dockerfileとdocker-compose.ymlの作成**
**4.herokuへのアップロード**

最終的なファイル構成は以下のようになる。

bijobot
├── Dockerfile
├── app.py
├── docker-compose.yml
└── requirements.txt

## 1.LINE Developersの登録
以下よりサイトに飛ぶ。
[LINE Developers](https://developers.line.biz/ja/services/messaging-api/)

![スクリーンショット 2020-11-15 21.56.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656158/b9ed1a96-9f19-307e-1660-4a69f986034b.png)

まずはLoginして、Providersよりcreateを選択する。
すでにdisny通知というのがあるのは気にしない。

Create a new providerと出てくるのでdisny通知のようにつけたい名前をつける。
筆者は"自由画像子"という名前をつけた。

次にThis provider doesn't have any channels yetと出てくるのでCreate a Messaging API channelを選択する。


![スクリーンショット 2020-11-15 21.59.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656158/0cebce3c-e82c-2ff5-0e24-de9cbe3805c2.png)

その後以下のような画面に飛ぶのでそれぞれ適当に記入する。
channel iconにはフリー画像でよく使われており個人的にタイプの子を採用した。

![スクリーンショット 2020-11-15 22.03.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656158/775e19c5-0850-4495-1dde-bcb468fdb813.png)

規約に同意しAdminを確認すると、以下のように新しくチャンネルが追加されているのが確認できる。

![スクリーンショット 2020-11-15 22.06.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656158/eab0d7bd-bdb8-c9ca-2444-c58702198d07.png)

ここで作成したチャンネルに移動し、Basic setting内の

`Channnel secret`

Messaging API内の

`Channel access token`

に関しては必要なので控えておくようにする。

## 2.pythonファイルの作成

[Messaging API SDKs](https://developers.line.biz/en/docs/messaging-api/line-bot-sdk/)
のOfficial SDKsよりflaskを使ってファイルを作成する手順が書かれているのでこれを応用して作成していく。

まずはappを作るための作業ディレクトリを作成する。
bijoとかでいいだろう。

```:terminal
mkdir bijo
cd bijo
```

次にpythonファイルを作成する。

作成したpythonファイルは以下のようになる。
必ずapp.pyという名前をつけること。

```:app.py

from flask import Flask, request, abort

# os内のenvironmentを扱うライブラリ
import os

from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)



#環境変数取得

YOUR_CHANNEL_ACCESS_TOKEN = os.environ["YOUR_CHANNEL_ACCESS_TOKEN"]
YOUR_CHANNEL_SECRET = os.environ["YOUR_CHANNEL_SECRET"]

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


app = Flask(__name__)



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

@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:
        print("Invalid signature. Please check your channel access token/channel secret.")
        abort(400)

    return 'OK'


@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text))


if __name__ == "__main__":
    app.run()
```


`YOUR_CHANNEL_ACCESS_TOKEN`
`YOUR_CHANNEL_ACCESS_TOKEN`
には先程控えた
`Channnel secret`
`Channel access token`
を使用する。
osを使ってherokuの環境変数から情報を取り出す。
これは後ほどherokuで設定する。


**3.Dockerfileとdocker-compose.ymlの作成**

herokuでProcfileなどを作成するのはめんどくさいし、できるならモダンな技術が使いたかったのでdockerを使用する。
Dockerfileとdocker-compose.ymlは以下のようにbijoディレクトリの中に作成する。

※herokuではdocker-compose.ymlは使用されないので注意。

heroku.ymlというdocker-compose.ymlのheroku版を作らなきゃいけないらしいけどなんか面倒そうなので今回はパス。

```:docker-compose.yml
# ローカル作業用:heroku内では作用しない。

# ローカル作業用:heroku内では作用しない。

version: '3'

services:
  app:
    build: .
    container_name: bijobot
    tty: true
    ports:
      - 5000:5000
    environment:
        FLASK_ENV: 'development'    #デバッグモードON
        FLASK_APP:  "app.py"        #app.pyを起動 
        YOUR_CHANNEL_ACCESS_TOKEN: 控えたaccess token
        YOUR_CHANNEL_SECRET:控えたchannel secret
    volumes:
      - ./:/code/
    command: flask run -h 0.0.0.0
    # command: gunicorn -b 0.0.0.0:$PORT app:app --log-file=-
```

`FLASK_ENV: 'development'`としておくことでファイルをsave時に自動でサーバーに反映される。


```:Dockerfile
FROM python:3.7-alpine

# codeというディレクトリを作成し、そこを作業フォルダとする
WORKDIR /code

#デバッグモードON
ENV FLASK_ENV: 'development'   

# alpineのおまじない
RUN apk add --no-cache gcc musl-dev linux-headers

# requirements.txtをcode内に移動
COPY requirements.txt requirements.txt
# requirements.txtの中のライブラリをinstallする
RUN pip install -r requirements.txt

# すべてのファイルをcode内にマウントする。
COPY . .

# $PORTを指定しないと動かないので注意
CMD gunicorn -b 0.0.0.0:$PORT app:app --log-file=-

```

さらにrequirements.txtを作成する。

こちらはいかのコマンドを叩くことで解凍可能だが今回は予め作成しておく手順で行く。

```:terminal
pip freeze > requirements.txt
```

requirements.txtには以下のように記入する。

```
certifi==2020.11.8
chardet==3.0.4
click==7.1.2
Flask==1.1.2
future==0.18.2
gunicorn==20.0.4
idna==2.10
itsdangerous==1.1.0
Jinja2==2.11.2
line-bot-sdk==1.17.0
MarkupSafe==1.1.1
requests==2.25.0
urllib3==1.26.2
Werkzeug==1.0.1
```

requirements.txtはpythonのライブラリを予め書いておきインストールできるようにするもので、本来であれば、

```
pip install gunicorn flask line-bot-sdk
```
としなければ行かなかったものを必要なくする。


```:terminal
touch requirements.txt
```

次に以下のコマンドを打ってdocker containerを立ち上げる。

```:terminal
docker-compose build
docker-compose up -d
```

ここでlocalhost:5000にアクセスし、hello!と表示されることを確認する。

# 4.herokuでのデプロイ
herokuの登録は予め済んでいるうえですすめる。
まずはherokuにログインする。

```:terminal
heroku  login
heroku container:login
```

次にherokuにappを作成する。
`heroku create つけたいアプリ名`とすることでおｋ。
今回はbijobijoという適当なapp名をつけた。
ここでエラーが出たら、すでにあるアプリ名なのでつけ直す必要がある。

```
heroku create bijobijo
```

次にlocalのgitとremoteのgitを紐付ける必要がある。
以下のコマンドで紐付けは可能。

```:terminal
heroku git:remote -a アプリ名
```


次に
`heroku config:set つけたい環境変数名="設定する環境変数の文字列" --app 自分のアプリケーション名`
とすることでherokuに環境変数がセットできる。

そのため以下をセットする。

```
heroku config:set YOUR_CHANNEL_SECRET="Channel Secretの文字列" --app 自分のアプリケーション名
heroku config:set YOUR_CHANNEL_ACCESS_TOKEN="アクセストークンの文字列" --app 自分のアプリケーション名
```

configに関しては以下のようにherokuのサイトで自分のアプリよりsettingのconfig varsより確認することも可能。

![スクリーンショット 2020-11-16 0.24.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656158/78f73d17-52fb-9ea2-801e-b864c6428d0b.png)


セットした環境変数は

```
heroku config
```

で確認することもできる。

これで環境変数が設定できたのでuploadしていく。

### ここで詰まったとこ

みんな安易に

```
heroku container:push web
```

としてたからdocker-compose.ymlの名前のことかと思い

```
heroku container:push app
```

としていたがうまく起動しなかった。

どういうことか検索していると、以下の記事に答えが書いてあった。

[Container Registry & Runtime (Docker Deploys)](https://devcenter.heroku.com/articles/container-registry-and-runtime)

```
heroku container:push <process-type>
```

>To push multiple images, rename your Dockerfiles using Dockerfile.<process-type>:

つまり、Dockerfile.webという名前をつけていればこれを起動させることができる。

すぐにDockerfileをDockerfile.webに書き換えて、以下のコマンドを順々に叩く。

```:terminal
heroku container:push web
heroku container:release web
```

これでアプリがリリースされた状態になる。

ここで以下のコマンドを叩き、hello!と書いてあるページに遷移すればokである。

```:terminal
heroku open
```

実際に動いているコンテナについてはheroku公式より自身のアプリからResourcesより確認することができる。
以下であればwebが起動しているのがわかる。

![スクリーンショット 2020-11-16 1.19.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656158/91a9f59f-44c0-1570-cf7e-fa601ac5e811.png)

最後に、LINE Developerより自身のchannelに移動し、Messaging APIより
Webhook settingsを選択し、
`https://{自身のアプリ名}.herokuapp.com/callback`
をセットしてやれば終わりである。
また、以下のように`Use webhook`をonにしないとlinebotが機能しないので注意。
![スクリーンショット 2020-11-16 1.28.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656158/6c80ab7b-8dbf-ca89-6792-d00895bb393a.png)

これで毎日美人とメッセージのやり取りができる！！

![S__116711449.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656158/7de10d3d-e23d-3548-390b-ef4a8c599621.jpeg)


あれ、なんか固定メッセージが来ているようなので対処します。

Messaging APIより`Auto-replay messages`を選択。
![スクリーンショット 2020-11-16 1.30.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656158/d3438891-d26d-732d-c301-3a80e272a590.png)

応答設定→詳細設定→応答メッセージ→応答メッセージ設定→オフ
でこれが解消される。

![スクリーンショット 2020-11-16 1.34.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656158/bbacbb67-2905-933c-6009-601b1b4d4544.png)

ではでは、

![S__116711452.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/656158/2ca57453-4790-d3e9-94c5-52be41fd5219.jpeg)

うまく返せていますね。

こころなしか虚しいです。

linebotを使うことで業務の自動化などもできそうなのでこれ以降も挑戦していきたいと思う。


# 参考記事
**全体図**

[初心者でも簡単！PythonでLINEチャットボットを作成してみよう！](https://www.youtube.com/watch?v=jBsvdgFMZtg&t=62s)

**herokuでenvironmentを設定する方法**

[Python + HerokuでLINE BOTを作ってみた](https://qiita.com/shimajiri/items/cf7ccf69d184fdb2fb26)

**requirements.txtの取り扱い**

[Python, pip list / freezeでインストール済みパッケージ一覧を確認](https://note.nkmk.me/python-pip-list-freeze/)

**dockerのherokuへのpush**

[Container Registry & Runtime
](https://devcenter.heroku.com/articles/container-registry-and-runtime)↑これがreleaseの際に最も役に立った。

[Local Development with Docker Compose]
(https://devcenter.heroku.com/articles/local-development-with-docker-compose)
[Python初心者がFlaskでLINEbotを作ることになった（Heroku+ClearDB編）](https://qiita.com/suwa-andante/items/8e3e9d12a87540c20aa0)