GROWI開発の知見でLINE_bot作ってみた

この記事は [WESEEK Advent Calendar 2020](https://qiita.com/advent-calendar/2020/weseek) 11日目の記事です。

# イントロダクション

GROWI 開発チームの yusuketk です。

この記事では Web 開発未経験だった僕が GROWI([公式サイト](https://growi.org/)) という Web アプリの開発で得た知識をもとに LINE bot 用のアプリケーションを構築した話をします。

と、タイトルを見ればわかる話ですが、注意して欲しいのはここでの GROWI というのは単に僕が人生で初めて開発に携わったアプリというだけで、今回の記事は「GROWI 独自の開発テクを活用した話」ではなく単純に「ある既存アプリを真似して自分でもアプリを作ってみた話」であるという点です。

LINE bot 作成は PC に慣れている人なら割りかし誰でもできるようにいろんなサービスが展開されています。その中でも Massaging API を自作アプリに導入することで bot の動作をかなりカスタマイズすることができ、今回はそれにチャレンジしていきます。

ちなみにそのやり方の中でも Google が提供している [Google Apps Script](https://developers.google.com/apps-script) というサービスを利用するとネットワークやアーキテクチャのことをほぼ何も考えずに簡単に作れるらしいです。が GROWI の開発でコンテナネットワークやアーキテクチャなどの知見を得たからにはそれすらも頼らず bot 開発してしまおう、と言うのが今回の話のメインです。

「3年前の自分へ、もう僕は一人で LINE bot を作れるまでに成長したよ :sparkles:」


## 環境
- Mac OSX 10.15.7


# Messaging API による LINE bot 作成

LINE bot でできることややり方についてはたくさん記事があるのでここでは詳細は割愛しますが、自動的にToDoを通知してくれるbotや送られてきたメッセージに応じた返信をしてくれるbotなど、いろいろできます。

大まかな仕組みとしてこんな感じ([LINE 公式 docs](https://developers.line.biz/ja/docs/messaging-api/overview/) の画像を加工)

![Screen Shot 2020-12-09 at 22.11.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272405/aaad6502-c9cd-0006-52d8-686bcfc3be4e.png)


必要な作業としては

- LINE Developers に登録し、LINE bot アカウントを作成
- Messaging API の webhook URL に今回作成するアプリを立ち上げる URL を登録(ここにPOSTリクエストが送信されます)
- アプリを作ってデプロイ！

これだけです！

# 何作ったの？
本記事内ではほぼ関係ないのでこの章は読み飛ばしてもらって結構です。

今回は麻雀の対戦結果を計算・管理してくれる「麻雀対戦結果管理用の LINE bot」をつくり、今も開発中です。

ちなみに僕は麻雀苦手なので対局のお誘いは遠慮させていただきます。

## きっかけ

麻雀やったことある人はわかると思いますが、麻雀では対戦が一回終わるごとに各ユーザーが対戦で勝ち取った素点に、順位点を足したり端数を切り落としたりといった計算処理があります。

友人同士のオンライン麻雀で使っているアプリでもその計算はしてくれますが、ローカルルールと計算方法がちょっと違うので再計算するようにしています。

オンライン麻雀の時の連絡は LINE でやってるので、この計算を自動でやってくれる LINE bot 欲しいねという話になり、最終的に「麻雀アプリの結果画面のスクショから点数読み取って自動的に計算して、ついでに結果を管理をしてくれるbot」を作ろうということになりました。(いずれは支払いシステムと連携して賭○金の自動精算機能とｋ)


# 製作開始
## 技術選定

できれば今まで使ったことない技術を使ってみたい、けど触ったことがない言語を選ぶと調べることが多すぎて捗らない。

ということで今回は書いたことがある言語の python を選び、使ったことがないフレームワークの Flask を使ったアプリ作成をしてみました。

## Hello, world

この章は単純に Flask を用いたアプリ開発の話です。(python 実行環境構築は忘れたので割愛します。)

Flask をインストールして以下のファイルを作成。

```python:app.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello World"

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

ターミナルにて `flask run` で起動。ブラウザで localhost:5000にアクセス。画面に Hello World が出れば成功です！

ちなみにパスを追加したい場合は `@app.route('/hoge')` と書いてその下にメソッドを書けば、そのパスに GET リクエストが飛んだ時にそのメソッドが実行されます。

今回は LINE Platform からの POST リクエストを受け取るための `/callback` (パス名は任意)を用意すれば良いのでこんな感じ。

```python:app.py
...

@app.route("/callback", methods=['POST'])
def callback():
    print('recieve a POST request')
    return
...
```

## sdk の導入
LINE Platform からのリクエストを受け取ってアプリ内でいろいろ処理を行うための sdk をインストールします。各言語用のものがあり、今回は python を使っているので [line-bot-sdk-python](https://github.com/line/line-bot-sdk-python) を使用します。

`$ pip install line-bot-sdk` でインストールしたら先ほど作った `app.py` に以下のように変更

```python:app.py
from flask import Flask, request
app = Flask(__name__)

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

YOUR_CHANNEL_ACCESS_TOKEN = "YOUR_CHANNEL_ACCESS_TOKEN" # LINE dev console で確認できるチャンネルのアクセストークンを入れる
line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN)
YOUR_CHANNEL_SECRET = "YOUR_CHANNEL_SECRET" # LINE dev console で確認できるシークレットを入れる（外部に漏らさないよう注意）
handler = WebhookHandler(YOUR_CHANNEL_SECRET)

@app.route("/callback", methods=['POST'])
def callback():
    signature = request.headers['X-Line-Signature']
    body = request.get_data(as_text=True)
    try:
        handler.handle(body, signature)
    except exceptions.InvalidSignatureError:
        abort(400)
    return 'OK'
    
@handler.add(MessageEvent, message=TextMessage) # テキストメッセージを受信した場合、ここの処理が走る
def handle_text_message(event):
    # テキストを受信した時に走る処理
    # 今回は送信者にリプライする
    line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text='メッセージを受け取りました'),
    )
    
if __name__ == "__main__":
    app.run()
```

### LINE Platform からアクセスできるように設定

では作ったアプリに LINE からアクセスできるようにしましょう！

#### 一時的なデプロイ

現状

![Untitleds.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272405/a1ed7cff-3852-36e7-f008-84e52611abd0.jpeg)

まだローカル PC の中でアプリを立ち上げただけなので、外部からはアクセスできません。LINE Platform からアクセスできるよう外部に公開していきます。heroku 等を使ってデプロイするのでも良いですが、これから開発を進めるにあたり、修正内容が随時反映されて欲しいので、開発用に一時的なデプロイをできるようにします。

そんな時に使えるのが [ngrok](https://ngrok.com/) ! これを使うとあっという間にローカルで立ち上がっているアプリをデプロイすることができます！

```
$ brew install ngrok
$ ngrok http 5000 # デプロイ対象のポート
```

これだけ！ 出力ログの `Forwarding` の部分に書かれた URL にデプロイされます！

#### LINE Platform に登録

先ほどの操作で現状このようになり、外の世界から今回作ったアプリケーションにアクセスすることができるようになりました。

![Untitled.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272405/5585b71a-ccfb-0266-2f43-b6d8ea410f7d.jpeg)

では LINE Platform からさっきの URL にアクセスするよう設定しましょう。LINE Developers にサインインし、コンソールにて作成しておいたチャンネルの Messaging API > Webhook Settigs > Webhook URL に ngrok が発行した URL に `/callback` と追記して登録します。

問題がなければ検証を実行すると成功のメッセージがきます！

#### 実際に LINE bot にメッセージを送ってみる

作成した LINE bot を友達に追加し、適当なメッセージを送信。

![IMG_8603.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272405/811c23d6-89a5-9c72-1ce0-5677145614fe.jpeg)

うまく行きました！

ちなみにアプリ側では handler に追加したメソッドの引数の `event` に以下のような情報が入っていて、参照することができます。

```json:event
{
"message": {
    "id": "メッセージid",
    "text": "メッセージ本文",
    "type": "text",
    }, 
"mode": "active",
"replyToken": "リプライする時に必要なトークン", 
"source": {
    "type": "user", 
    "userId": "LINEユーザーid"
    }, 
"timestamp": 1604..., 
"type": "message"
}
```

## heroku でデプロイ

本番用は heroku でデプロイしました。heroku でのデプロイ方法はこの記事では割愛します。

ここまでで LINE bot 用アプリの基礎が出来上がりました！長くなってしまったので今回の話はここまでにしようと思います。

# 終わりに

ほんとはこの後以下のように GROWI を真似た話をしたかったのですが、話すほどでもなかったので自分のブログで書くことにします。

- アクションの種類やテキストメッセージの接頭辞によるルーティング用のファイル作成
    - GROWI の routes/index.js の真似
- 各機能ごとにシングルトンの service クラスを用意し使用する
    - GROWI での unstated container や crowi インスタンスの各種 service の真似
- docker-compose を使った開発用DBコンテナの準備（本番用はherokuのpostgresql を使用)
    - GROWI 開発環境 v1 を真似

最後まで読んでいただきありがとうございました。初心者エンジニアの方もこれを見てワンランク上の LINE bot 作成にチャレンジしたいと思っていただければ嬉しい限りです。

※この記事は [WESEEK Tips wiki](https://tips.weseek.co.jp/) に 2020/12/10 に投稿された記事の転載です。
Tips wiki では、IT企業の技術的な情報やプロジェクトの情報を公開可能な範囲で公開してます。