Как реализовать кнопку "назад" в телеграмм боте?

Рейтинг: 1Ответов: 2Опубликовано: 19.07.2025

Не пойму где ошибка при реализации кнопки "назад" в телеграмм боте? При выборе любой кнопки действие идет вперед, а кнопка назад "игнорируется". Часть кода прилагаю.

    import telebot
from config import TELEGRAM_BOT_TOKEN
from telebot import types

bot = telebot.TeleBot(TELEGRAM_BOT_TOKEN)
users ={}


@bot.message_handler(commands=['start'])
def menu(message: telebot.types.Message):
    chat_id = message.chat.id
    keyboard1 = types.ReplyKeyboardMarkup(resize_keyboard=True)
    button_glazing = telebot.types.KeyboardButton(text="Остекление окон", )
    button_balcony = telebot.types.KeyboardButton(text="Остекление балкона/лоджии")
    button_contacts = telebot.types.KeyboardButton(text="📞 Контакты")
    button_portfolio = telebot.types.KeyboardButton(text="🖼 Портфолио")
    keyboard1.add(button_glazing, button_balcony, button_contacts, button_portfolio)
    bot.send_message(chat_id,
                     '👋 Здравствуйте!Мы рады помочь вам с остеклением. Пожалуйста, выберите, что вас интересует:',
                     reply_markup=keyboard1)

@bot.message_handler(content_types=['text'])
def glazing(message):
    if message.text == 'Остекление окон':
        chat_id = message.chat.id
        keyboard2 = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
        button_glazing_kvartira = telebot.types.KeyboardButton(text="Квартира")
        button_glazing_dom = telebot.types.KeyboardButton(text="Дом")
        button_glazing_office = telebot.types.KeyboardButton(text="Офис")
        button_glazing_other = telebot.types.KeyboardButton(text="Другое помещение")
        bot.register_next_step_handler(message, glazing_type)
        button_glazing_back = telebot.types.KeyboardButton(text="Назад")
        keyboard2.add(button_glazing_kvartira, button_glazing_dom, button_glazing_office, button_glazing_other, button_glazing_back )
        bot.send_message(chat_id, 'Куда хотите установить окна?', reply_markup=keyboard2)

@bot.message_handler(content_types=['text'])
def glazing_type(message):

    chat_id = message.chat.id
    keyboard3 = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
    button_glazing_kv_window = telebot.types.KeyboardButton(text="Квартирное окно")
    button_glazing_balcony_block = telebot.types.KeyboardButton(text="Балконный блок")
    button_glazing_enter_block = telebot.types.KeyboardButton(text="Входная группа")
    button_glazing_back = telebot.types.KeyboardButton(text="Назад")
    keyboard3.add(button_glazing_kv_window, button_glazing_balcony_block, button_glazing_enter_block, button_glazing_back)
    bot.send_message(chat_id, 'Какой тип изделия?', reply_markup=keyboard3)
    if message.text == 'Квартира' or "Дом" or "Офис" or "Другое помещение":
        bot.register_next_step_handler(message, glazing_profile)
    elif message.text == 'Назад':
        menu(message)

#
def glazing_profile(message):
    chat_id = message.chat.id
    keyboard4 = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
    button_glazing_rehau = telebot.types.KeyboardButton(text="Rehau")
    button_glazing_kbe = telebot.types.KeyboardButton(text="KBE")
    button_glazing_brusbox = telebot.types.KeyboardButton(text="Brusbox")
    button_glazing_consultation = telebot.types.KeyboardButton(text="Нужна консультация")
    button_glazing_profile_back = telebot.types.KeyboardButton(text="Назад")
    keyboard4.add(button_glazing_rehau, button_glazing_kbe, button_glazing_brusbox,button_glazing_consultation, button_glazing_profile_back)
    bot.send_message(chat_id, 'Выберите профиль', reply_markup=keyboard4)
    bot.register_next_step_handler(message, glazing_size)

def glazing_size(message):
    chat_id = message.chat.id
    keyboard5 = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
    button_glazing_size_yes = telebot.types.KeyboardButton(text="Да, могу")
    button_glazing_size_no = telebot.types.KeyboardButton(text="Нет, нужен замерщик")
    button_glazing_size_back = telebot.types.KeyboardButton(text="Назад")
    keyboard5.add(button_glazing_size_yes, button_glazing_size_no, button_glazing_size_back )
    bot.send_message(chat_id, ' Можете указать размеры изделий?', reply_markup=keyboard5)
    bot.register_next_step_handler(message, adress)



@bot.message_handler(
    func=lambda message: message.text == 'Остекление балкона/лоджии')
def balcony(message):
    chat_id = message.chat.id
    keyboard6 = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
    button_balcony_hot = telebot.types.KeyboardButton(text="Тёплое остекление")
    button_balcony_cold = telebot.types.KeyboardButton(text="Холодное остекление")
    button_balcony_slidors = telebot.types.KeyboardButton(text="Slidors")
    button_balcony_back = telebot.types.KeyboardButton(text="Назад")
    keyboard6.add(button_balcony_hot, button_balcony_cold, button_balcony_slidors,button_balcony_back )
    bot.send_message(chat_id, 'Выберите тип остекления:', reply_markup=keyboard6)
    bot.register_next_step_handler(message, balcony_form)

def balcony_form(message):
    chat_id = message.chat.id
    keyboard7 = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
    button_form_1 = telebot.types.KeyboardButton(text="Прямая лоджия")
    button_form_2 = telebot.types.KeyboardButton(text="Лодочка")
    button_form_3 = telebot.types.KeyboardButton(text="Сапожек")
    button_form_4 = telebot.types.KeyboardButton(text="П-образный")
    button_form_5 = telebot.types.KeyboardButton(text="Другое")
    button_form_back = telebot.types.KeyboardButton(text="Назад")
    keyboard7.add(button_form_1, button_form_2, button_form_3, button_form_4, button_form_5, button_form_back )
    bot.send_message(chat_id, 'Какая у вас форма балкона?', reply_markup=keyboard7)
    bot.register_next_step_handler(message, balcony_type)

def balcony_type(message):
    chat_id = message.chat.id
    keyboard8 = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
    button_type_1 = telebot.types.KeyboardButton(text="Да")
    button_type_2 = telebot.types.KeyboardButton(text="Нет")
    button_type_3 = telebot.types.KeyboardButton(text="Другое")
    button_type_back = telebot.types.KeyboardButton(text="Назад")
    keyboard8.add(button_type_1, button_type_2, button_type_3, button_type_back )
    bot.send_message(chat_id, 'Нужна ли обшивка?', reply_markup=keyboard8)
    bot.register_next_step_handler(message, balcony_insulation)


def balcony_insulation(message):
    chat_id = message.chat.id
    keyboard9 = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
    button_insulation_type_1 = telebot.types.KeyboardButton(text="Да")
    button_insulation_type_2 = telebot.types.KeyboardButton(text="Нет")
    button_insulation_type_3 = telebot.types.KeyboardButton(text="Другое")
    button_insulation_back = telebot.types.KeyboardButton(text="Назад")
    keyboard9.add(button_insulation_type_1, button_insulation_type_2, button_insulation_type_3, button_insulation_back)
    bot.send_message(chat_id, 'Утепление требуется?', reply_markup=keyboard9)
    bot.register_next_step_handler(message, balcony_size)

def balcony_size(message):
    chat_id = message.chat.id
    keyboard10 = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
    button_balcony_size_1 = telebot.types.KeyboardButton(text="Да, могу")
    button_balcony_size_2 = telebot.types.KeyboardButton(text="Нет, нужен замерщик")
    button_balcony_size_back = telebot.types.KeyboardButton(text="Назад")
    keyboard10.add(button_balcony_size_1, button_balcony_size_2, button_balcony_size_back)
    bot.send_message(chat_id, 'Указать размеры?', reply_markup=keyboard10)
    bot.register_next_step_handler(message, adress)


@bot.message_handler(
    func=lambda message: message.text == '📞 Контакты')
def contacts(message):
    chat_id = message.chat.id
    keyboard11 = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
    button_gcontacts_back = telebot.types.KeyboardButton(text="🔙 Главное меню")
    keyboard11.add(button_gcontacts_back )
    bot.send_message(chat_id, '📍 Наши контакты:'
                              'ул. Центральная, д. 10'
                              '📱 +7 (900) 000-00-00'
                              '✉ info@okna.ru'
                              '📲 WhatsApp: https://wa.me/79000000000'
                              '📲 Telegram: https://t.me/okna_manager', reply_markup=keyboard11)
    bot.register_next_step_handler(message, glazing_type)



@bot.message_handler(
    func=lambda message: message.text == '🖼 Портфолио')
def portfolio(message):
    chat_id = message.chat.id
    keyboard12 = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
    button_gcontacts_back = telebot.types.KeyboardButton(text="🔙 Главное меню")
    keyboard12.add(button_gcontacts_back )
    bot.send_message(chat_id, 'Отправка портфолио'
                              , reply_markup=keyboard12)
    bot.register_next_step_handler(message, glazing_type)



def adress(message):
    chat_id = message.chat.id
    keyboard13 = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
    button_gcontacts_back = telebot.types.KeyboardButton(text="🔙 Назад")
    keyboard13.add(button_gcontacts_back )
    bot.send_message(chat_id, 'Укажите ваш город/район'
                              , reply_markup=keyboard13)
    bot.register_next_step_handler(message, phone_number)


def phone_number(message):
    chat_id = message.chat.id
    keyboard14 = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
    button_gcontacts_back = telebot.types.KeyboardButton(text="🔙 Назад")
    keyboard14.add(button_gcontacts_back )
    bot.send_message(chat_id, 'Укажите ваш номер телефона'
                              , reply_markup=keyboard14)
    bot.register_next_step_handler(message, enter_username)


def enter_username(message):
    chat_id = message.chat.id
    keyboard15 = telebot.types.ReplyKeyboardMarkup(resize_keyboard=True)
    button_gcontacts_back = telebot.types.KeyboardButton(text="🔙 Назад")
    keyboard15.add(button_gcontacts_back )
    bot.send_message(chat_id, 'Укажите ваше имя'
                              , reply_markup=keyboard15)
    bot.register_next_step_handler(message, glazing_type)




bot.polling()

Ответы

▲ 0Принят

Главная проблема - условие.

if message.text == 'Квартира' or "Дом" or "Офис" or "Другое помещение":

В Python 'Дом' - это всегда True, так что ветка elif message.text == 'Назад' ни разу не сработает. Пиши так:

if message.text in ('Квартира', 'Дом', 'Офис', 'Другое помещение'): bot.register_next_step_handler(message, glazing_profile) elif message.text == 'Назад': menu(message)

У тебя несколько @bot.message_handler(content_types=['text']) без фильтров. Все они цепляются за любое сообщение, порядок запуска неочевиден. исправь либо через func=‑фильтры, либо через finite‑state machine (pyTelegramBotAPI имеет telebot.states). Иначе 'Назад' ловит не тот хендлер и едет дальше.

Регистрируй следующий шаг после проверки 'Назад'. Сначала спросил - ждёшь ответ - разбираешь, что пришло. Не регистрируй хендлер заранее внутри меню, а то перескакиваешь уровни.

Простейший скелет:

def show_menu(chat_id):
    kb = types.ReplyKeyboardMarkup(resize_keyboard=True)
    kb.add('Окна', '📞 Контакты')
    bot.send_message(chat_id, 'Меню', reply_markup=kb)
    bot.register_next_step_handler_by_chat_id(chat_id, route_main)

def route_main(msg):
    if msg.text == 'Окна':
        show_glazing(msg.chat.id)
    else:
        show_menu(msg.chat.id)

def show_glazing(chat_id):
    kb = types.ReplyKeyboardMarkup(resize_keyboard=True)
    kb.add('Квартира', 'Дом', 'Назад')
    bot.send_message(chat_id, 'Куда ставим?', reply_markup=kb)
    bot.register_next_step_handler_by_chat_id(chat_id, route_glazing)

def route_glazing(msg):
    if msg.text == 'Назад':
        show_menu(msg.chat.id)
    else:
        # дальше по цепочке
        pass

Работает тупо, но честно: каждое меню своё, 'Назад' просто цепляет предыдущую функцию.

▲ 1

У вас на данный момент проблема заключается в том, что при переходах между шагами register_next_step_handler - намертво приколачивает следующий ввод\нажатие только к одному конкретному обработчику.

Что из этого следует?

Когда нажимаем "Назад", бот не понимает, что это команда BACK, потому что текущий обработчик назначенный register_next_step_handler ожидает сообщение, а не кнопку "Назад".

Кнопка "Назад" просто становится текстом, который вы никак специально не обрабатываете.

У вас НЕТ общей логики или памяти(стека состояния) о том, где пользователь был - история переходов ни как не хранится.

И вот получается, что кнопка "Назад" не работает, потому что бот всегда смотрит только вперёд, и ни шагу назад. А сама кнопка исключительно для красоты.

В общем, что бы это как-то исправить - нужна централизованная система, которая хранит состояние пользователя и умеет возвращать его назад, а не цепочка вызовов по шагам.

Конечно можно реализовать всё и цепочкой, как вам любезно предложили в другом ответе, но тогда при добавлении каждого нового блока нужно будет ВСЁ переделывать. Что очень быстро сведёт расширяемость бота на ноль, а его поддержка превратится в лютую головную боль.

Одним словам, нужно менять подход и искать более масштабируемую систему, а не строить логику работы на register_next_step_handler. В данном случае - это путь в никуда.


Небольшой пример для вас:

from typing import TypedDict, NotRequired, Callable, Dict
import telebot
from telebot import types

bot = telebot.TeleBot("TOKEN")

BACK = "🔙 Назад"
MAIN_MENU = "main_menu"


def send_summary(user_id: int):
    session = get_session(user_id)
    data = session.data
    if not data:
        bot.send_message(user_id, "Пока нет собранных данных.")
        return
    text = "📋 Ваша заявка:\n"
    for key, value in data.items():
        text += f"• {key}: {value}\n"
    bot.send_message(user_id, text)
    session.reset()


class State(TypedDict):
    text: str
    transitions: NotRequired[Dict[str, str]]
    next: NotRequired[str]
    buttons: NotRequired[list[str]]
    handler: NotRequired[Callable[[int], None]]
    input: NotRequired[bool]


EMPTY_STATE: State = {"text": "Ошибка состояния", "next": MAIN_MENU}

fsm: Dict[str, State] = {
    "main_menu": {
        "text": "👋 Здравствуйте! Мы рады помочь вам с остеклением. Что вас интересует?",
        "buttons": [
            "Остекление окон",
            "Остекление балкона/лоджии",
            "📞 Контакты",
            "🖼 Портфолио",
        ],
        "transitions": {
            "Остекление окон": "glazing_place",
            "Остекление балкона/лоджии": "balcony_type",
            "📞 Контакты": "contacts",
            "🖼 Портфолио": "portfolio",
        },
    },
    "glazing_place": {
        "text": "Куда хотите установить окна?",
        "buttons": ["Квартира", "Дом", "Офис", "Другое помещение"],
        "transitions": {
            "Квартира": "glazing_item",
            "Дом": "glazing_item",
            "Офис": "glazing_item",
            "Другое помещение": "glazing_item",
        },
    },
    "glazing_item": {
        "text": "Какой тип изделия?",
        "buttons": ["Квартирное окно", "Балконный блок", "Входная группа"],
        "transitions": {
            "Квартирное окно": "glazing_profile",
            "Балконный блок": "glazing_profile",
            "Входная группа": "glazing_profile",
        },
    },
    "glazing_profile": {
        "text": "Выберите профиль:",
        "buttons": ["Rehau", "KBE", "Brusbox", "Нужна консультация"],
        "transitions": {
            "Rehau": "glazing_size",
            "KBE": "glazing_size",
            "Brusbox": "glazing_size",
            "Нужна консультация": "glazing_size",
        },
    },
    "glazing_size": {
        "text": "Можете указать размеры изделий?",
        "buttons": ["Да, могу", "Нет, нужен замерщик"],
        "transitions": {"Да, могу": "adress", "Нет, нужен замерщик": "adress"},
    },
    "balcony_type": {
        "text": "Выберите тип остекления:",
        "buttons": ["Тёплое остекление", "Холодное остекление", "Slidors"],
        "transitions": {
            "Тёплое остекление": "balcony_form",
            "Холодное остекление": "balcony_form",
            "Slidors": "balcony_form",
        },
    },
    "balcony_form": {
        "text": "Какая у вас форма балкона?",
        "buttons": ["Прямая лоджия", "Лодочка", "Сапожек", "П-образный", "Другое"],
        "transitions": {
            "Прямая лоджия": "balcony_sheathing",
            "Лодочка": "balcony_sheathing",
            "Сапожек": "balcony_sheathing",
            "П-образный": "balcony_sheathing",
            "Другое": "balcony_sheathing",
        },
    },
    "balcony_sheathing": {
        "text": "Нужна ли обшивка?",
        "buttons": ["Да", "Нет", "Другое"],
        "transitions": {
            "Да": "balcony_insulation",
            "Нет": "balcony_insulation",
            "Другое": "balcony_insulation",
        },
    },
    "balcony_insulation": {
        "text": "Утепление требуется?",
        "buttons": ["Да", "Нет", "Другое"],
        "transitions": {
            "Да": "balcony_size",
            "Нет": "balcony_size",
            "Другое": "balcony_size",
        },
    },
    "balcony_size": {
        "text": "Указать размеры?",
        "buttons": ["Да, могу", "Нет, нужен замерщик"],
        "transitions": {
            "Да, могу": "adress",
            "Нет, нужен замерщик": "adress",
        },
    },
    "contacts": {
        "text": (
            "📍 Наши контакты:\n"
            "ул. Центральная, д. 10\n"
            "📱 +7 (900) 000-00-00\n"
            "✉ info@okna.ru\n"
            "📲 [WhatsApp](https://wa.me/79000000000)\n"
            "📲 [Telegram](https://t.me/okna_manager)"
        ),
    },
    "portfolio": {
        "text": "🖼 Портфолио: [пример](https://ru.stackoverflow.com/users/562052/amgarak)",
    },
    "adress": {
        "text": "Укажите ваш город/район:",
        "next": "phone",
        "input": True,
    },
    "phone": {
        "text": "Укажите ваш номер телефона:",
        "next": "name",
        "input": True,
    },
    "name": {
        "text": "Укажите ваше имя:",
        "next": "summary",
        "input": True,
    },
    "summary": {
        "text": "Спасибо за информацию! Вот ваша заявка:",
        "handler": send_summary,
    },
}


class UserSession:
    def __init__(self):
        self.state_stack = [MAIN_MENU]
        self.data = {}

    def push_state(self, state: str):
        self.state_stack.append(state)

    def pop_state(self):
        if len(self.state_stack) > 1:
            removed_state = self.state_stack.pop()
            if removed_state in self.data:
                del self.data[removed_state]

    def current_state(self) -> str:
        return self.state_stack[-1]

    def reset(self):
        self.state_stack = [MAIN_MENU]
        self.data = {}


sessions: Dict[int, UserSession] = {}


def get_session(user_id: int) -> UserSession:
    return sessions.setdefault(user_id, UserSession())


def send_state(user_id: int):
    session = get_session(user_id)
    state_name = session.current_state()
    state = fsm.get(state_name, EMPTY_STATE)

    buttons = state.get("buttons", [])
    markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
    for btn in buttons:
        markup.add(types.KeyboardButton(btn))
    if state_name != MAIN_MENU:
        markup.add(types.KeyboardButton(BACK))

    bot.send_message(
        user_id, state.get("text", "..."), reply_markup=markup, parse_mode="Markdown"
    )

    handler = state.get("handler")
    if callable(handler):
        handler(user_id)


def is_text_input(message):
    session = get_session(message.chat.id)
    state = fsm.get(session.current_state(), EMPTY_STATE)
    return state.get("input") is True


def is_menu_choice(message):
    text = message.text
    if not text:
        return False
    if text.startswith("/"):
        return False
    if is_text_input(message):
        return False
    if text == BACK:
        return False
    return True


@bot.message_handler(commands=["start"])
def handle_start(message):
    session = get_session(message.from_user.id)
    session.reset()
    send_state(message.from_user.id)


@bot.message_handler(func=lambda message: message.text == BACK)
def handle_back(message):
    session = get_session(message.chat.id)
    session.pop_state()
    send_state(message.chat.id)


@bot.message_handler(func=is_menu_choice)
def handle_menu_choice(message):
    session = get_session(message.chat.id)
    text = message.text.strip()
    state_name = session.current_state()
    state = fsm.get(state_name, EMPTY_STATE)

    next_state = state.get("transitions", {}).get(text)
    if next_state:
        session.data[state_name] = text
        session.push_state(next_state)
        send_state(message.chat.id)
    else:
        bot.send_message(
            message.chat.id, "Не понял. Пожалуйста, выберите один из вариантов."
        )


@bot.message_handler(func=is_text_input)
def handle_text_states(message):
    session = get_session(message.chat.id)
    state_name = session.current_state()
    state = fsm.get(state_name, EMPTY_STATE)
    session.data[state_name] = message.text.strip()
    next_state = state.get("next", MAIN_MENU)
    session.push_state(next_state)
    send_state(message.chat.id)


if __name__ == "__main__":
    bot.polling(none_stop=True)

По итогу:

Для каждого пользователя есть свой стек состояний state_stack, который хранит последовательность его шагов по меню.

При переходе вперёд - новое состояние кладётся в стек push_state.

При нажатии "Назад" - последнее состояние удаляется из стека pop_state, возвращая пользователя на предыдущий шаг.

Сама структура целиком лежит в словаре fsm - по сути весь диалог с ботом описан в одном месте:

  • Текст, кнопки, куда нужно перейти по каждому выбору, нужно ли вводить текст.

  • Благодаря этому можно непринуждённо менять\удалять или добавлять шаги, не ломая себе мозг и остальной код.

И самое главное, при таком подходе мы получаем централизованное управление нашими обработчиками. Нам не нужно плодить 100500 message_handler, громоздить управление через десятки вложенных if, простраивать сложные цепочки через register_next_step_handler, а просто достаточно описать структуру в словаре и радоваться жизни.