【Python】英単語をランダムに出題するLine botを作ってみた

#はじめに
前回、オウム返しbotを作ったので、応用して
英単語をランダムに出題するbotを作ってみました。

前回記事のリンク
https://qiita.com/takuya0125/items/36bdea94c249f592a59f

Heroku、gitの操作方法については割愛します。

#ディレクトリ構成
ディレクトリに下記のファイルを作成しました。
・main.py
Line messagingAPIを叩いたり、出題したりするメインソース

・high1.txt
高校一年生の履修単元における英単語とその意味を書いたテキストファイル

・Procfile
・requirements
・runtime
(隠しファイルに.git)

#実装
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 re
import random

#high1.txtの読み込み
source = 'high1.txt'
with open(source, encoding="utf-8_sig") as f:
    data = f.read()
english_words = re.findall('[a-z]+', data) #アルファベットの抽出
ja = re.findall('.*[ぁ-ん].*', data)　#日本語の抽出
words_dict = dict(zip(english_words, ja))　#それぞれを辞書に格納

answer =[] #単語に対する答えの日本語の箱を作成

#単語と意味の一覧を1～4の選択肢にして出題
def question():
    question_word =random.choice(english_words)
    correct_answer = words_dict[question_word]
    meanings_copy = ja.copy()　#間違いの選択肢を抽出するため、コピーを作成しておく
    meanings_copy.remove(correct_answer)
    wrong_answers = random.sample(meanings_copy, 3)
    answer_options = [correct_answer] + wrong_answers
    random.shuffle(answer_options) #回答をシャッフル

    list =[] #出題する選択肢を箱に入れておく
    for i in range(4):
        x = '{}. {}'.format(i + 1, answer_options[i])
        list.append(x)
        res = re.findall(correct_answer, x)
        if len(res) ==1:
            answer_num = i+1
            answer.append(answer_num)
            
    question_message = ('問題:{}\n{}\n{}\n{}\n{}\nYour answer?'.format(question_word,list[0],list[1],list[2],list[3]))
    return question_message　#question_messageに返す
```
ここまでが、英単語の出題になります。

次に、LinemessagingAPIとの連携を行います。

```main.py
app = Flask(__name__)
app.debug = False
 
#環境変数取得
# LINE Developersで設定されているアクセストークンとChannel Secretをを取得し、設定します。
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)
```

ここまでは、トークン等の設定です。

次に、実際に出題に対するユーザの回答を記載します。

```main.py
@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)
 
    # handle webhook body
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)
    return 'OK'

@handler.add(MessageEvent, message=TextMessage)　#テキストファイルへの応答
def handle_message(event):

    question_message = question() #questionの実行と変数への登録
    text_pass = event.message.text #受け取ったメッセージを変数へ登録

    #「a」、「あ」、「0」とユーザが送信すれば、出題#
    if text_pass == 'a' or text_pass =='あ' or text_pass =='0':
        line_bot_api.reply_message(
            event.reply_token,
            [TextSendMessage(text = question_message),])
    elif int(text_pass) == int(answer[-2]):　#ユーザが送信した番号が正しければ、返信
        line_bot_api.reply_message(event.reply_token,
        [TextSendMessage(text = '正解'),])
    elif int(text_pass) != int(answer[-2]): #ユーザが送信した番号が間違いであれば、返信
        line_bot_api.reply_message(event.reply_token,
        [TextSendMessage(text = '不正解'),
         TextSendMessage(text = '正解は {}'.format(answer[-2]))],)
        

# ポート番号の設定
if __name__ == "__main__":
#    app.run()
    port = int(os.getenv("PORT"))
    app.run(host="0.0.0.0", port=port)
```

ここまでが、Lineとの連携になります。
出題するたびに、「a」、「あ」、「0」を入力するのが手間ですね。
今回は、ここまでの開発にして、今後機会あれば、改良します。

#総括
Lineとの連携はそこまでハードルは高くないが
実際にユーザとコミュニケーションをとるのはかなりハードルが高そう。
今後、LinemessagingAPIとの連携を増やせるアイディアを増やし、実装する。
ユーザフレンドリーなツールを少しずつ作成する。

2020年3月5日　プログラム修正
int(answer[0])⇒int(answer[-2])
リストの最後から2番目をとってくる

#参考
英単語botの作り方

参考文献
・返信用メッセージの工夫
https://miyabi-lab.space/blog/21
該当箇所
TextSendMessage(text='「' + event.message.text + '」って何？')
     )

・返信用メッセージを複数作成
https://engineering.linecorp.com/ja/blog/imezimatsupumetsuseziwoshi-tsutezhong-dian-nicheng-richi-renaibotsutowozuo-rimashita/
該当箇所
TextSendMessage(text='位置情報を送ると近くで終電まで空いている駅一覧を教えるよ(※絵文字1) '),
                TextSendMessage(text='line://nv/location'),

・英単語出題箇所の関数化と実行
https://shikasen-engineer.com/python_line_bot/
該当箇所
result = sc.getNews(word)

・その他
https://datacoach.me/data/engineering/python-linebot-talk/
https://keinumata.hatenablog.com/entry/2018/05/08/122348
https://myafu-python.com/line-basic-1/