質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
LINE Messaging API

LINE Messaging APIは、メッセージの送信・返信ができるAPIです。Web APIを経由しアプリケーションサーバとLINEのAPIでやり取りが可能。複数のメッセージタイプや分かりやすいAPIリファレンスを持ち、グループチャットにも対応しています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

1612閲覧

【Python】LINE Messaging APIにおける多重ループの抜け方

poker

総合スコア14

LINE Messaging API

LINE Messaging APIは、メッセージの送信・返信ができるAPIです。Web APIを経由しアプリケーションサーバとLINEのAPIでやり取りが可能。複数のメッセージタイプや分かりやすいAPIリファレンスを持ち、グループチャットにも対応しています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

1クリップ

投稿2018/01/13 14:11

編集2018/01/15 08:37

LINE Messaging APIによる多重ループから抜ける方法がわかりません。

作りたいものは占いbotのようなものです。
流れは

①「bot:「占い」と入力してください。」
|      ↓   ↑
|      ユーザーが占い以外の言葉を入力。

②「bot:性別を入力してください。」
|      ↓   ↑
|      ユーザーが男・女・その他以外の言葉を入力。

③「bot:誕生日を入力してください。」
|      ↓   ↑
|      ユーザーが4桁の数字以外を入力。

④「bot:血液型を入力してください。」
|      ↓   ↑
|      ユーザーがA,B,AB,O以外の言葉を入力。

⑤「bot:あなたはタイプAです。」

上記のようなものになります。
②〜④において、『終了』と入力したら一気に①に戻るプログラムにしたいのですが、うまくいきません。

python

1(途中からです。) 2@handler.add(MessageEvent, message=TextMessage) 3def handle_text_message(event): 4 text = event.message.text 5 if text == '占い': 6 line_bot_api.reply_message( 7 event.reply_token, TextSendMessage(text='占いスタート。\n性別を教えてください。')) 8 @handler.add(MessageEvent, message=TextMessage) 9 def handle_text_message(event): 10 text = event.message.text 11 if text in ["男","女"]: 12 line_bot_api.reply_message( 13 event.reply_token, TextSendMessage(text='誕生日を教えてください。\n(例:7月7日は「0707」と入力。)')) 14 @handler.add(MessageEvent, message=TextMessage) 15 def handle_text_message(event): 16 text = event.message.text 17 if 0100 < int(text) < 1232: 18 line_bot_api.reply_message( 19 event.reply_token, TextSendMessage(text='血液型を教えてね。')) 20 elif text == '終了': 21 line_bot_api.reply_message( 22 event.reply_token, TextSendMessage("終了します。")) 23 break #ここがエラーの出ている箇所です。 24 else: 25 line_bot_api.reply_message( 26 event.reply_token, TextSendMessage('誕生日を教えて。')) 27 elif text == '終了': 28 line_bot_api.reply_message( 29 event.reply_token, TextSendMessage("終了します。")) 30 break 31 else: 32 line_bot_api.reply_message( 33 event.reply_token, TextSendMessage('性別を教えてください。')) 34 else: 35 line_bot_api.reply_message( 36 event.reply_token, TextSendMessage('占いと入力してね。')) 37 38(省略)

④まで上記のように書きました。

下記のようなエラーメッセージがでます。

File "〜main.py", line 108 break ^ SyntaxError: 'break' outside loop

初歩的な質問で大変申し訳ありません。
よろしくお願いします。

追記:コード全文記載します。
終了とした関数から抜け出せない状態です。
よろしくお願いします。

python

1# -*- coding: utf-8 -*- 2 3# Licensed under the Apache License, Version 2.0 (the "License"); you may 4# not use this file except in compliance with the License. You may obtain 5# a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations 13# under the License. 14 15from __future__ import unicode_literals 16 17import errno 18import os 19import sys 20import tempfile 21import requests 22import random 23import datetime 24from argparse import ArgumentParser 25 26from flask import Flask, request, abort 27 28from linebot import ( 29 LineBotApi, WebhookHandler 30) 31from linebot.exceptions import ( 32 InvalidSignatureError 33) 34from linebot.models import ( 35 MessageEvent, TextMessage, TextSendMessage, 36 SourceUser, SourceGroup, SourceRoom, 37 TemplateSendMessage, ConfirmTemplate, MessageTemplateAction, 38 ButtonsTemplate, ImageCarouselTemplate, ImageCarouselColumn, URITemplateAction, 39 PostbackTemplateAction, DatetimePickerTemplateAction, 40 CarouselTemplate, CarouselColumn, PostbackEvent, 41 StickerMessage, StickerSendMessage, LocationMessage, LocationSendMessage, 42 ImageMessage, VideoMessage, AudioMessage, FileMessage, 43 UnfollowEvent, FollowEvent, JoinEvent, LeaveEvent, BeaconEvent, 44 ImageSendMessage 45) 46 47app = Flask(__name__) 48 49# get channel_secret and channel_access_token from your environment variable 50channel_secret = os.getenv('LINE_CHANNEL_SECRET', '省略') 51channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', '省略') 52 53line_bot_api = LineBotApi(channel_access_token) 54handler = WebhookHandler(channel_secret) 55 56static_tmp_path = os.path.join(os.path.dirname(__file__), 'static', 'tmp') 57 58# function for create tmp dir for download content 59def make_static_tmp_dir(): 60 try: 61 os.makedirs(static_tmp_path) 62 except OSError as exc: 63 if exc.errno == errno.EEXIST and os.path.isdir(static_tmp_path): 64 pass 65 else: 66 raise 67 68 69@app.route("/callback", methods=['POST']) 70def callback(): 71 # get X-Line-Signature header value 72 signature = request.headers['X-Line-Signature'] 73 74 # get request body as text 75 body = request.get_data(as_text=True) 76 app.logger.info("Request body: " + body) 77 78 # handle webhook body 79 try: 80 handler.handle(body, signature) 81 except InvalidSignatureError: 82 abort(400) 83 84 return 'OK' 85 86@handler.add(MessageEvent, message=TextMessage) 87def handle_text_message(event): 88 text = event.message.text 89 if text == '占い': 90 line_bot_api.reply_message( 91 event.reply_token, TextSendMessage(text='占いスタート。\n性別を教えてください。')) 92 @handler.add(MessageEvent, message=TextMessage) 93 def handle_text_message(event): 94 text = event.message.text 95 if text in ["男","女"]: 96 line_bot_api.reply_message( 97 event.reply_token, TextSendMessage(text='誕生日を教えてください。\n(例:7月7日は「0707」と入力。)')) 98 @handler.add(MessageEvent, message=TextMessage) 99 def handle_text_message(event): 100 text = event.message.text 101 if 100 < int(text) < 1232: 102 line_bot_api.reply_message( 103 event.reply_token, TextSendMessage(text='血液型を教えてね。')) 104 @handler.add(MessageEvent, message=TextMessage) 105 def handle_text_message(event): 106 text = event.message.text 107 if text in ["A","B","AB","O"]: 108 line_bot_api.reply_message( 109 event.reply_token, TextSendMessage(text='あなたはタイプAだよ。')) 110 elif text == '終了': 111 line_bot_api.reply_message( 112 event.reply_token, TextSendMessage("終了します。")) 113 return 114 else: 115 line_bot_api.reply_message( 116 event.reply_token, TextSendMessage('血液型を教えて。')) 117 elif text == '終了': 118 line_bot_api.reply_message( 119 event.reply_token, TextSendMessage("終了します。")) 120 return 121 else: 122 line_bot_api.reply_message( 123 event.reply_token, TextSendMessage('誕生日を教えて。')) 124 elif text == '終了': 125 line_bot_api.reply_message( 126 event.reply_token, TextSendMessage("終了します。")) 127 return 128 else: 129 line_bot_api.reply_message( 130 event.reply_token, TextSendMessage('性別を教えてください。')) 131 else: 132 line_bot_api.reply_message( 133 event.reply_token, TextSendMessage('占いと入力してね。')) 134 135 136if __name__ == "__main__": 137 arg_parser = ArgumentParser( 138 usage='Usage: python ' + __file__ + ' [--port <port>] [--help]' 139 ) 140 arg_parser.add_argument('-p', '--port', type=int, default=5000, help='port') 141 arg_parser.add_argument('-d', '--debug', default=False, help='debug') 142 options = arg_parser.parse_args() 143 144 # create tmp dir for download content 145 make_static_tmp_dir() 146 147 app.run(debug=options.debug, port=options.port) 148 149

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Udomomo

2018/01/14 01:36

②以降のソースコードを見ないとなんともいえません。そちらも記載していただけるでしょうか?
poker

2018/01/14 12:24

②以降がエラーが出て、わからない状態です^^;
Udomomo

2018/01/14 12:31

だからこそ②以降のソースコードを見ることが必要です。起きている症状だけで原因を見抜くことはできません。症状が同じでも、どこに原因があるかは様々な可能性がありえるからです。
poker

2018/01/15 03:29 編集

試行錯誤して、なんとか動くものができましたが載せたコードで大丈夫でしょうか?また、新たに問題が発生しましたのでご教授いただければと思います。よろしくお願いします。
Udomomo

2018/01/15 07:12

なんとか動くというのは、今は「終了」と入力しなければ①〜⑤まで全て正しく動くという状態でしょうか?
poker

2018/01/15 07:17

はい。ただループから抜けられず、⑤まで行き着いた後に④に戻る状態です。(⑤で結果が出た後で何か入力すると⑤の結果が出る。⑤の後に④に戻ってしまいます。)⑤まで行った後や途中で終了を入力した時にループを抜ける方法または①に戻る方法を知りたいです。
guest

回答1

0

ベストアンサー

breakforwhileなどループするブロックでのみ使えます。ifの中では使えません。
関数から抜けるにはreturnが使えます。returnとだけ書くと、何も返さずに関数から抜けることができます。
returnするとその時点で関数から抜けるので、例えば

python

1def func(a): 2 if a == 0: 3 return 4 5 return a + 1

とすると、a=0だった場合、何も返されずに関数から抜けます。a+1は実行されません。

⑤から抜けられない件は、⑤の結果を出す部分のソースコードがないのでわかりません。

-- 追記 --
handlerがいくつもあり、テキストを入力するとどのhandlerが呼ばれるかわからないのが、変な動作の原因になっている気がします。
以下のように直すと、handlerが1つになります。

python

1@handler.add(MessageEvent, message=TextMessage) 2def handle_text_message(event): 3 text = event.message.text 4 if text == '占い': 5 # メッセージを返す処理 6 elif text in ['男', '女']: 7 # メッセージを返す処理 8 ... 9 elif text in ['A', 'B', 'O', 'AB']: 10 # 結果を返す処理 11 else: 12 # その他の入力があったときのエラーメッセージを返す処理

このとき、最初にいきなり「男」と入力しても、占いのフローを無視して「誕生日を教えてください」と返されますが、
フローに依存せずに適当に結果を返してもよければ問題ないでしょう。
botは本来ステートレス(以前の会話を覚えていないこと)であるという想定で作られているので、botに複雑な処理を求めるとコードも複雑になってしまいます。

もし、ステートフル(今までの会話のやり取りを覚えており、それをふまえて会話すること)なサービスにしたいのであれば、bot単体だけでは厳しいです。

少しコーディングが難しくなってもよければ、結果を出す用のスクリプトとデータベースを用意する方がよいと思います。
例えば、以下のものを用意します。
・受付コード:ユーザと占い師を仲介し、それぞれと情報をやり取りするだけ
・占い師コード:受付からユーザの入力をもらい、占いをするだけ
・データベース:ユーザの入力を記憶しておくだけ

例として、③と④の流れは以下のようにできます。

③「0101」と入力
・受付は占い師に「0101」を伝え、「血液型を教えてください」と返す
・占い師はデータベースに「0101」を入れておく。

④「A」と入力
・受付は占い師に「A」を伝え、結果を判定するよう伝える
・占い師はデータベースに「A」を入れる
・占い師コードの結果判定用の関数が動く。データベースからそのユーザの情報を全て取り出し、必要なものが全てあれば結果を判定し、受付に伝える。
・受付はユーザに結果を伝える

こうすると、受付コードはユーザとの会話の流れを覚えておく必要がありません。実際に占っているのは背後の占い師コードなので、botがステートレスでも、ユーザからはbotが占ってるように見えます。
また、このように役割を分担すると、将来機能を追加したりメンテナンスしたりするときに、どこをいじればよいかわかりやすくなり、コードが複雑になりにくいです。

もしこういう実装を自力でやるのは難しすぎると思うのであれば、コーディングなしでノンプログラマーでもステートフルなLine botを作れるツールが世の中にあるので、それを使うと良いと思います。(「チャットボットツール」で検索するといろいろ出てきます。ものによっては個人向け無料プランもあります)

投稿2018/01/15 08:01

編集2018/01/15 08:48
Udomomo

総合スコア1524

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

poker

2018/01/15 08:12

return をコードの break #ここがエラーの出ている箇所です。(③の動作中のコード) に入れて実行したところ、エラーもなく動いたのですが③で「終了」と入力すると「終了します。」と出力されますが、適当な文字を入力すると'誕生日を教えて。' と出力されるため、③の関数から抜けられていないようです。
poker

2018/01/15 08:30

コード全文記載しました。よろしくお願いします!
Udomomo

2018/01/15 08:47

コードの構成全体を直した方がいいかもしれません。回答に追記しました。
poker

2018/01/15 08:56

回答ありがとうございます! そうなんですね…。elifで分岐させるやり方で一度試してみようかと思います! ありがとうございました!
poker

2018/01/15 09:07

elifに直して構築したところ、 if 0100 < int(text) < 1232:の部分でエラーがでてしまいました^^; 今回のbotの作成はこのやり方では難しそうなので、結果を出す用のスクリプトとデータベースを用意する方法を勉強してみたいと思います。
Udomomo

2018/01/15 09:15

おっとすみません。textが占いでも男・女でもない場合にそこの条件分岐に行くのですが、そのときtextが4ケタ数字でない場合にint型に変換できず、エラーになっているんだと思います。 ご自身でコーディングを続けるなら、例えばtext.isdigit()などの条件を追加する必要があります。これは文字列中に数値しかない場合にTrueになります。
poker

2018/01/15 10:02

ありがとうございます!条件式にtext.isdigit()==Trueを入れて動きました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問