Как предотвратить обработку inline кнопок после изменения сообщения в Telebot?
У меня есть 2 функции, обе из которых редактируют сообщение бота (telebot), 1 из них при последующем вызове другой должна не дать ей отредактировать сообщение
Пользователь может написав "Поиск" - получить сообщение вместе с inline клавиатурой, но при этом вызвав повторный раз "Поиск" предыдущее сообщение с клавиатурой должно измениться на сообщение "Клавиатура более недоступна" и лишиться клавиатуры. Используя inline клавиатуру пользователь тоже меняет сообщение и генерирует новую клавиатуру этому же сообщению (т.к. он должен ввести с помощью нее несколько параметров). Но может быть ситуация, когда пользователь после отправления слова "Поиск" до изменения сообщения нажмет на inline кнопку - в этом случае я хочу, чтобы сообщение тем не менее не выполняло эту часть кода в @bot.callback_query_handler()
:
global users_keyboard_data
users_keyboard_data = change_to_other_inline_keyboard(bot, call, users_keyboard_data)
То есть должна сработать только эта часть(т.к. она первой была вызвана) в @bot.message_handler()
:
if message.text == "Поиск":
global users_keyboard_data
users_keyboard_data = search(bot, message.chat.id, users_keyboard_data)
Я скопировал не весь код, а главные работоспособные фрагменты. Также хочу уточнить, что глобальная переменная users_keyboard_data
отвечает за хранение полученных от пользователя с inline клавиатуры параметров, если есть способ для хранения легче - подскажите пожалуйста.
from telebot import TeleBot
from telebot.types import Message, CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup
from settings import config
users_keyboard_data = {}
bot = TeleBot(config.TOKEN)
def build_inline_keyboard(buttons: list[str]) -> InlineKeyboardMarkup:
return InlineKeyboardMarkup().add(*[InlineKeyboardButton(text=button, callback_data=button) for button in buttons])
def get_keys_if_chat_id_in_dict(chat_id: int, dictionary: dict) -> list[str]:
keys = []
for key in dictionary:
if key.split("_")[0] == str(chat_id):
keys.append(key)
return keys
def delete_previous_searches_besides(bot: TeleBot, id: int, users_keyboard_data: dict[dict[str: str]], besides: Message) -> dict[dict[str: str]]:
to_delete_lst = get_keys_if_chat_id_in_dict(id, users_keyboard_data)
to_delete_lst.remove(f"{id}_{besides.message_id}")
for key in to_delete_lst:
users_keyboard_data.pop(key, None)
bot.edit_message_text(chat_id=id, message_id=int(key.split("_")[1]), text="Клавиатура более недоступна")
return users_keyboard_data
def search(bot: TeleBot, id: int, users_keyboard_data: dict[dict[str: str]]) -> dict[dict[str: str]]:
message = bot.send_message(chat_id=id, text="Выберите округ", reply_markup=build_inline_keyboard(["Кнопка 1", "Кнопка 2"]))
users_keyboard_data[f"{id}_{message.message_id}"] = {}
users_keyboard_data = delete_previous_searches_besides(bot, id, users_keyboard_data, message)
return users_keyboard_data
def change_to_other_inline_keyboard(bot: TeleBot, call: CallbackQuery, users_keyboard_data: dict[dict[str: str]]) -> dict[dict[str: str]]:
users_keyboard_data[f"{call.from_user.id}_{call.message.message_id}"] = {"FIRST_DATA": f"{call.data}"}
bot.edit_message_text(chat_id=call.from_user.id, message_id=call.message.id, text="Новая клавиатура",
reply_markup=build_inline_keyboard(["Новая кнопка 1", "Новая кнопка 2"]))
return users_keyboard_data
@bot.message_handler()
def handle_message(message: Message) -> None:
if message.text == "Поиск":
global users_keyboard_data
users_keyboard_data = search(bot, message.chat.id, users_keyboard_data)
@bot.callback_query_handler(func=lambda call: True)
def handle_query(call: CallbackQuery) -> None:
global users_keyboard_data
users_keyboard_data = change_to_other_inline_keyboard(bot, call, users_keyboard_data)
if __name__ == "__main__":
bot.polling(none_stop=True)
Я пробовал сделать вот так:
@bot.callback_query_handler(func=lambda call: True)
def handle_query(call: CallbackQuery) -> None:
if call.message.text != "Клавиатура более недоступна":
global users_keyboard_data
users_keyboard_data = change_to_other_inline_keyboard(bot, call, users_keyboard_data)
Но даже не смотря на отсутствие асинхронности в библиотеке telebot - он все равно принимает условие за правду.
Ответы (1 шт):
Итак, у тебя Telebot
что это значит? (Ну в принципе ничего кроме того, что в твоём случае у тебя синхронный код).
Итак синхронность, казалось бы, и на что это влияет.
Как ты сам видишь, тут всё работает по очереди, но что если что-то очень долго генерируется? Допустим, кнопки сгенерировались позже, чем пользователь нажал на кнопку (имеется в виду реализация кнопок по типу недоступных). Тогда Telegram
отправит Апдейт, что так-то так, а кнопка содержит текст, отличный от "Клавиатура более недоступна"
.
Дальше пройдёмся по фильтрам: вроде всё просто и легко, но всплывает айсберг, кажется проблема мала, но что же на самом деле?
@bot.callback_query_handler(func=lambda call: True)
- Вроде простой понятный фильтр, и что тут париться, но он приводит к логике, которая не должна происходить, а именно к обработке всех колбеков: Используя func=lambda call: True
, вы обрабатываете все колбек-запросы, независимо от их содержания.
Так что лучше сделать практику вот такую:
@bot.callback_query_handler(func=lambda call: call.message.text != "Клавиатура более недоступна")
def handle_query(call: CallbackQuery) -> None:
# Ваша логика обработки колбеков
Она будет убирать возможность активировать кнопки, которые недоступны.
А можно сделать ещё практичнее - написать Хэндлер, который будет говорить: Эй, пользователь, кнопка уже недоступна зачем жмёшь?
Если идти совсем на уровень корректной логики, то можно назначать кнопкам, которые не нужны, специфичный data
, и обрабатывать этот data
по своему усмотрению.
Далее по поводу вашего сообщения о том, что я хочу прервать логику callback, а как?
Всё просто: после "Поиск"
сделайте быстрый кэш, его суть будет в том, чтобы быстрее всех сохранить информацию о пользователе и о кнопке, а точнее лишь:
[f"{call.from_user.id}_{call.message.message_id}_can_change"] = False
Далее вы уже применяете её для того, чтобы посмотреть, а надо ли менять, и если нет, ну значит нет, а если значения нет или оно True, то надо.
Хотя для большей простоты и понятности рекомендую вам обзавестись базой данных, к примеру SQLite
для Синхронного бота (это на самом деле не очень хорошая БД, но для старта пойдёт), а потом вы уже можете перейти к PostgreSQL
, MySQL
, MongoDB
и другим (ну или для хранения кэша можно использовать Redis, но это, конечно, всё поводы для других вопросов и ответов).
Я надеюсь, что смог помочь вам решить вашу проблему или хотя бы натолкнуть на верный ход мыслей. Если это не так - уточняйте, что непонятно в комментариях.