1時間でWikipedia検索できるLINE BOTをサクッと作ってみよう！

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">土日で何か作ろうと思って、言葉を検索できるLINE BOTをミニマムで作ってみた。 <a href="https://t.co/SgzzsAFVjc">pic.twitter.com/SgzzsAFVjc</a></p>&mdash; 辻佳佑 (@t0daaay) <a href="https://twitter.com/t0daaay/status/1208657381090971648?ref_src=twsrc%5Etfw">December 22, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
こんな感じのWikipedia検索できるLINE BOTを作成してみました。

思った以上に簡単に作れたので、作り方を紹介します。
時間測ってないですが、初心者でも1時間あれば全然作れる気がします（知らんけど）
振り返りながら書いているので、手順が抜けていたらごめんなさい...
今回は、python(Flask)で開発し、herokuにデプロイしていきます。

#LINE Developersの登録
まずはじめに、LINE Developersに登録しましょう。
https://developers.line.biz/ja/ へアクセス

<img width="1440" alt="スクリーンショット 2020-01-07 22.36.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491279/3089f1a7-9568-0374-56cd-b9cdcbe94b1d.png">
右上のログインをクリック
多くの人はLINEのアカウントをお持ちかと思いますので、そのまま登録してしまいましょう。

登録後、サービス提供者を表す「プロバイダー」を作成。
続けて、登録したプロバイダー内で新規チャネルを作成。
チャネルの種類はMessage APIを選択してください。

<img width="1440" alt="68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3439313237392f64636532643562332d656532382d366235392d333732632d3837356630353034383333312e706e67-2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491279/d4eed6e3-3002-df9a-df4d-345c8e179414.png">
また後ほど触りますが、一旦これでLINE Developersの設定はおっけーです！

#herokuの登録
続いてherokuにも登録します。
今更初めて使ったけど素晴らしい...
PaaSと呼ばれるサービスで、超簡単にデプロイできちゃいます。

- サーバー
- OS
- DB
- プログラミング言語の実行環境

といったサービス公開のために必要なものを用意してくれて、しかも無料で使えるという。
無料で使うと、挙動が遅くなってしまったりと、制約つきではありますが、簡単なLINE BOTを遊びで作るには十分かと思います。
それでは、登録していきましょう。
https://jp.heroku.com/ へアクセス

<img width="1440" alt="スクリーンショット 2020-01-08 0.54.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491279/f5a81623-f1b5-1081-8cc8-d781df96d43e.png">

右上の新規登録から、手順に沿って登録しましょう。
日本語の説明に沿って入力するだけですので、詳しい説明は割愛します。

登録完了すると、こんな画面が表示されます。
<img width="1440" alt="スクリーンショット 2020-01-08 0.57.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491279/273e4492-ed56-c6c6-5a50-4c69559f7d9c.png">
続いてアプリケーションの作成をします。
コマンドでの作成も可能なのですが、今回はブラウザ上で作成します。
右上のNewをクリック、続けてCreate new appをクリックしましょう。
<img width="1440" alt="スクリーンショット 2020-01-08 0.58.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491279/0cf9c70c-e0c2-9920-534b-fddb452b7972.png">

すると次のような画面に遷移するので、名前とリージョンを選択します。どっちでも良いかと思いますが、今回、リージョンはアメリカにします。

<img width="1440" alt="スクリーンショット 2020-01-08 0.58.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491279/83b033e1-2474-ba34-5fb6-469696a284a2.png">

<img width="1440" alt="スクリーンショット 2020-01-08 9.02.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491279/005a2e72-ce7d-f4f1-08f1-31f83482e2d3.png">


続いてherokuをインストールしていきます。
下記URLより、対応するOSのインストーラーから、もしくはコマンド入力でインストールします。
https://devcenter.heroku.com/articles/heroku-cli#download-and-install

インストールするとherokuのコマンドラインが使えるようになります。
まずはログインしてみましょう。

```
$ heroku login
```

すると、メールアドレスとパスワードを求められるので、heroku登録時のアドレスとパスワードを入力しましょう。

```
Enter your Heroku credentials:
Email: 登録したアドレスを入力
Password: 登録したパスワードを入力
```

これでログイン完了できたかと思います。

続いて、環境変数を設定していきましょう。
環境変数とは？: https://wa3.i-3-i.info/word11027.html

```
$ heroku config:set YOUR_CHANNEL_ACCESS_TOKEN="チャネルアクセストークンの文字列" -a アプリケーション名
$ heroku config:set YOUR_CHANNEL_SECRET="チャネルシークレットの文字列" -a アプリケーション名
```
アプリケーション名は先ほど決めたherokuのApp name、
「アクセストークンの文字列」と「チャネルシークレットの文字列」はLine developersで以下のように確認可能です。

まずは、チャネルアクセストークンの確認。
<img width="1440" alt="スクリーンショット_2020-01-09_23_49_32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491279/1eccb438-3dd7-4914-e426-e320af861b19.png">
<img width="1440" alt="スクリーンショット_2020-01-09_23_49_47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491279/074067ef-5d4f-293d-86dd-7a346f897a0a.png">
<img width="1440" alt="スクリーンショット_2020-01-09_23_51_31-2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491279/b438c382-a283-8a4a-a4f9-fca668e1ffcf.png">
続けて、チャネルシークレットの確認
<img width="1440" alt="スクリーンショット_2020-01-09_23_51_13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491279/8bf7e1c7-650c-160d-b5ff-2039c04dd397.png">
<img width="1440" alt="スクリーンショット_2020-01-09_23_51_13-2-2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491279/0cd630d6-3925-dfff-5d97-a95f72d863c2.png">
<img width="1440" alt="スクリーンショット_2020-01-09_23_51_17-2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491279/37c20d02-5503-5d3b-1e56-53214e025a4d.png">

これで、「YOUR_CHANNEL_ACCESS_TOKEN」と「YOUR_CHANNEL_SECRET」という環境変数がデプロイ先で使えるようになりました。

ここまでできたらコードを書いていきましょう。pythonで書いていきます。

まず、以下のようにファイル、およびフォルダを作成してください。

```
lineBot
├── Procfile
├── main.py
├── runtime.txt
└── requirements.txt
```
作成した後、以下のように作成したファイルに入力していきます。

Pythonのバージョンを記載

```runtime.txt
python-3.6.6
```

インストールするライブラリを記載する設定ファイル

```requirements.txt
Flask==0.12.2
line-bot-sdk==1.5.0
```
今回は小規模なアプリケーションでも対応しやすいマイクロフレームワーク、flaskを使って開発します。
LINE Messaging API SDKは、LINE BOT開発のためのライブラリです。
参考: https://developers.line.biz/ja/docs/messaging-api/line-bot-sdk/

プログラムの実行方法を定義する設定ファイル

```
# Procfile
web: python main.py
```
あとは、main.pyに先ほどのLINE Messaging API SDKのリンク先にあるpythonの公式SDKからコードを引用します。
pythonの公式SDK: https://github.com/line/line-bot-sdk-python

ここのSynopsisに記載されるコードがオウム返しのLINE BOTになっています。
少し変更する必要がありますが、基本的には引用でいけるので、一旦、main.pyにコピペします。

```main.py
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('YOUR_CHANNEL_ACCESS_TOKEN')
handler = WebhookHandler('YOUR_CHANNEL_SECRET')


@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()
```
コードを読んでみると、先ほど定義したherokuの2つの環境変数ではなく、直接代入される形になってしまっているので、これを書き換えます。

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

app = Flask(__name__)

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.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__":
    port = int(os.getenv("PORT", 5000))
    app.run(host="0.0.0.0", port=port)
```

変更箇所は以下の通りです。

```.py
# 追加
import os

app = Flask(__name__)

# 追加
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)

//省略//

# 変更
if __name__ == "__main__":
    port = int(os.getenv("PORT", 5000))
    app.run(host="0.0.0.0", port=port)
```
これで、定義したOSの環境変数を使用することができます。

ざっくりですが、コード解説

```.py
@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'
```

/callbackへPOSTすると、Webhook（後ほど出てきます）からのリクエストをチェックし、正しく帰ってきたとき、handlerに定義されている関数が呼び出されます。

```.py
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text))
```
LINEメッセージが正しく送信できると、関数handle_messageが呼び出され、reply_messageの第2引数の値がLINEの返信として返されます。

ここで一度、herokuへデプロイしてみましょう。

```
$ cd 作成したディレクトリ/
$ git init
$ heroku git:remote -a アプリケーション名
$ git add .
$ git commit -am "make it better"
$ git push heroku master
```
正しくpushできていれば、

```
$ git branch -a
```
で

```
remotes/heroku/master
```
と表示されるはずです。

#Webhookの設定
Webhookとは、あるアプリケーションから別のアプリケーションにリアルタイムに情報を送信するための仕組みです。
LINE BOTのイベント通知を、リアルタイムに行うために使います。
設定を行うために、再度、Line Developersにアクセスします。
<img width="1440" alt="スクリーンショット_2020-01-11_1_02_05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491279/331c4731-afca-67f8-e24b-82a4ec6f2e6f.png">

Message APIの設定 > Webhook設定に

```
https://<アプリケーション名>.herokuapp.com/callback
```
を入力、Webhookの利用をオンにしましょう。
これでオウム返しするようになります。ぜひ実際にラインを送ってみてください。
Line Developers の Message APIの設定 にQRコードがあるので、そこから作成したLINE BOTを友達登録して、会話してみましょう。

ただし、このままの設定だと、「あいさつメッセージ」やラインを送信した際に返ってくる「応答メッセージ」など余計なメッセージが表示されてしまいます。
Messaging API設定からあいさつメッセージの内容の変更、または無効化、応答メッセージの無効化など設定できるので修正してください。
<img width="1439" alt="スクリーンショット_2020_01_13_1_26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491279/eea91b0c-6c16-069d-58c3-f4fc1825ce9e.png">


#Wikipedia検索できるようにする
オウム返しするだけだと面白くないので、
WikipediaのAPIを使って検索できるようにしてみましょう。
参考: https://pypi.org/project/wikipedia/

まずはWikipediaのライブラリを登録

```requirements.txt
Flask==0.12.2
line-bot-sdk==1.5.0
wikipedia==1.4.0 # 追加
```

続いて、先ほどのmain.pyを以下のように書き換えます。

```main.py
from flask import Flask, request, abort
from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)
import os
import wikipedia # 追加

app = Flask(__name__)

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)

wikipedia.set_lang("ja") # 追加

@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:
        abort(400)
    return 'OK'

@handler.add(MessageEvent, message=TextMessage)
# 先ほどの関数内にWikipediaのライブラリを使った処理を記述していく
def handle_message(event):
    send_message = event.message.text
    # 正常に検索結果が返った場合
    try:
        wikipedia_page = wikipedia.page(send_message)
        # wikipedia.page()の処理で、ページ情報が取得できれば、以下のようにタイトル、リンク、サマリーが取得できる。
        wikipedia_title = wikipedia_page.title
        wikipedia_url = wikipedia_page.url
        wikipedia_summary = wikipedia.summary(send_message)
        reply_message = '【' + wikipedia_title + '】\n' + wikipedia_summary + '\n\n' + '【詳しくはこちら】\n' + wikipedia_url
    # ページが見つからなかった場合
    except wikipedia.exceptions.PageError:
        reply_message = '【' + send_message + '】\nについての情報は見つかりませんでした。'
    # 曖昧さ回避にひっかかった場合
    except wikipedia.exceptions.DisambiguationError as e:
        disambiguation_list = e.options
        reply_message = '複数の候補が返ってきました。以下の候補から、お探しの用語に近いものを再入力してください。\n\n'
        for word in disambiguation_list:
            reply_message += '・' + word + '\n'
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(reply_message)
    )

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

上記のコードで1番最初のtwitterに掲載したようなものが作成できました。
再びデプロイして、挙動を確かめてみてください。

一応これで完成なのですが、wikipedia内で検索してもらえれば分かる通り、日本語検索のアルゴリズムがかなり微妙なので、返ってくるワードがしっくりこなかったりします。多分しゃーない。

以上で解説は終わりです。
色々応用できると思いますので、ぜひともオリジナルのLINE BOTを作成してみてください。
結構急いで書いたので、抜け、漏れ、改善点などあればご指摘いただけますと幸いです。

#最後に
以下は弊社の宣伝になります。

現在弊社では、HRモンスターと呼ばれる
採用の新しいスタイルを提供するサービスをローンチいたしました。

ローンチ後のさらなる機能追加、改善などのPDCAサイクルを回すべく、
エンジニアを募集しております。
https://www.wantedly.com/projects/341182

Kubernetes、Vue.js（Javascript）、Django（Python）といったモダンな技術を使って、
開発しておりますので、もしご興味がある方はぜひ、ご応募お待ちしております。