Некорректно работает бот (python-telegram-bot)
Создала бота используя python-telegram-bot. Вот код:
import os
import time
import sys
import logging
from telegram import Update, ReplyKeyboardRemove, InlineKeyboardButton, InlineKeyboardMarkup, ReplyKeyboardMarkup, KeyboardButton
from telegram.ext import Updater, MessageHandler, CommandHandler, Filters, CallbackContext, CallbackQueryHandler, ConversationHandler
from telegram.ext.dispatcher import run_async
TOKEN = ''
log = get_logger(__file__)
def facts_to_str(user_data):
facts = list()
for key, value in user_data.items():
facts.append('{} - {}'.format(key, value))
return "\n".join(facts).join(['\n', '\n'])
user_dict = {}
@run_async
@log_func(log)
def start(update, context):
user_id = update.message.from_user.id
print('User ID: {} '.format(user_id))
keyboard = [[InlineKeyboardButton("О мастере ??", callback_data='master'),
InlineKeyboardButton("Контакты ☎️", callback_data='contacts'),
InlineKeyboardButton("Виды массажа", callback_data='types_massage')],
[InlineKeyboardButton("Записаться", callback_data='sing_up'),
InlineKeyboardButton("Отменить запись", callback_data='cancel')]]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text('''Здравствуйте!
Я личный помощник Вашего мастера.
C моей помощью Вы можете узнать о видах массажа, записаться на сеанс или отменить свою запись.
С чего начнём? ⬇️''', reply_markup=reply_markup)
@run_async
@log_func(log)
def sing(update, context):
query = update.callback_query
query.answer()
keyboard = [[InlineKeyboardButton("Классический", callback_data='klass'),
InlineKeyboardButton("Лечебный", callback_data='lech'),
InlineKeyboardButton("Медовый", callback_data='med')],
[InlineKeyboardButton("Лимфодренажный", callback_data='limfo'),
InlineKeyboardButton("Антицеллюлитный", callback_data='anti')],
[InlineKeyboardButton("? Главное меню", callback_data='home')]]
reply_markup = InlineKeyboardMarkup(keyboard)
query.edit_message_text(text='Выберите вид массажа: ⬇️', reply_markup=reply_markup)
@run_async
@log_func(log)
def klassik(update, context):
user_data = context.user_data
category = 'Вид массажа'
massage = 'Классический'
user_data[category] = massage
query = update.callback_query
query.answer()
query.edit_message_text("Выберите дату: ", reply_markup=telegramcalendar.create_calendar())
@run_async
@log_func(log)
def lechebny(update, context):
user_data = context.user_data
category = 'Вид массажа'
massage = 'Лечебный'
user_data[category] = massage
query = update.callback_query
query.answer()
query.edit_message_text("Выберите дату: ", reply_markup=telegramcalendar.create_calendar())
@run_async
@log_func(log)
def medovy(update, context):
user_data = context.user_data
category = 'Вид массажа'
massage = 'Медовый'
user_data[category] = massage
query = update.callback_query
query.answer()
query.edit_message_text("Выберите дату: ", reply_markup=telegramcalendar.create_calendar())
@run_async
@log_func(log)
def limfo(update, context):
user_data = context.user_data
category = 'Вид массажа'
massage = 'Лимфодренажный'
user_data[category] = massage
query = update.callback_query
query.answer()
query.edit_message_text("Выберите дату: ", reply_markup=telegramcalendar.create_calendar())
@run_async
@log_func(log)
def anti(update, context):
user_data = context.user_data
category = 'Вид массажа'
massage = 'Антицеллюлитный'
user_data[category] = massage
query = update.callback_query
query.answer()
query.edit_message_text("Выберите дату: ", reply_markup=telegramcalendar.create_calendar())
@run_async
@log_func(log)
def select(update: Update, context: CallbackContext):
query = update.callback_query
query.answer()
bot = context.bot
selected, date = telegramcalendar.process_calendar_selection(bot, update)
if selected:
user_data = context.user_data
category = 'Дата'
text = date.strftime("%d/%m/%Y")
user_data[category] = text
keyboard = [[InlineKeyboardButton("12:00", callback_data='12'),
InlineKeyboardButton("14:30", callback_data='14'),
InlineKeyboardButton("16:00", callback_data='16')]]
reply_markup = InlineKeyboardMarkup(keyboard)
query.message.reply_text(text='''Вы выбрали %s
Выберите свободное время: ''' % (date.strftime("%d/%m/%Y")), reply_markup=reply_markup)
@run_async
@log_func(log)
def time_12(update: Update, context: CallbackContext):
user_data = context.user_data
category = 'Время'
text = '12:00'
user_data[category] = text
query = update.callback_query
query.answer()
query.edit_message_text('''Введите своё *Имя и Фамилию*''', parse_mode='Markdown')
@run_async
@log_func(log)
def time_14(update: Update, context: CallbackContext):
user_data = context.user_data
category = 'Время'
text = '14:30'
user_data[category] = text
query = update.callback_query
query.answer()
query.edit_message_text('''Введите своё *Имя и Фамилию*''', parse_mode='Markdown')
@run_async
@log_func(log)
def time_16(update: Update, context: CallbackContext):
user_data = context.user_data
category = 'Время'
text = '16:00'
user_data[category] = text
query = update.callback_query
query.answer()
query.edit_message_text('''Введите своё *Имя и Фамилию*''', parse_mode='Markdown')
@run_async
@log_func(log)
def sing_name(update: Update, context: CallbackContext):
global user_name
user_data = context.user_data
category = 'Имя Фамилия'
user_name = update.message.text
user_data[category] = user_name
print('Сохранено имя: ' + user_name)
contact_keyboard = KeyboardButton('Отправить номер', request_contact=True)
custom_keyboard = [[contact_keyboard]]
markup = ReplyKeyboardMarkup(custom_keyboard, resize_keyboard=True, one_time_keyboard=True)
update.message.reply_text('''*Введите свой номер телефона*''', parse_mode='Markdown', reply_markup=markup)
@run_async
@log_func(log)
def sing_contact(update: Update, context: CallbackContext):
global phone
user_data = context.user_data
category = 'Телефон'
phone = update.message.text or update.message.contact.phone_number
user_data[category] = phone
print('Сохранен номер: ' + phone)
keyboard = [[InlineKeyboardButton("Подтвердить", callback_data='okay'),
InlineKeyboardButton("Изменить запись", callback_data='recording')]]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text('''*Вы зарегестрированы!*
_Ваши данные:_
{}'''.format(facts_to_str(user_data)), parse_mode='Markdown', reply_markup=reply_markup)
@run_async
@log_func(log)
def okay(update: Update, context: CallbackContext):
query = update.callback_query
query.answer()
query.edit_message_text('''*Спасибо за регестрацию!*
Мастер будет ожидать Вас.''', parse_mode='Markdown')
@run_async
@log_func(log)
def recording(update: Update, context: CallbackContext):
sing(update, context)
def error(update, context):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, context.error)
def main():
# Create the EventHandler and pass it your bot's token.
updater = Updater(
token=TOKEN,
use_context=True
)
# Get the dispatcher to register handlers
dp = updater.dispatcher
dp.add_handler(CommandHandler('start', start))
dp.add_handler(CallbackQueryHandler(sing, pattern='sing_up'))
dp.add_handler(CallbackQueryHandler(klassik, pattern='klass'))
dp.add_handler(CallbackQueryHandler(lechebny, pattern='lech'))
dp.add_handler(CallbackQueryHandler(medovy, pattern='med'))
dp.add_handler(CallbackQueryHandler(limfo, pattern='limfo'))
dp.add_handler(CallbackQueryHandler(anti, pattern='anti'))
dp.add_handler(CallbackQueryHandler(time_12, pattern='12'))
dp.add_handler(CallbackQueryHandler(time_14, pattern='14'))
dp.add_handler(CallbackQueryHandler(time_16, pattern='16'))
dp.add_handler(CallbackQueryHandler(okay, pattern='okay'))
dp.add_handler(CallbackQueryHandler(recording, pattern='recording'))
dp.add_handler(CallbackQueryHandler(select))
dp.add_handler(MessageHandler(Filters.text, sing_name))
dp.add_handler(MessageHandler(Filters.text | Filters.contact, sing_contact))
dp.add_error_handler(error)
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()
Проблема заключается в том, что когда пользователь доходит до функции sing_contact, у него есть два варианта: ввести свой номер вручную или оставить с помощью кнопки. Когда пользователь оставляет номер с помощью кнопки, всё работает и сохраняется корректно. Но если решат ввести номер вручную то, почему-то, срабатывает функция sing_name и номер сохраняется как Имя и Фамилия пользователя.
Подскажите, пожалуйста, в чём ошибка?
Ответы (2 шт):
Чтобы попроще - все команды обрабатываются сверху вниз. Т.е. sing_name и sing_contact обрабатывают одни и те же аргументы, но именно текст из сообщения обрабатывает первой sing_name. Используйте декораторы или проверяйте ввод на соответствие только цифрам (или в каком формате вы запрашиваете номер).
Конкретные строки в вашем коде:
def sing_name(update: Update, context: CallbackContext):
...
user_name = update.message.text
def sing_contact(update: Update, context: CallbackContext):
...
phone = update.message.text or update.message.contact.phone_number
UPDATE. По мотивам информации в комментариях
Нужны изменения в хендлерах:
dp.add_handler(MessageHandler(Filters.text, sing_name))
dp.add_handler(MessageHandler(Filters.text | Filters.contact, sing_contact))
Первый перехватывает все текстовые сообщения, так что лучше добавить проверку на ввод цифр во второй и поменять их местами, чтобы второй обрабатывался раньше
Взял за основу код этого вопроса и вашего предыдущего.
Не работал с сложными ConversationHandler, поэтому были шероховатости. Рекомендую поразбираться с примером сложных меню из библиотеки (сам хочу на днях выделить время и поразбираться, т.к. было: сложно и много непонятных моментов).
Порядок начала выполнения с ботом:
/start- Клик на кнопку Записаться
import sys
from telegram import (
Update, ReplyKeyboardRemove, InlineKeyboardButton, InlineKeyboardMarkup, ReplyKeyboardMarkup, KeyboardButton
)
from telegram.ext import (
Updater, MessageHandler, CommandHandler, Filters, CallbackContext, CallbackQueryHandler, ConversationHandler
)
from telegram.ext.dispatcher import run_async
from config import TOKEN
from common import get_logger, log_func
import telegramcalendar
log = get_logger(__file__)
# TODO: enum
STATE_SELECT_MASSAGE = 'STATE_SELECT_MASSAGE'
STATE_SELECT_DATE = 'STATE_SELECT_DATE'
STATE_SELECT_TIME = 'STATE_SELECT_TIME'
STATE_SELECT_USER = 'STATE_SELECT_USER'
STATE_SELECT_PHONE = 'STATE_SELECT_PHONE'
STATE_FINISH = 'STATE_FINISH'
def facts_to_str(user_data: dict) -> str:
facts = []
for key, value in user_data.items():
facts.append('{} - {}'.format(key, value))
return "\n".join(facts).join(['\n', '\n'])
@run_async
@log_func(log)
def on_main_menu(update: Update, context: CallbackContext):
# Если функция вызывана из CallbackQueryHandler
query = update.callback_query
if query:
query.answer()
message = update.effective_message
user_id = update.effective_user.id
print('User ID: {} '.format(user_id))
keyboard = [
[
InlineKeyboardButton("О мастере ??", callback_data='master'),
InlineKeyboardButton("Контакты ☎️", callback_data='contacts'),
InlineKeyboardButton("Виды массажа", callback_data='types_massage')
],
[
InlineKeyboardButton("Записаться", callback_data='sing_up'),
InlineKeyboardButton("Отменить запись", callback_data='cancel')
]
]
reply_markup = InlineKeyboardMarkup(keyboard)
text = '''Здравствуйте!
Я личный помощник Вашего мастера.
C моей помощью Вы можете узнать о видах массажа, записаться на сеанс или отменить свою запись.
С чего начнём? ⬇️'''
if query:
message.edit_text(text, reply_markup=reply_markup)
else:
message.reply_text(text, reply_markup=reply_markup)
@run_async
@log_func(log)
def on_sing_up(update: Update, context: CallbackContext):
query = update.callback_query
query.answer()
keyboard = [
[
InlineKeyboardButton("Классический", callback_data='klass'),
InlineKeyboardButton("Лечебный", callback_data='lech'),
InlineKeyboardButton("Медовый", callback_data='med')
],
[
InlineKeyboardButton("Лимфодренажный", callback_data='limfo'),
InlineKeyboardButton("Антицеллюлитный", callback_data='anti')
],
[InlineKeyboardButton("? Главное меню", callback_data='main_menu')]
]
reply_markup = InlineKeyboardMarkup(keyboard)
query.edit_message_text(text='Выберите вид массажа: ⬇️', reply_markup=reply_markup)
return STATE_SELECT_MASSAGE
@run_async
@log_func(log)
def on_massage_klassik(update: Update, context: CallbackContext):
user_data = context.user_data
category = 'Вид массажа'
massage = 'Классический'
user_data[category] = massage
query = update.callback_query
query.answer()
query.edit_message_text("Выберите дату: ", reply_markup=telegramcalendar.create_calendar())
return STATE_SELECT_DATE
@run_async
@log_func(log)
def on_massage_lechebny(update: Update, context: CallbackContext):
user_data = context.user_data
category = 'Вид массажа'
massage = 'Лечебный'
user_data[category] = massage
query = update.callback_query
query.answer()
query.edit_message_text("Выберите дату: ", reply_markup=telegramcalendar.create_calendar())
return STATE_SELECT_DATE
@run_async
@log_func(log)
def on_massage_medovy(update: Update, context: CallbackContext):
user_data = context.user_data
category = 'Вид массажа'
massage = 'Медовый'
user_data[category] = massage
query = update.callback_query
query.answer()
query.edit_message_text("Выберите дату: ", reply_markup=telegramcalendar.create_calendar())
return STATE_SELECT_DATE
@run_async
@log_func(log)
def on_massage_limfo(update: Update, context: CallbackContext):
user_data = context.user_data
category = 'Вид массажа'
massage = 'Лимфодренажный'
user_data[category] = massage
query = update.callback_query
query.answer()
query.edit_message_text("Выберите дату: ", reply_markup=telegramcalendar.create_calendar())
return STATE_SELECT_DATE
@run_async
@log_func(log)
def on_massage_anti(update: Update, context: CallbackContext):
user_data = context.user_data
category = 'Вид массажа'
massage = 'Антицеллюлитный'
user_data[category] = massage
query = update.callback_query
query.answer()
query.edit_message_text("Выберите дату: ", reply_markup=telegramcalendar.create_calendar())
return STATE_SELECT_DATE
@run_async
@log_func(log)
def on_select_date(update: Update, context: CallbackContext):
query = update.callback_query
query.answer()
bot = context.bot
selected, date = telegramcalendar.process_calendar_selection(bot, update)
if not selected:
return ConversationHandler.END
user_data = context.user_data
text = date.strftime("%d/%m/%Y")
user_data['Дата'] = text
keyboard = [[
InlineKeyboardButton("12:00", callback_data='12'),
InlineKeyboardButton("14:30", callback_data='14'),
InlineKeyboardButton("16:00", callback_data='16')
]]
reply_markup = InlineKeyboardMarkup(keyboard)
query.message.reply_text(text='''Вы выбрали %s
Выберите свободное время: ''' % text, reply_markup=reply_markup)
return STATE_SELECT_TIME
@run_async
@log_func(log)
def on_time_12(update: Update, context: CallbackContext):
user_data = context.user_data
text = '12:00'
user_data['Время'] = text
query = update.callback_query
query.answer()
query.edit_message_text('''Введите своё *Имя и Фамилию*''', parse_mode='Markdown')
return STATE_SELECT_USER
@run_async
@log_func(log)
def on_time_14(update: Update, context: CallbackContext):
user_data = context.user_data
text = '14:30'
user_data['Время'] = text
query = update.callback_query
query.answer()
query.edit_message_text('''Введите своё *Имя и Фамилию*''', parse_mode='Markdown')
return STATE_SELECT_USER
@run_async
@log_func(log)
def on_time_16(update: Update, context: CallbackContext):
user_data = context.user_data
text = '16:00'
user_data['Время'] = text
query = update.callback_query
query.answer()
query.edit_message_text('''Введите своё *Имя и Фамилию*''', parse_mode='Markdown')
return STATE_SELECT_USER
@run_async
@log_func(log)
def on_sing_name(update: Update, context: CallbackContext):
user_data = context.user_data
category = 'Имя Фамилия'
user_name = update.message.text
user_data[category] = user_name
print('Сохранено имя: ' + user_name)
contact_keyboard = KeyboardButton('Отправить номер', request_contact=True)
custom_keyboard = [[contact_keyboard]]
markup = ReplyKeyboardMarkup(custom_keyboard, resize_keyboard=True, one_time_keyboard=True)
update.message.reply_text('''*Введите свой номер телефона*''', parse_mode='Markdown', reply_markup=markup)
return STATE_SELECT_PHONE
@run_async
@log_func(log)
def on_sing_contact(update: Update, context: CallbackContext):
user_data = context.user_data
category = 'Телефон'
phone = update.message.text or update.message.contact.phone_number
user_data[category] = phone
print('Сохранен номер: ' + phone)
keyboard = [[
InlineKeyboardButton("Подтвердить", callback_data='okay'),
InlineKeyboardButton("Изменить запись", callback_data='recording')
]]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text('''*Вы зарегистрированы!*
_Ваши данные:_
{}'''.format(facts_to_str(user_data)), parse_mode='Markdown', reply_markup=reply_markup)
return STATE_FINISH
@run_async
@log_func(log)
def on_finish(update: Update, context: CallbackContext):
query = update.callback_query
query.answer()
query.edit_message_text('''*Спасибо за регистрацию!*
Мастер будет ожидать Вас.''', parse_mode='Markdown')
return ConversationHandler.END
@run_async
@log_func(log)
def on_recording(update: Update, context: CallbackContext):
return on_sing_up(update, context)
def on_error(update, context):
"""Log Errors caused by Updates."""
log.warning('Update "%s" caused error "%s"', update, context.error)
def main():
# Create the EventHandler and pass it your bot's token.
updater = Updater(
token=TOKEN,
use_context=True
)
# Get the dispatcher to register handlers
dp = updater.dispatcher
dp.add_handler(CommandHandler('start', on_main_menu))
dp.add_handler(ConversationHandler(
entry_points=[
CommandHandler('start', on_main_menu),
CallbackQueryHandler(on_sing_up, pattern='sing_up')
],
states={
STATE_SELECT_MASSAGE: [
CallbackQueryHandler(on_massage_klassik, pattern='klass'),
CallbackQueryHandler(on_massage_lechebny, pattern='lech'),
CallbackQueryHandler(on_massage_medovy, pattern='med'),
CallbackQueryHandler(on_massage_limfo, pattern='limfo'),
CallbackQueryHandler(on_massage_anti, pattern='anti'),
CallbackQueryHandler(on_main_menu, pattern='main_menu'),
],
STATE_SELECT_DATE: [
CallbackQueryHandler(on_select_date)
],
STATE_SELECT_TIME: [
CallbackQueryHandler(on_time_12, pattern='12'),
CallbackQueryHandler(on_time_14, pattern='14'),
CallbackQueryHandler(on_time_16, pattern='16')
],
STATE_SELECT_USER: [
MessageHandler(Filters.text, on_sing_name)
],
STATE_SELECT_PHONE: [
MessageHandler(Filters.text | Filters.contact, on_sing_contact)
],
STATE_FINISH: [
CallbackQueryHandler(on_finish, pattern='okay'),
CallbackQueryHandler(on_recording, pattern='recording')
]
},
fallbacks=[
],
# allow_reentry=True,
# per_message=True,
))
dp.add_error_handler(on_error)
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()