Q&A

LineBotApiをngrokで実行すると正常に動くコードがherokuで実行するとバグが発生する。

linebotで、 キーワードを入力すると問題が開始され、その後ランダムに5問、回答を送ると次の問題をbotが返すシステムを作ろうとしています。 完成形では、問題部分に画像メッセージを使用し、問題と回答のデータをgoogleスプレッドシートに書き込みます。こちらの部分は別途完成しています。 今回掲載しているのは簡略化したものです。(pythonを使用しています)

from os import path
from re import I
from flask import Flask, request, abort

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

from random import randint as rnt

app = Flask(__name__)

line_bot_api = LineBotApi('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')
handler = WebhookHandler('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')

@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'


give=["1","2","3","4","5","6","7","8","9"]


check=None
count=1
take=[]
answer=[]     #初期状態
@handler.add(MessageEvent, message=TextMessage)
def handle_message1(event):
    global check       #変数を共通化
    global count
    print(count,check,"a")   #確認用のコマンド
    global take
    global answer
    if event.message.text=="@start":   #開始コマンド
        print(count,check,"b")
        check=None       #変数の初期化
        count=1
        take=[]
        answer=[]
        dice=rnt(0,8)
        take.append(give[dice])     #問題の記録
        print(count,check,"c")
        check=event.message.text     #問題実行中を示す
        print(count,check,"d")
        line_bot_api.reply_message(
            event.reply_token,
            [TextSendMessage(text="問題を開始します。\n 5問あります。1~3の数字で選んでください。"),
                TextSendMessage(text=give[dice])])
        print(count,check,"e")
    elif (event.message.text=="1" or event.message.text=="2" or event.message.text=="3") and check=="@start" and count<=5:
        answer.append(event.message.text)     #1,2,3のいずれかで回答
        print(count,check,"f")
        if count<5:     #残り問題数の確認
            dice=rnt(0,8)
            take.append(give[dice])
            print(count,check,"g")
            count=count+1
            print(count,check,"h")
            line_bot_api.reply_message(
                event.reply_token,
                TextSendMessage(text=give[dice]))    #2問目以降の出題
            print(count,check,"i")
        else:
            print(count,check,"j")    #最終5問目の確認
            count=count+1           #6問目を実行不可にする
            print(count,check,"k")
            line_bot_api.reply_message(
                    event.reply_token,
                    TextSendMessage(text="以上で問題は終了です。ありがとうございました。"))
            print("take=",take,"answer=",answer)        #問題終了、完成形はここでスプレッドシートに書き込み
            print(count,check,"l")
    
    elif event.message.text=="@undo":      #問題のやり直し
        print(count,check,"m")
        check=None     #変数の初期化
        count=1
        take=[]
        answer=[]
        print(count,check,"n")
        line_bot_api.reply_message(
                    event.reply_token,
                    TextSendMessage(text="問題を中断します。"))
        print(count,check,"o")



    elif check=="@start":     #1,2,3以外の回答が入力された場合の処理
        print(count,check,"p")
        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text="入力に誤りがあります。"))
        print(count,check,"q")
    else:
        print(count,check,"r")                 #問題が開始されるまでの処理
        print(count,check,"s")
        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text="@startで問題が開始されます。"))
        print(count,check,"t")



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

以上がapp.pyのコードです。 以下がまずngrokで実行したときのログです。

1 None a
1 None b
1 None c
1 @start d
1 @start e
127.0.0.1 - - [20/Dec/2021 20:46:13] "POST /callback HTTP/1.1" 200 –
1 @start a
1 @start f
1 @start g
2 @start h
2 @start i
127.0.0.1 - - [20/Dec/2021 20:46:30] "POST /callback HTTP/1.1" 200 –
2 @start a
2 @start f
2 @start g
3 @start h
3 @start i
127.0.0.1 - - [20/Dec/2021 20:46:47] "POST /callback HTTP/1.1" 200 –
3 @start a
3 @start f
3 @start g
4 @start h
4 @start i
127.0.0.1 - - [20/Dec/2021 20:47:03] "POST /callback HTTP/1.1" 200 –
4 @start a
4 @start f
4 @start g
5 @start h
5 @start i
127.0.0.1 - - [20/Dec/2021 20:47:18] "POST /callback HTTP/1.1" 200 –
5 @start a
5 @start f
5 @start j
6 @start k
take= ['7', '5', '9', '3', '1'] answer= ['1', '1', '1', '1', '2']
6 @start l
127.0.0.1 - - [20/Dec/2021 20:47:32] "POST /callback HTTP/1.1" 200 –

一方、次がherokuのログです。

2021-12-20T11:53:49.728573+00:00 app[web.1]: 1 None a
2021-12-20T11:53:49.728581+00:00 app[web.1]: 1 None b
2021-12-20T11:53:49.728622+00:00 app[web.1]: 1 None c
2021-12-20T11:53:49.728638+00:00 app[web.1]: 1 @start d
2021-12-20T11:53:49.956599+00:00 app[web.1]: 1 @start e
2021-12-20T11:53:49.956601+00:00 app[web.1]: 10.1.35.9 - - [20/Dec/2021:11:53:49 +0000] "POST /callback HTTP/1.1" 200 2 "-" "LineBotWebhook/2.0"
2021-12-20T11:53:49.953192+00:00 heroku[router]: at=info method=POST path="/callback" host=name.herokuapp.com request_id=XXXXXXXXXXXXXXXX fwd="Y.Y.Y.Y" dyno=web.1 connect=0ms service=226ms status=200 bytes=154 protocol=https
2021-12-20T11:54:00.570506+00:00 app[web.1]: 1 None a
2021-12-20T11:54:00.570522+00:00 app[web.1]: 1 None r
2021-12-20T11:54:00.570549+00:00 app[web.1]: 1 None s
2021-12-20T11:54:00.787538+00:00 app[web.1]: 1 None t
2021-12-20T11:54:00.788757+00:00 app[web.1]: 10.1.85.195 - - [20/Dec/2021:11:54:00 +0000] "POST /callback HTTP/1.1" 200 2 "-" "LineBotWebhook/2.0"
2021-12-20T11:54:00.789490+00:00 heroku[router]: at=info method=POST path="/callback" host=name.herokuapp.com request_id=XXXXXXXXXXXXXXXX fwd="Y.Y.Y.Y" dyno=web.1 connect=0ms service=219ms status=200 bytes=154 protocol=https
2021-12-20T11:54:07.810933+00:00 app[web.1]: 1 @start a
2021-12-20T11:54:07.810941+00:00 app[web.1]: 1 @start f
2021-12-20T11:54:07.810978+00:00 app[web.1]: 1 @start g
2021-12-20T11:54:07.810992+00:00 app[web.1]: 2 @start h
2021-12-20T11:54:08.036204+00:00 app[web.1]: 2 @start i
2021-12-20T11:54:08.037133+00:00 app[web.1]: 10.1.54.169 - - [20/Dec/2021:11:54:08 +0000] "POST /callback HTTP/1.1" 200 2 "-" "LineBotWebhook/2.0"
2021-12-20T11:54:08.036555+00:00 heroku[router]: at=info method=POST path="/callback" host=name.herokuapp.com request_id=XXXXXXXXXXXXXXXX fwd="Y.Y.Y.Y" dyno=web.1 connect=0ms service=227ms status=200 bytes=154 protocol=https
2021-12-20T11:54:13.516839+00:00 app[web.1]: 1 None t
2021-12-20T11:54:13.517685+00:00 app[web.1]: 10.1.39.131 - - [20/Dec/2021:11:54:13 +0000] "POST /callback HTTP/1.1" 200 2 "-" "LineBotWebhook/2.0"
2021-12-20T11:54:13.518030+00:00 heroku[router]: at=info method=POST path="/callback" host=name.herokuapp.com request_id=XXXXXXXXXXXXXXXX fwd="Y.Y.Y.Y" dyno=web.1 connect=0ms service=550ms status=200 bytes=154 protocol=https
2021-12-20T11:54:12.968476+00:00 app[web.1]: 1 None a
2021-12-20T11:54:12.968486+00:00 app[web.1]: 1 None r
2021-12-20T11:54:12.968510+00:00 app[web.1]: 1 None s
2021-12-20T11:54:15.843994+00:00 app[web.1]: 2 @start a
2021-12-20T11:54:15.844005+00:00 app[web.1]: 2 @start f
2021-12-20T11:54:15.844033+00:00 app[web.1]: 2 @start g
2021-12-20T11:54:15.844053+00:00 app[web.1]: 3 @start h
2021-12-20T11:54:16.072788+00:00 app[web.1]: 3 @start i
2021-12-20T11:54:16.073737+00:00 app[web.1]: 10.1.39.131 - - [20/Dec/2021:11:54:16 +0000] "POST /callback HTTP/1.1" 200 2 "-" "LineBotWebhook/2.0"
2021-12-20T11:54:16.074076+00:00 heroku[router]: at=info method=POST path="/callback" host=name.herokuapp.com request_id=XXXXXXXXXXXXXXXX fwd="Y.Y.Y.Y" dyno=web.1 connect=0ms service=230ms status=200 bytes=154 protocol=https
2021-12-20T11:54:19.112558+00:00 heroku[router]: at=info method=POST path="/callback" host=name.herokuapp.com request_id=XXXXXXXXXXXXXXXX fwd="Y.Y.Y.Y" dyno=web.1 connect=0ms service=411ms status=200 bytes=154 protocol=https
2021-12-20T11:54:18.702208+00:00 app[web.1]: 1 None a
2021-12-20T11:54:18.702217+00:00 app[web.1]: 1 None r
2021-12-20T11:54:18.702226+00:00 app[web.1]: 1 None s
2021-12-20T11:54:19.111369+00:00 app[web.1]: 1 None t
2021-12-20T11:54:19.112225+00:00 app[web.1]: 10.1.39.131 - - [20/Dec/2021:11:54:19 +0000] "POST /callback HTTP/1.1" 200 2 "-" "LineBotWebhook/2.0"
2021-12-20T11:54:21.612178+00:00 app[web.1]: 3 @start a
2021-12-20T11:54:21.612188+00:00 app[web.1]: 3 @start f
2021-12-20T11:54:21.612214+00:00 app[web.1]: 3 @start g
2021-12-20T11:54:21.612232+00:00 app[web.1]: 4 @start h
2021-12-20T11:54:21.918452+00:00 heroku[router]: at=info method=POST path="/callback" host=name.herokuapp.com request_id=XXXXXXXXXXXXXXXX fwd="Y.Y.Y.Y" dyno=web.1 connect=0ms service=307ms status=200 bytes=154 protocol=https
2021-12-20T11:54:21.917222+00:00 app[web.1]: 4 @start i
2021-12-20T11:54:21.918158+00:00 app[web.1]: 10.1.39.131 - - [20/Dec/2021:11:54:21 +0000] "POST /callback HTTP/1.1" 200 2 "-" "LineBotWebhook/2.0"
2021-12-20T11:54:24.406381+00:00 app[web.1]: 4 @start a
2021-12-20T11:54:24.406386+00:00 app[web.1]: 4 @start f
2021-12-20T11:54:24.406419+00:00 app[web.1]: 4 @start g
2021-12-20T11:54:24.406440+00:00 app[web.1]: 5 @start h
2021-12-20T11:54:24.613184+00:00 app[web.1]: 5 @start i
2021-12-20T11:54:24.614037+00:00 app[web.1]: 10.1.39.131 - - [20/Dec/2021:11:54:24 +0000] "POST /callback HTTP/1.1" 200 2 "-" "LineBotWebhook/2.0"
2021-12-20T11:54:24.614382+00:00 heroku[router]: at=info method=POST path="/callback" host=name.herokuapp.com request_id=XXXXXXXXXXXXXXXX fwd="Y.Y.Y.Y" dyno=web.1 connect=0ms service=208ms status=200 bytes=154 protocol=https
2021-12-20T11:54:29.074998+00:00 app[web.1]: 1 None a
2021-12-20T11:54:29.075007+00:00 app[web.1]: 1 None r
2021-12-20T11:54:29.075019+00:00 app[web.1]: 1 None s
2021-12-20T11:54:29.298898+00:00 app[web.1]: 1 None t
2021-12-20T11:54:29.299739+00:00 app[web.1]: 10.1.86.37 - - [20/Dec/2021:11:54:29 +0000] "POST /callback HTTP/1.1" 200 2 "-" "LineBotWebhook/2.0"
2021-12-20T11:54:29.300442+00:00 heroku[router]: at=info method=POST path="/callback" host=name.herokuapp.com request_id=XXXXXXXXXXXXXXXX fwd="Y.Y.Y.Y" dyno=web.1 connect=0ms service=226ms status=200 bytes=154 protocol=https
2021-12-20T11:54:31.785176+00:00 app[web.1]: 5 @start a
2021-12-20T11:54:31.785186+00:00 app[web.1]: 5 @start f
2021-12-20T11:54:31.785206+00:00 app[web.1]: 5 @start j
2021-12-20T11:54:31.785355+00:00 app[web.1]: 6 @start k
2021-12-20T11:54:31.993182+00:00 app[web.1]: take= ['7', '1', '2', '7', '3'] answer= ['3', '3', '1', '3', '2']
2021-12-20T11:54:31.993202+00:00 app[web.1]: 6 @start l
2021-12-20T11:54:31.994052+00:00 app[web.1]: 10.1.86.37 - - [20/Dec/2021:11:54:31 +0000] "POST /callback HTTP/1.1" 200 2 "-" "LineBotWebhook/2.0"
2021-12-20T11:54:31.993956+00:00 heroku[router]: at=info method=POST path="/callback" host=name.herokuapp.com request_id=XXXXXXXXXXXXXXXX fwd="Y.Y.Y.Y" dyno=web.1 connect=0ms service=210ms status=200 bytes=154 protocol=https

見てわかるとおり、一度check="@start"としたものが勝手にNoneに戻っていて、countも変則的な増え方をしています。 どうすればherokuあるいは他のpaasで正常に実行できるでしょうか。

バージョン情報: Windows 11 Home Python 3.8.12 64-bit('spyder4':conda) ngrok Windows(64-bit)

  • 0
  • 3
  • 1098
  • twitter facebook
	スプレッドシートにユーザーIDを記録することで無事正常に動きました。ありがとうございました。
  • 1

多分herokuのアプリケーションの方がマルチプロセスになってます。 herokuやngrok自体に問題があるというよりは、アプリケーションの設定だと思います。 スレッドの場合はグローバル変数を共有しますが、プロセスを跨ぐと共有されません。 gunicornを使っている場合は gunicorn server:app -w 1 --threads 2 のようにwオプションに1を渡せば良いです。

  • 0

ローカルで動かしている分にはcountcheckの変数の中身はたしかに保証されるかもしれませんが、herokuにデプロイするとサーバーは手元じゃなくなり不特定多数の人がアクセスできる状態になるので、countcheckの変数をサーバーで管理するのには無理があります。 やるのであればユーザーごとに countcheckをデータベースに記録していくやり方が普通です。 スプレッドシートのデータを読み込むことが出来ているのであればスプレッドシートに countcheckをユーザーごとに記録していくというやり方でもありだと思います

ついでに言っておくと、変数の担保の問題もあるのでWebアプリでglobal変数は極力使わないようにしましょう(アプリの設計次第ですが)。

  • 0

Similar posts

    No similar posts

Are you sure? question.vm