LINE Messaging API＋Ngrok＋Python　で実験的にLINEボットを開発してみよう　Part1準備とコードを書く編

#０．先日のLINEに関する一連の事件に関して
まず記事に関する説明の前に先日、LINEに関する情報漏洩の疑いに関する事件などがありました。LINEはすでに国内において大衆ツールとして多くの人が利用しています、また行政機関もLINEでのサービスを始めています。私がこの事件に関して何か意見できるほどの知識を持っているわけでないので事件に関しての意見は控えますが、幅広い年齢層の情報意識見直しのきっかけとなったものになったのではと考えます。やはり物（システムなどを含めて）を作るという事はその先にある責任にも作成者は多少向き合う必要があると思うのです。もちろん使う側が実際には責任を負って使うわけですが、だからと言って作る側がどんなもの（倫理違反になりえるものなど）でも作っていいとは思いません。でも深く考えすぎてもよくないのも事実です！ある程度のリスクは妥協し、そしてその先の可能性に触れるべきだと思います。大事なのは「もの」を作って誰が幸せになるか？不幸になる可能性がないか？です。もちろんすべての人が幸せで不幸じゃないという状況は作れませんが、意識のあるなしはその先の行動に大きく影響すると思います。この記事を読んで、求めている技術を習得してくださるのはとても光栄ですが、それと同時に物を作る事に関する倫理問題などにも興味を持ってくれたら幸いです。
#１．概要
さて前置きが長くなってしまいましたが今回は題名にもある通りLINE Messaging APIとPython、そしてNgrokを使用してWebhookを使用したLINEボットを作っていきます。<strong>注意点としてこの記事を参考に”実験的”に作るのは構いませんが本番仕様で作成するのは推奨しません。</strong>Ngrokはあくまで実験的なものだからです。本番仕様に様々な機能を使用したり長い間の動作などはセキュリティ上あまりよくないのです。NgrokではWebhookを使用する上で自分のローカル環境をある程度セキュリティがかかった状態で公開するものです。もし本番仕様にしたい場合はAWSなどセキュリティがおおよそ大丈夫であると判断できるサービスを利用しましょう。
#２．準備
まずは何を使うかですが私が個人的にお勧めするのはRaspberry Piです。特にRaspberry Pi 4（4GB以上のメモリがあるとよい）ですね。WindowsやMac上でも今から説明する機能は使えますが実際にサーバーのような扱いをするのでRaspberry Piがサーバーを勉強したりするのにとても便利です。またある程度安価だというのも特徴的です。
では今回の説明で準備するものを以下にまとめました：

* Rapberry Pi 4 4GB
* Windows PC（実際にはSSHでWindowsからラズパイを操作、MACでも可）
* Raspberry PiをSSHで操作する（参考として[VSCodeのSSH接続機能で、RaspberryPi内のコードを編集してデバッグ](https://qiita.com/c60evaporator/items/26ab9cfb9cd36facc8fd)）
* Raspberry PiのIPアドレス固定化（参考として[RaspberryPiのIP固定化に関する問題など](https://qiita.com/Antareskkudo/items/782e4e94b8d04d95645d)）
* LINE Messaging API登録（参考として[Messaging APIを始めよう](https://developers.line.biz/ja/docs/messaging-api/getting-started)）
* Raspberry PiにNgrokインストール（参考として[Raspberry Pi】ngrokのインストールからローカル環境の公開まで](https://qiita.com/sunaga70/items/6821772a9bcbdbbc2c03)）
* LINEBOTSDKのインストール（Raspberry Piの場合はpip install line-bot-sdk＊pip3の方が良いかも）
* その他Pythonで使用するライブラリのインストール（一部後述）

```
OSに関する情報：Linux raspberrypi 5.10.17-v7l+ #1403 SMP Mon Feb 22 11:33:35 GMT 2021 armv7l GNU/Linux
Ngrokバージョン：ngrok version 2.3.35
（Raspberry Piの）pythonバージョン：Python 3.7.3
エディタ：VScode
```

#３．始めよう！
では２の準備ができたら始めていきましょう。まだ準備がうまくできないよという方に関してはコメント欄で尋ねるか、各自ネットで調べてくださいね。ではコードを書いていきますがここから説明するにあたって使用する画像などはWindows上のVScodeで撮ったものです。
##１．ディレクトリ構成を作る
ここではただ作るのは無く、応用ができるようしていきます。今回はPiの下に以下のような構成でディレクトリを作りました。参考にしてみてください。
　　　　　　　　　　　　　　　　　　　　　　　　　　　
>pi/
 └ LINEbot/
 │    └ config.py（環境変数、アクセストークンなど）
 │    └app.py（実際に操作する内容）
 │    └data/
 │    │ └ actiondata.csv（操作内容や会話パターンなど）

上記のディレクトリが構成できたらOKです。実際には自分好みで構成を変えていくとさらに機能性が増します。また上記ディレクトリ名は何でも大丈夫です。以下の例では上記のディレクトリ構成を使用し説明していきます。
##２．コードを書く
操作コードを書く前にconfig.pyにLINE Messaging APIのアクセストークンやチャンネルシークレットを書いていきましょう。

```config.py
LINE_CHANNEL_ACCESS_TOKEN = "アクセストークン"
LINE_CHANNEL_SECRET = "チャンネルシークレット"
```

ではapp.pyに簡単な操作コードを書いていきましょう！

```app.py
import config   #環境変数のインポート
from flask import Flask, request, abort

from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage
)
app = Flask(__name__)

line_bot_api = LineBotApi(config.LINE_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(config.LINE_CHANNEL_SECRET)

@app.route("/callback", methods=['POST'])
def callback():

    signature = request.headers['X-Line-Signature']

    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + 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):
    if event.reply_token == "00000000000000000000000000000000":
        return
    req  = request.json["events"][0]
    userMessage = req["message"]["text"]


    replymessage = selectwords(userMessage)
    
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=replymessage)
    )

def selectwords(commandtext): #対応する言葉を選択
    reply = "こんにちは"
    return reply

if __name__ == "__main__":#最後に置かないと関数エラーが出るので注意！
    app.run(host="localhost", port=8080) # ポート番号を8080に指定

```

上記プログラムはどんなメッセージでも「こんにちは」と返すものです。
##３．コード解説
ここでは上記プログラムをすべて解説します。まずはimport類ですがこれはLine bot apiを使う上で必ず必要なものばかりで機能に依存せず（一部特例を除き）、flaskからfrom linebot.exceptions importまではimportします。configに関しては上記のconfig.pyをimportしています。これにより操作プログラムに直接重要な環境変数を書かないためプログラム内容を共有する際に情報の漏洩を防ぐことができます。

```py
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage
)
```
上記に関してはボットがどんな機能を使用するかに依存します。ここでは **”テキストメッセージ”** を受け取り **”テキストメッセージを送信する”** という意味で<code>TextMessage</code>と<code>TextSendMessage</code>をimportしています。また<code>MessageEvent</code>に関しては **メッセージにより機能が発動する** ボットに関しては必ず必要です。
では次に

```py
app = Flask(__name__)

line_bot_api = LineBotApi(config.LINE_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(config.LINE_CHANNEL_SECRET)

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

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

    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        print("Invalid signature. Please check your channel access token/channel secret.")
        abort(400)

    return 'OK'
```
に関してですがここはほぼ機能に依存しない項目です。操作内容としてはアクセストークンなどの環境変数を挿入します。<code>@app.route("/callback", methods=['POST'])</code>では環境変数をLINE側に渡して認証させる操作です。もし認証が失敗すれば<code>Invalid signature. Please check your channel access token/channel secret.</code>とコンソール上に表示されます。私が調べたところこれ以外の書き方で、認証の操作を書いているものを見つけられなかったので多分これが通説です（断言はできませんが）。

```py
@handler.add(MessageEvent, message=TextMessage)#メッセージが送られた際の操作
def handle_message(event):
    if event.reply_token == "00000000000000000000000000000000":
        return
    req  = request.json["events"][0]
    userMessage = req["message"]["text"]

    replymessage = selectwords(userMessage)
    
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=replymessage)
    )

```
次に上記ですがこれは簡単に言うと”テキストメッセージが送られてきた際に発生する操作”です。なので今回はこの中身がメインですね。上から見ていきましょう。<code>@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):</code>ここまではほぼ通説です。def関数名の関数名などは変えても良いかもしれませんがあまり推奨しません。
<code>if event.reply_token</code>このif文に関してはLINE側の設定でWebhookを設定しますが、その認証時に発動する操作です。<code>req  = request.json["events"][0]</code>と<code>userMessage = req["message"]["text"]</code>は受信メッセージを変数userMessageに格納しています。後ろの["events"][0]や["message"]["text"]については今は詳しく述べませんが後の回で述べるつもりです。次に<code>replymessage = selectwords(userMessage)</code>これは先ほどのuserMessageをselectwords関数の引数として挿入し関数内で出力（返された）変数をreplymessageに格納します。
次に<code>line_bot_api.reply_message(event.reply_token,TextSendMessage(text=replymessage))</code>に関してはLINEボットの本来行う操作をまとめたものです。event.reply_tokenは誰にメッセージを送るのかを設定（この場合は返信なので送信してきた相手に送信）、textmessageに関しては何を送信するかを決めています（この場合は前の操作で格納されたreplymessageです）。

```py
def selectwords(commandtext): #対応する言葉を選択
    reply = "こんにちは"
    return reply
```
次にselectwords関数ですがこの関数は唯一このプログラムの中で作者がすべて考え作成したものです（通説ではないという意味）。ここでは送られてきたメッセージに対しての処理の内容を記しています。応用させるには引数であるcommandtestをうまく使えばいいとわかりますね。

```py
if __name__ == "__main__":#最後に置かないと関数エラーが出るので注意！
    app.run(host="localhost", port=8080) # ポート番号を8080に指定
```
最後に上記のコードですがこれはほぼ通説です。コメントにもある通り、このコード行は必ずプログラム全体の最後に挿入してください。また引数のportはシステムを動かすポートなので自分の環境下に合ったものに設定してください。大体は8080や8000番台が多いです。

#４．疲れたので休憩
ここからは実行の説明、実演に移りますが正直まだまだ説明したい事項がありますし、読んでくださる人がどれくらいいるのかわからないので少し様子見です。もしある程度需要があれば早めにその２実演と簡単な応用編を出します。需要がなくてもいずれ出す予定ですが結構後になるかもです（私の大学の日程などに依存するかも、、、）少し最後の方は雑に書いてしまって申し訳ないのですが楽しめたでしょうか？
何か不明点、指摘点などありましたら是非コメント欄にお願いします！改変すべきところがありましたら随時、改変します！
では良い電子工作ライフを！！

続編出しました！ > [LINE Messaging API＋Ngrok＋Python　で実験的にLINEボットを開発してみよう　Part2簡易動作と応用の準備](https://qiita.com/Antareskkudo/items/4d70c6cc0f8dfd18e82b)

#参考資料、サイト
・[【入門用】PythonによるLINEbot作り方](https://www.tomotaku.com/line-repeat-bot)：ディレクトリの構成などとても参考になりました。もし私の説明が分からない場合はこちらのブログを見てもらえるとよくわかると思います。

・[VSCodeのSSH接続機能で、RaspberryPi内のコードを編集してデバッグ](https://qiita.com/c60evaporator/items/26ab9cfb9cd36facc8fd)
・[RaspberryPiのIP固定化に関する問題など](https://qiita.com/Antareskkudo/items/782e4e94b8d04d95645d)
・[Messaging APIを始めよう](https://developers.line.biz/ja/docs/messaging-api/getting-started)
・[Raspberry Pi】ngrokのインストールからローカル環境の公開まで](https://qiita.com/sunaga70/items/6821772a9bcbdbbc2c03)

#追記
コードの重要な部分を忘れていました！！
<code>def handle_message(event):</code>関数の中に

```py
req  = request.json["events"][0]
```

を追加しましたので確認してみてください！！
またコピペしやすいように行も整理しました！２０２１年４月２０日