у меня ошибка в этом коде и процесс зависает:
Nov 15 23:07:40 vps1707806210 python[15811]: 2024-11-15 23:07:40,444 - bot.handlers.sms - INFO - Пользователь 389669884 ввел сервис: сам
Nov 15 23:07:40 vps1707806210 python[15811]: 2024-11-15 23:07:40,584 - aiogram.event - INFO - Update id=600812442 is handled. Duration 349 ms by bot id=7633673561
Nov 15 23:07:42 vps1707806210 python[15811]: 2024-11-15 23:07:42,210 - aiogram.event - INFO - Update id=600812443 is handled. Duration 140 ms by bot id=7633673561
Nov 15 23:07:42 vps1707806210 python[15811]: 2024-11-15 23:07:42,210 - bot.handlers.sms - INFO - Пользователь 389669884 выбрал сервис: jr
Nov 15 23:07:42 vps1707806210 python[15811]: 2024-11-15 23:07:42,351 - bot.api - INFO - Запрос цен для сервиса: jr, страна: 0, валюта: 643
Nov 15 23:07:42 vps1707806210 python[15811]: 2024-11-15 23:07:42,638 - bot.api - INFO - Цены для сервиса jr: {'0': {'jr': {'0.0153': 28577, '0.0159': 88, '0.0163': 27, '0.0177': 73, '0.0205': 40, '0.0221': 48, '0.0245': 481, '0.0272': 17, '0.0275': 54, '0.0276': 8, '0.0306': 11504}}}
Nov 15 23:07:45 vps1707806210 python[15811]: 2024-11-15 23:07:45,324 - bot.api - INFO - Запрос номера для сервиса: jr, страна: 0, оператор: any, макс. цена: 0.0153, валюта: 643
Nov 15 23:07:45 vps1707806210 python[15811]: 2024-11-15 23:07:45,448 - bot.api - INFO - Ответ номера: MAILGROUP_NOT_AVAILABLE
Nov 15 23:07:45 vps1707806210 python[15811]: 2024-11-15 23:07:45,450 - aiogram.event - INFO - Update id=600812444 is not handled. Duration 348 ms by bot id=7633673561
Nov 15 23:07:45 vps1707806210 python[15811]: 2024-11-15 23:07:45,450 - aiogram.event - ERROR - Cause exception while process update id=600812444 by bot id=7633673561
Nov 15 23:07:45 vps1707806210 python[15811]: TypeError: errors_handler() missing 1 required positional argument: 'exception'
Nov 15 23:07:45 vps1707806210 python[15811]: Traceback (most recent call last):
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/bot/handlers/sms.py", line 867, in process_number_purchase
Nov 15 23:07:45 vps1707806210 python[15811]:     raise Exception("Unexpected API response")
Nov 15 23:07:45 vps1707806210 python[15811]: Exception: Unexpected API response
Nov 15 23:07:45 vps1707806210 python[15811]: During handling of the above exception, another exception occurred:
Nov 15 23:07:45 vps1707806210 python[15811]: Traceback (most recent call last):
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/middlewares/error.py", line 25, in __call__
Nov 15 23:07:45 vps1707806210 python[15811]:     return await handler(event, data)
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/middlewares/user_context.py", line 56, in __call__
Nov 15 23:07:45 vps1707806210 python[15811]:     return await handler(event, data)
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/fsm/middleware.py", line 42, in __call__
Nov 15 23:07:45 vps1707806210 python[15811]:     return await handler(event, data)
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/event/telegram.py", line 121, in trigger
Nov 15 23:07:45 vps1707806210 python[15811]:     return await wrapped_inner(event, kwargs)
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/event/handler.py", line 43, in call
Nov 15 23:07:45 vps1707806210 python[15811]:     return await wrapped()
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/dispatcher.py", line 276, in _listen_update
Nov 15 23:07:45 vps1707806210 python[15811]:     return await self.propagate_event(update_type=update_type, event=event, **kwargs)
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/router.py", line 146, in propagate_event
Nov 15 23:07:45 vps1707806210 python[15811]:     return await observer.wrap_outer_middleware(_wrapped, event=event, data=kwargs)
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/router.py", line 141, in _wrapped
Nov 15 23:07:45 vps1707806210 python[15811]:     return await self._propagate_event(
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/router.py", line 166, in _propagate_event
Nov 15 23:07:45 vps1707806210 python[15811]:     response = await observer.trigger(event, **kwargs)
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/event/telegram.py", line 121, in trigger
Nov 15 23:07:45 vps1707806210 python[15811]:     return await wrapped_inner(event, kwargs)
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/event/handler.py", line 43, in call
Nov 15 23:07:45 vps1707806210 python[15811]:     return await wrapped()
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/bot/handlers/sms.py", line 687, in price_selected_handler
Nov 15 23:07:45 vps1707806210 python[15811]:     await process_number_purchase(callback_query.message, state)
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/bot/handlers/sms.py", line 870, in process_number_purchase
Nov 15 23:07:45 vps1707806210 python[15811]:     logger.error(f"Error obtaining number for service '{service_code}', attempt {attempt}: {e}")
Nov 15 23:07:45 vps1707806210 python[15811]: NameError: name 'service_code' is not defined
Nov 15 23:07:45 vps1707806210 python[15811]: During handling of the above exception, another exception occurred:
Nov 15 23:07:45 vps1707806210 python[15811]: Traceback (most recent call last):
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/dispatcher.py", line 309, in _process_update
Nov 15 23:07:45 vps1707806210 python[15811]:     response = await self.feed_update(bot, update, **kwargs)
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/dispatcher.py", line 158, in feed_update
Nov 15 23:07:45 vps1707806210 python[15811]:     response = await self.update.wrap_outer_middleware(
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/middlewares/error.py", line 29, in __call__
Nov 15 23:07:45 vps1707806210 python[15811]:     response = await self.router.propagate_event(
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/router.py", line 146, in propagate_event
Nov 15 23:07:45 vps1707806210 python[15811]:     return await observer.wrap_outer_middleware(_wrapped, event=event, data=kwargs)
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/router.py", line 141, in _wrapped
Nov 15 23:07:45 vps1707806210 python[15811]:     return await self._propagate_event(
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/router.py", line 166, in _propagate_event
Nov 15 23:07:45 vps1707806210 python[15811]:     response = await observer.trigger(event, **kwargs)
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/event/telegram.py", line 121, in trigger
Nov 15 23:07:45 vps1707806210 python[15811]:     return await wrapped_inner(event, kwargs)
Nov 15 23:07:45 vps1707806210 python[15811]:   File "/root/smstest/venv/lib/python3.10/site-packages/aiogram/dispatcher/event/handler.py", line 43, in call
Nov 15 23:07:45 vps1707806210 python[15811]:     return await wrapped()
Nov 15 23:07:45 vps1707806210 python[15811]: TypeError: errors_handler() missing 1 required positional argument: 'exception'


код:

import asyncio
import logging
import time
import json
from pathlib import Path
from typing import Optional, List, Tuple, Set
from aiogram import Dispatcher, types, F
from aiogram.utils.keyboard import InlineKeyboardBuilder
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup

from ..data import get_user_api_key, get_user_operator, get_user_country
from ..api import SmsHubAPI
from ..utils import get_usd_to_rub_rate, safe_edit_or_send_message
from .start import send_main_menu
from .activation import user_activations, poll_for_sms
from ..constants.operators import OPERATOR_DISPLAY_NAMES
from ..constants.countries import COUNTRIES
from ..constants.services import SERVICE_DISPLAY_NAMES

logger = logging.getLogger(__name__)

class SmsOrderState(StatesGroup):
    waiting_for_service = State()
    waiting_for_max_price = State()

SERVICES: List[Tuple[str, str]] = list(SERVICE_DISPLAY_NAMES.items())
POPULARITY_FILE = Path("service_popularity.json")

def load_popularity_data() -> dict:
    if POPULARITY_FILE.exists():
        with open(POPULARITY_FILE, 'r', encoding='utf-8') as f:
            return json.load(f)
    return {}

def save_popularity_data(popularity_data: dict):
    with open(POPULARITY_FILE, 'w', encoding='utf-8') as f:
        json.dump(popularity_data, f, ensure_ascii=False, indent=4)

def update_service_popularity(service_code: str):
    popularity_data = load_popularity_data()
    if service_code in popularity_data:
        popularity_data[service_code] += 1
    else:
        popularity_data[service_code] = 1
    save_popularity_data(popularity_data)

def get_sorted_services(available_services: List[str]) -> List[Tuple[str, str]]:
    popularity_data = load_popularity_data()
    sorted_services = sorted(
        [(code, name) for code, name in SERVICES if code in available_services],
        key=lambda service: popularity_data.get(service[0], 0),
        reverse=True
    )
    return sorted_services

def get_service_pages(services: List[Tuple[str, str]], page_size: int = 18) -> List[List[Tuple[str, str]]]:
    pages = []
    for i in range(0, len(services), page_size):
        pages.append(services[i:i + page_size])
    return pages

def build_service_keyboard(page_number: int, services_pages: List[List[Tuple[str, str]]], services_with_numbers: Set[str]) -> InlineKeyboardBuilder:
    keyboard = InlineKeyboardBuilder()

    if page_number < len(services_pages):
        current_page = services_pages[page_number]

        for code, name in current_page:
            # Add a red circle if the service has no available numbers
            if code not in services_with_numbers:
                name += " 🔴"
            keyboard.button(text=name, callback_data=f"service_select:{code}")

        keyboard.adjust(3)
        
        if len(services_pages) > 1:  # Show navigation buttons only if there are multiple pages
            nav_buttons = []
            if page_number > 0:
                nav_buttons.append(types.InlineKeyboardButton(text="⬅️ Назад", callback_data=f"service_page:{page_number - 1}"))
            # Center the page number information
            nav_buttons.append(types.InlineKeyboardButton(text=f"Страница {page_number + 1}/{len(services_pages)}", callback_data="ignore"))
            if page_number < len(services_pages) - 1:
                nav_buttons.append(types.InlineKeyboardButton(text="➡️ Вперед", callback_data=f"service_page:{page_number + 1}"))
            keyboard.row(*nav_buttons)

    # Keep the "🔙 Назад в меню" button consistent
    keyboard.row(types.InlineKeyboardButton(text="🔙 Назад в меню", callback_data="back_to_main_menu"))

    return keyboard

def build_search_result_keyboard(page_number: int, search_results_pages: List[List[Tuple[str, str]]]) -> InlineKeyboardBuilder:
    """
    Builds an inline keyboard for search result pagination.

    Args:
        page_number (int): The current page number.
        search_results_pages (List[List[Tuple[str, str]]]): The paginated search results.

    Returns:
        InlineKeyboardBuilder: The constructed inline keyboard.
    """
    keyboard = InlineKeyboardBuilder()

    if page_number < len(search_results_pages):
        current_page = search_results_pages[page_number]

        for code, name in current_page:
            keyboard.button(text=name, callback_data=f"service_select:{code}")

        keyboard.adjust(3)
        
        if len(search_results_pages) > 1:
            nav_buttons = []
            if page_number > 0:
                nav_buttons.append(types.InlineKeyboardButton(text="⬅️ Назад", callback_data=f"search_page:{page_number - 1}"))
            # Center the page number information
            nav_buttons.append(types.InlineKeyboardButton(text=f"Страница {page_number + 1}/{len(search_results_pages)}", callback_data="ignore"))
            if page_number < len(search_results_pages) - 1:
                nav_buttons.append(types.InlineKeyboardButton(text="➡️ Вперед", callback_data=f"search_page:{page_number + 1}"))
            keyboard.row(*nav_buttons)

    # Keep the "🔙 Назад в меню" button consistent
    keyboard.row(types.InlineKeyboardButton(text="🔙 Назад", callback_data="back_to_services"))

    return keyboard

def get_service_message_text(total_services_with_numbers: int, total_services_for_country: int, services_with_operator: int, country_name: str, operator_name: str) -> str:
    return (
        f"🔢 *Общее кол-во сервисов:* `{total_services_for_country}`\n"
        f"💼 *Доступные сервисы (где есть номера):* `{total_services_with_numbers}`\n"
        f"✅ *Доступные сервисы (с вашими настройками оператора):* `{services_with_operator}`\n\n"
        f"— Нажмите на кнопку, чтобы выбрать сервис.\n"
        f"— Или введите название, чтобы найти сервис.\n\n"
        f"🐾 *Текущая страна:* `{country_name}`\n"
        f"📡 *Текущий оператор:* `{operator_name}`\n"
        f"_(Эти параметры можно изменить в настройках)_\n"
        f"\n_🔴 - нет номеров_"
    )

async def fetch_all_services(user_api_key: str, user_country_code: str, user_operator: str, state: FSMContext) -> Tuple[Optional[List[str]], Set[str], int, int]:
    smshub_api = SmsHubAPI(user_api_key)
    max_attempts = 3
    last_exception = None
    available_services = []
    services_with_numbers = set()
    numbers_status = {}

    for attempt in range(1, max_attempts + 1):
        try:
            # Retrieve current data from state
            data = await state.get_data()
            message = data.get('message')

            # Send progress message
            progress_text = f"Идет запрос списка сервисов {attempt}/{max_attempts}..."
            keyboard = InlineKeyboardBuilder()
            keyboard.button(text="❌ Отмена", callback_data="cancel_operation")  # Keep "Cancel" button here
            keyboard.adjust(1)

            await safe_edit_or_send_message(
                message=message,
                new_text=progress_text,
                state=state,
                reply_markup=keyboard.as_markup()
            )

            # Make API request with specified operator
            numbers_status = await smshub_api.get_numbers_status(country=user_country_code, operator=user_operator)
            if numbers_status:
                # All services and services with available numbers
                available_services = [service.split('_')[0] for service in numbers_status.keys()]
                services_with_numbers = {service.split('_')[0] for service, count in numbers_status.items() if int(count) > 0}
                break
        except Exception as e:
            last_exception = e
            logger.error(f"Ошибка при запросе статуса всех сервисов, попытка {attempt}: {e}")

        data = await state.get_data()
        if data.get("cancel_request"):
            logger.info("Запрос на получение всех сервисов был отменен.")
            return None, set(), 0, 0

        await asyncio.sleep(5)

    if last_exception:
        logger.error(f"Ошибка при запросе всех сервисов: {last_exception}")
        return None, set(), 0, 0

    # Save numbers_status in state for further use
    await state.update_data(numbers_status=numbers_status)

    return available_services, services_with_numbers, len(services_with_numbers), len(available_services)

async def get_sms_handler(callback_query: types.CallbackQuery, state: FSMContext):
    try:
        await callback_query.answer()
    except Exception as e:
        logger.warning(f"Не удалось ответить на callback_query: {e}")

    user_api_key = get_user_api_key(callback_query.from_user.id)
    user_country_code = get_user_country(callback_query.from_user.id) or "0"
    user_operator = get_user_operator(callback_query.from_user.id) or "any"

    if not user_api_key:
        keyboard = InlineKeyboardBuilder()
        keyboard.button(text="⚙️ Настройки", callback_data="settings")
        # Изменено: Заменяем "🔙 Назад в меню" на "🔙 Назад"
        keyboard.button(text="🔙 Назад", callback_data="back_to_services")
        keyboard.adjust(1)
        await safe_edit_or_send_message(
            message=callback_query.message,
            new_text="Пожалуйста, сначала настройте ваш API ключ в разделе 'Настройки'.",
            state=state,
            reply_markup=keyboard.as_markup()
        )
        return

    logger.info(f"Пользователь {callback_query.from_user.id} начал процесс получения номера.")

    await state.update_data(message=callback_query.message, cancel_request=False)
    available_services, services_with_numbers, total_services_with_numbers, total_services_for_country = await fetch_all_services(
        user_api_key, user_country_code, user_operator, state
    )

    if available_services is None:
        await send_main_menu(callback_query.message, state)
        return

    country_name = COUNTRIES.get(user_country_code, 'Неизвестно')
    operator_name = OPERATOR_DISPLAY_NAMES.get(user_operator, 'Любой')

    await state.update_data(
        total_services_with_numbers=total_services_with_numbers, 
        total_services_for_country=total_services_for_country,
        country_name=country_name, 
        operator_name=operator_name
    )

    services_with_operator = services_with_numbers  # Now it's a set

    # Sort and split into pages
    sorted_services = get_sorted_services(available_services)
    services_pages = get_service_pages(sorted_services)

    keyboard = build_service_keyboard(page_number=0, services_pages=services_pages, services_with_numbers=services_with_operator)
    message_text = get_service_message_text(
        total_services_with_numbers, 
        total_services_for_country, 
        len(services_with_operator), 
        country_name, 
        operator_name
    )

    await safe_edit_or_send_message(
        message=callback_query.message,
        new_text=message_text,
        state=state,
        reply_markup=keyboard.as_markup(),
        parse_mode="Markdown"
    )
    await state.set_state(SmsOrderState.waiting_for_service)
    await state.update_data(
        available_services=available_services,
        services_pages=services_pages,  # Save pages for navigation
        services_with_numbers=services_with_operator,
        services_current_page=0  # Save current service list page
    )

async def go_back_to_service_selection(callback_query: types.CallbackQuery, state: FSMContext):
    try:
        await callback_query.answer()
    except Exception as e:
        logger.warning(f"Не удалось ответить на callback_query: {e}")
    logger.info(f"Пользователь {callback_query.from_user.id} отменил операцию.")

    await state.update_data(cancel_request=True)

    data = await state.get_data()
    current_task = data.get('current_task')
    if current_task:
        current_task.cancel()

    available_services = data.get('available_services', [])
    total_services_with_numbers = data.get('total_services_with_numbers', len(available_services))
    total_services_for_country = data.get('total_services_for_country', len(available_services))
    country_name = data.get('country_name', 'Неизвестно')
    operator_name = data.get('operator_name', 'Любой')
    services_pages = data.get('services_pages', [])
    services_with_numbers = data.get('services_with_numbers', set())
    services_current_page = data.get('services_current_page', 0)

    # Keep services unchanged; do not make a new API request
    keyboard = build_service_keyboard(page_number=services_current_page, services_pages=services_pages, services_with_numbers=services_with_numbers)
    message_text = get_service_message_text(
        total_services_with_numbers, 
        total_services_for_country, 
        len(services_with_numbers), 
        country_name, 
        operator_name
    )

    await safe_edit_or_send_message(
        message=callback_query.message,
        new_text=message_text,
        state=state,
        reply_markup=keyboard.as_markup(),
        parse_mode="Markdown"
    )

    await state.set_state(SmsOrderState.waiting_for_service)

async def back_to_main_menu(callback_query: types.CallbackQuery, state: FSMContext):
    try:
        await callback_query.answer()
    except Exception as e:
        logger.warning(f"Не удалось ответить на callback_query: {e}")
    logger.info(f"Пользователь {callback_query.from_user.id} вернулся в главное меню.")

    await state.set_state(None)
    await send_main_menu(callback_query.message, state)

# Добавлено: Новый обработчик для кнопки "🔙 Назад" в результатах поиска
async def back_from_search_handler(callback_query: types.CallbackQuery, state: FSMContext):
    try:
        await callback_query.answer()
    except Exception as e:
        logger.warning(f"Не удалось ответить на callback_query: {e}")

    # Получаем данные из состояния
    data = await state.get_data()
    services_pages = data.get('services_pages', [])
    services_current_page = data.get('services_current_page', 0)  # Получаем текущую страницу сервисов
    services_with_numbers = data.get('services_with_numbers', set())
    total_services_with_numbers = data.get('total_services_with_numbers', 0)
    total_services_for_country = data.get('total_services_for_country', 0)
    country_name = data.get('country_name', 'Неизвестно')
    operator_name = data.get('operator_name', 'Любой')

    # Создаём клавиатуру для текущей страницы сервисов
    keyboard = build_service_keyboard(page_number=services_current_page, services_pages=services_pages, services_with_numbers=services_with_numbers)
    message_text = get_service_message_text(
        total_services_with_numbers, 
        total_services_for_country, 
        len(services_with_numbers),
        country_name,
        operator_name
    )

    # Редактируем сообщение
    await safe_edit_or_send_message(
        message=callback_query.message,
        new_text=message_text,
        state=state,
        reply_markup=keyboard.as_markup(),
        parse_mode="Markdown"
    )

    # Устанавливаем состояние ожидания выбора сервиса
    await state.set_state(SmsOrderState.waiting_for_service)

async def service_page_handler(callback_query: types.CallbackQuery, state: FSMContext):
    try:
        await callback_query.answer()
    except Exception as e:
        logger.warning(f"Не удалось ответить на callback_query: {e}")

    page_number_str = callback_query.data.split(":")[1]
    try:
        page_number = int(page_number_str)
    except ValueError:
        logger.error(f"Некорректный номер страницы: {page_number_str}")
        return

    await state.update_data(services_current_page=page_number)  # Save current service list page

    data = await state.get_data()
    services_pages = data.get('services_pages', [])
    services_with_numbers = data.get('services_with_numbers', set())
    total_services_with_numbers = data.get('total_services_with_numbers', 0)
    total_services_for_country = data.get('total_services_for_country', 0)
    country_name = data.get('country_name', 'Неизвестно')
    operator_name = data.get('operator_name', 'Любой')

    if page_number < 0 or page_number >= len(services_pages):
        # Invalid page number, return to the first page
        page_number = 0
        await state.update_data(services_current_page=page_number)

    keyboard = build_service_keyboard(page_number, services_pages, services_with_numbers)
    message_text = get_service_message_text(
        total_services_with_numbers, 
        total_services_for_country, 
        len(services_with_numbers), 
        country_name, 
        operator_name
    )

    await safe_edit_or_send_message(
        message=callback_query.message,
        new_text=message_text,
        state=state,
        reply_markup=keyboard.as_markup(),
        parse_mode="Markdown"
    )

    # Set the state to waiting_for_service
    await state.set_state(SmsOrderState.waiting_for_service)

async def handle_search_page_navigation(callback_query: types.CallbackQuery, state: FSMContext):
    """
    Handles navigation between search result pages.

    Args:
        callback_query (types.CallbackQuery): The callback query from the user.
        state (FSMContext): The current state of the conversation.
    """
    try:
        await callback_query.answer()  # Acknowledge the callback to remove the loading state
    except Exception as e:
        logger.warning(f"Не удалось ответить на callback_query: {e}")

    page_number_str = callback_query.data.split(":")[1]
    try:
        page_number = int(page_number_str)
    except ValueError:
        await callback_query.message.reply("Некорректный номер страницы.")
        return

    data = await state.get_data()
    search_results_pages = data.get('search_results_pages', [])

    if 0 <= page_number < len(search_results_pages):
        keyboard = build_search_result_keyboard(page_number, search_results_pages)

        # Изменено: Обновлён текст сообщения при навигации по результатам поиска
        message_text = (
            f"По вашему поисковому запросу найдено:\n"
            f"Страница {page_number + 1}/{len(search_results_pages)}"
        )

        await callback_query.message.edit_text(
            text=message_text,
            reply_markup=keyboard.as_markup(),
            parse_mode="Markdown"
        )
        # Update the search current page in state
        await state.update_data(search_current_page=page_number)

        await state.set_state(SmsOrderState.waiting_for_service)
    else:
        await callback_query.message.reply("Страница не найдена.")

async def service_selected_handler(callback_query: types.CallbackQuery, state: FSMContext):
    try:
        await callback_query.answer()
    except Exception as e:
        logger.warning(f"Не удалось ответить на callback_query: {e}")

    service_code = callback_query.data.split(":")[1]
    update_service_popularity(service_code)

    await state.update_data(selected_service=service_code, cancel_request=False)
    task = asyncio.create_task(service_handler_logic(
        callback_query.message, state, user_id=callback_query.from_user.id, service_code=service_code, is_service_selection=True))
    await state.update_data(current_task=task)

async def service_handler(message: types.Message, state: FSMContext, user_id: Optional[int] = None):
    try:
        await message.delete()
    except Exception as e:
        logger.error(f"Не удалось удалить сообщение пользователя: {e}")

    user_input = message.text.strip().lower()

    if user_id is None:
        user_id = message.from_user.id

    logger.info(f"Пользователь {user_id} ввел сервис: {user_input}")

    data = await state.get_data()
    available_services = data.get('available_services', [])
    services_with_numbers = data.get('services_with_numbers', set())

    matched_services = [
        (code, name) for code, name in SERVICE_DISPLAY_NAMES.items()
        if code in available_services and (user_input in name.lower() or user_input == code.lower())
    ]

    if matched_services:
        pages = get_service_pages(matched_services)
        page_number = 0
        keyboard = build_search_result_keyboard(page_number, pages)
        
        # Сохраняем текущую страницу сервисов перед началом поиска
        previous_services_page = data.get('services_current_page', 0)

        await state.update_data(
            search_results_pages=pages, 
            search_current_page=page_number,
            previous_services_page=previous_services_page  # Сохраняем предыдущую страницу сервисов
        )

        # Изменено: Обновлён текст сообщения при успешном поиске (убран жирный шрифт)
        await safe_edit_or_send_message(
            message=message,
            new_text="По вашему поисковому запросу найдено:",
            state=state,
            reply_markup=keyboard.as_markup(),
            parse_mode="Markdown"
        )
        await state.set_state(SmsOrderState.waiting_for_service)
    else:
        keyboard = InlineKeyboardBuilder()
        # Get current service page to return
        current_page = data.get('services_current_page', 0)
        keyboard.button(text="🔙 Назад к сервисам", callback_data=f"service_page:{current_page}")
        await safe_edit_or_send_message(
            message=message,
            new_text="Сервис не найден. Попробуйте ввести код или полное название сервиса.",
            state=state,
            reply_markup=keyboard.as_markup()
        )

async def service_handler_logic(message: types.Message, state: FSMContext, user_id: int, service_code: str, is_service_selection: bool = False):
    logger.info(f"Пользователь {user_id} выбрал сервис: {service_code}")

    user_api_key = get_user_api_key(user_id)
    user_operator = get_user_operator(user_id) or "any"
    user_country_code = get_user_country(user_id) or "0"
    country_name = COUNTRIES.get(user_country_code, user_country_code)

    if not user_api_key:
        await safe_edit_or_send_message(
            message=message,
            new_text="Пожалуйста, сначала настройте ваш API ключ в разделе 'Настройки'.",
            state=state
        )
        await state.set_state(None)
        await send_main_menu(message, state)
        return

    smshub_api = SmsHubAPI(user_api_key)
    usd_to_rub_rate = await get_usd_to_rub_rate()

    max_attempts = 3
    success = False
    last_exception = None

    data = await state.get_data()
    numbers_status = data.get('numbers_status', {})
    available_numbers = int(numbers_status.get(f"{service_code}_0", 0) or 0)

    for attempt in range(1, max_attempts + 1):
        if data.get("cancel_request"):
            logger.info(f"Запрос для пользователя {user_id} был отменен.")
            return

        try:
            progress_text = f"Идет запрос на получение номера {attempt}/{max_attempts}..."
            keyboard = InlineKeyboardBuilder()
            # Изменено: Заменяем "❌ Отмена" на "🔙 Назад", возвращая на текущую страницу сервисов
            current_page = data.get('services_current_page', 0)
            keyboard.button(text="🔙 Назад", callback_data=f"service_page:{current_page}")
            keyboard.adjust(1)

            await safe_edit_or_send_message(
                message=message,
                new_text=progress_text,
                state=state,
                reply_markup=keyboard.as_markup()
            )

            # Use cached `numbers_status` instead of making a new request
            if available_numbers > 0:
                # Get price from API
                price_info = await smshub_api.get_price_for_service(service_code, country=user_country_code)
                if price_info:
                    prices = [float(price_str) for price_str in price_info.keys() if price_str is not None]
                    if prices:
                        min_price_usd = min(prices)
                        max_price_usd = max(prices)
                        min_price_rub = min_price_usd * usd_to_rub_rate if usd_to_rub_rate else 0
                        max_price_rub = max_price_usd * usd_to_rub_rate if usd_to_rub_rate else 0

                        operator_display = OPERATOR_DISPLAY_NAMES.get(user_operator, user_operator)
                        service_display = SERVICE_DISPLAY_NAMES.get(service_code, service_code)

                        keyboard = InlineKeyboardBuilder()
                        keyboard.button(text=f"⬇️ Мин. цена ({min_price_usd:.4f}$)", callback_data=f"select_price:{min_price_usd}")
                        keyboard.button(text=f"⬆️ Макс. цена ({max_price_usd:.4f}$)", callback_data=f"select_price:{max_price_usd}")
                        keyboard.adjust(2)
                        # Изменено: Заменяем "❌ Отмена" на "🔙 Назад", возвращая на текущую страницу сервисов
                        keyboard.row(types.InlineKeyboardButton(text="🔙 Назад", callback_data=f"service_page:{current_page}"))

                        message_text = (
                            f"Страна: `{country_name}`\n"
                            f"Доступно номеров: `{available_numbers}`\n"
                            f"Оператор: `{operator_display}`\n"
                            f"Сервис: `{service_display}`\n"
                            f"Цены: от `{min_price_usd:.4f}` $ (₽{min_price_rub:.2f}) до `{max_price_usd:.4f}` $ (₽{max_price_rub:.2f}).\n\n"
                            f"Выберите максимальную допустимую стоимость покупки с помощью кнопок ниже или введите сумму вручную (например: 0.5). Все суммы указаны в долларах ($):"
                        )

                        await safe_edit_or_send_message(
                            message=message,
                            new_text=message_text,
                            state=state,
                            reply_markup=keyboard.as_markup(),
                            parse_mode="Markdown"
                        )

                        await state.update_data(
                            service=service_code,
                            service_display=service_display,
                            country_name=country_name,
                            available_numbers=available_numbers,
                            operator_display=operator_display,
                            min_price=min_price_usd,
                            max_price_usd=max_price_usd,
                            min_price_rub=min_price_rub,
                            max_price_rub=max_price_rub,
                            usd_to_rub_rate=usd_to_rub_rate,
                            user_id=user_id
                        )

                        await state.set_state(SmsOrderState.waiting_for_max_price)
                        success = True
                        break
                    else:
                        logger.info(f"Attempt {attempt}: Prices not found.")
                        last_exception = "Prices not found"
                else:
                    logger.info(f"Attempt {attempt}: Prices not found.")
                    last_exception = "Prices not found"
            else:
                logger.info(f"Attempt {attempt}: No available numbers.")
                last_exception = "No available numbers"

        except Exception as e:
            logger.error(f"Error obtaining number for service '{service_code}', attempt {attempt}: {e}")
            last_exception = e

        await asyncio.sleep(5)

    if not success:
        data = await state.get_data()
        current_page = data.get('services_current_page', 0)
        keyboard = InlineKeyboardBuilder()
        # Изменено: Заменяем "❌ Отмена" на "🔙 Назад"
        keyboard.button(text="🔙 Назад", callback_data=f"service_page:{current_page}")
        await safe_edit_or_send_message(
            message=message,
            new_text=f"Произошла ошибка при обработке запроса: {last_exception}. Попробуйте снова.",
            state=state,
            reply_markup=keyboard.as_markup()
        )

    if last_exception and last_exception != "NO_NUMBERS":
        data = await state.get_data()
        current_page = data.get('services_current_page', 0)
        keyboard = InlineKeyboardBuilder()
        # Изменено: Заменяем "❌ Отмена" на "🔙 Назад"
        keyboard.button(text="🔙 Назад", callback_data=f"service_page:{current_page}")
        await safe_edit_or_send_message(
            message=message,
            new_text=f"Произошла ошибка при обработке запроса: {last_exception}. Попробуйте снова.",
            state=state,
            reply_markup=keyboard.as_markup()
        )

async def price_selected_handler(callback_query: types.CallbackQuery, state: FSMContext):
    try:
        await callback_query.answer()
    except Exception as e:
        logger.warning(f"Не удалось ответить на callback_query: {e}")

    price_str = callback_query.data.split(":")[1]
    try:
        price = float(price_str)
    except ValueError:
        logger.error(f"Некорректная цена: {price_str}")
        return

    await state.update_data(max_price=price)

    await process_number_purchase(callback_query.message, state)

async def max_price_handler(message: types.Message, state: FSMContext):
    max_price_input = message.text.strip()
    data = await state.get_data()
    user_id = data.get('user_id', message.from_user.id)

    try:
        await message.delete()
    except Exception as e:
        logger.error(f"Не удалось удалить сообщение пользователя: {e}")

    logger.info(f"Пользователь {user_id} указал максимальную стоимость: {max_price_input}")

    try:
        max_price = float(max_price_input)
    except ValueError:
        user_data = await state.get_data()
        min_price = user_data.get('min_price', 0)
        max_price_usd = user_data.get('max_price_usd', 0)
        min_price_rub = user_data.get('min_price_rub', 0)
        max_price_rub = user_data.get('max_price_rub', 0)
        current_page = user_data.get('services_current_page', 0)

        keyboard = InlineKeyboardBuilder()
        # Изменено: Заменяем "❌ Отмена" на "🔙 Назад"
        keyboard.button(text="🔙 Назад", callback_data=f"service_page:{current_page}")
        await safe_edit_or_send_message(
            message=message,
            new_text=(
                f"Пожалуйста, введите корректное число для максимальной стоимости. "
                f"От `{min_price:.4f}` $ (₽{min_price_rub:.2f}) до `{max_price_usd:.4f}` $ (₽{max_price_rub:.2f})."
            ),
            state=state,
            reply_markup=keyboard.as_markup(),
            parse_mode="Markdown"
        )
        return

    if max_price < data.get('min_price', 0):
        user_data = await state.get_data()
        current_page = user_data.get('services_current_page', 0)
        keyboard = InlineKeyboardBuilder()
        # Изменено: Заменяем "❌ Отмена" на "🔙 Назад"
        keyboard.button(text="🔙 Назад", callback_data=f"service_page:{current_page}")
        keyboard.adjust(1)
        await safe_edit_or_send_message(
            message=message,
            new_text=f"Максимальная стоимость должна быть не менее `{data.get('min_price', 0):.4f}` $.",
            state=state,
            reply_markup=keyboard.as_markup(),
            parse_mode="Markdown"
        )
        return

    await state.update_data(max_price=max_price)

    await process_number_purchase(message, state)

async def process_number_purchase(message: types.Message, state: FSMContext):
    user_data = await state.get_data()
    user_id = user_data.get('user_id', message.from_user.id)
    service = user_data['service']
    service_display = user_data['service_display']
    country_name = user_data['country_name']
    operator_display = user_data['operator_display']
    max_price = user_data['max_price']
    user_api_key = get_user_api_key(user_id)
    smshub_api = SmsHubAPI(user_api_key)
    user_country_code = get_user_country(user_id) or "0"

    max_attempts = 3
    last_exception = None

    data = await state.get_data()
    numbers_status = data.get('numbers_status', {})
    available_numbers = user_data.get('available_numbers', 0)

    for attempt in range(1, max_attempts + 1):
        if data.get("cancel_request"):
            logger.info(f"Запрос на получение номера для пользователя {user_id} был отменен.")
            return

        try:
            progress_text = f"Идет запрос на получение номера {attempt}/{max_attempts}..."
            keyboard = InlineKeyboardBuilder()
            # Изменено: Заменяем "❌ Отмена" на "🔙 Назад", возвращая на текущую страницу сервисов
            current_page = data.get('services_current_page', 0)
            keyboard.button(text="🔙 Назад", callback_data=f"service_page:{current_page}")
            keyboard.adjust(1)

            await safe_edit_or_send_message(
                message=message,
                new_text=progress_text,
                state=state,
                reply_markup=keyboard.as_markup()
            )

            user_operator = get_user_operator(user_id) or "any"

            # Use cached `numbers_status` instead of making a new request
            result = await smshub_api.get_number(service, country=user_country_code, max_price=max_price, operator=user_operator)

            if data.get("cancel_request"):
                logger.info(f"Запрос на получение номера для пользователя {user_id} был отменен.")
                return

            if result and result.startswith("ACCESS_NUMBER"):
                _, activation_id, number = result.split(":")
                logger.info(f"Пользователь {user_id} получил номер: {number}, активация ID: {activation_id}")

                if user_id not in user_activations:
                    user_activations[user_id] = {}
                user_activations[user_id][activation_id] = {
                    'number': number,
                    'status': 'Ожидание SMS',
                    'service': service,
                    'service_display': service_display,
                    'country_name': country_name,
                    'last_update': time.time(),
                    'task': None
                }

                keyboard = InlineKeyboardBuilder()
                keyboard.button(text="📂 Текущие активации", callback_data="current_activations")
                keyboard.button(text="❌ Отменить активацию", callback_data=f"cancel_activation:{activation_id}")
                keyboard.button(text="🔙 Главное меню", callback_data="back_to_main_menu")
                keyboard.adjust(1)

                message_text = (
                    f"Получен номер: `{number}`\n"
                    f"Страна: `{country_name}`\n"
                    f"Оператор: `{operator_display}`\n"
                    f"Сервис: `{service_display}`\n"
                    f"Максимальная стоимость: `{max_price:.2f}` $ (₽{max_price * user_data['usd_to_rub_rate']:.2f})\n"
                    f"Статус: Ожидание SMS\n"
                    f"Вы можете управлять активацией или вернуться в главное меню."
                )

                sent_message = await safe_edit_or_send_message(
                    message=message,
                    new_text=message_text,
                    state=state,
                    reply_markup=keyboard.as_markup(),
                    parse_mode="Markdown"
                )
                user_activations[user_id][activation_id]['message_id'] = sent_message.message_id

                await state.set_state(None)

                task = asyncio.create_task(poll_for_sms(
                    bot=message.bot,
                    user_id=user_id,
                    chat_id=message.chat.id,
                    activation_id=activation_id,
                    smshub_api=smshub_api
                ))
                user_activations[user_id][activation_id]['task'] = task
                return

            elif result == "NO_NUMBERS":
                logger.info(f"Attempt {attempt} out of {max_attempts}: No available numbers.")
                last_exception = result
                if attempt == max_attempts:
                    suggestion = (
                        "Попробуйте выбрать 'Любой оператор' в настройках или увеличить максимальную цену."
                        if user_operator != "any" else
                        "Попробуйте увеличить максимальную цену или повторите попытку позже."
                    )
                    keyboard = InlineKeyboardBuilder()
                    # Изменено: Заменяем "❌ Отмена" на "🔙 Назад", возвращая на текущую страницу сервисов
                    keyboard.button(text="🔙 Назад", callback_data=f"service_page:{current_page}")
                    await safe_edit_or_send_message(
                        message=message,
                        new_text=f"Нет доступных номеров для оператора {operator_display} по данной цене.\n{suggestion}",
                        state=state,
                        reply_markup=keyboard.as_markup()
                    )
                    return
            else:
                raise Exception("Unexpected API response")

        except Exception as e:
            logger.error(f"Error obtaining number for service '{service_code}', attempt {attempt}: {e}")
            last_exception = e

        await asyncio.sleep(5)

    if not success:
        data = await state.get_data()
        current_page = data.get('services_current_page', 0)
        keyboard = InlineKeyboardBuilder()
        # Изменено: Заменяем "❌ Отмена" на "🔙 Назад", возвращая на текущую страницу сервисов
        keyboard.button(text="🔙 Назад", callback_data=f"service_page:{current_page}")
        await safe_edit_or_send_message(
            message=message,
            new_text=f"Произошла ошибка при обработке запроса: {last_exception}. Попробуйте снова.",
            state=state,
            reply_markup=keyboard.as_markup()
        )

    if last_exception and last_exception != "NO_NUMBERS":
        data = await state.get_data()
        current_page = data.get('services_current_page', 0)
        keyboard = InlineKeyboardBuilder()
        # Изменено: Заменяем "❌ Отмена" на "🔙 Назад", возвращая на текущую страницу сервисов
        keyboard.button(text="🔙 Назад", callback_data=f"service_page:{current_page}")
        await safe_edit_or_send_message(
            message=message,
            new_text=f"Произошла ошибка при обработке запроса: {last_exception}. Попробуйте снова.",
            state=state,
            reply_markup=keyboard.as_markup()
        )

async def ignore_callback_handler(callback_query: types.CallbackQuery):
    try:
        await callback_query.answer()  # Just acknowledge to remove the loading state
    except Exception as e:
        logger.warning(f"Не удалось ответить на callback_query: {e}")

# Добавлено: Новый обработчик для кнопки "🔙 Назад" в результатах поиска
async def back_from_search_handler(callback_query: types.CallbackQuery, state: FSMContext):
    try:
        await callback_query.answer()
    except Exception as e:
        logger.warning(f"Не удалось ответить на callback_query: {e}")

    # Получаем данные из состояния
    data = await state.get_data()
    services_pages = data.get('services_pages', [])
    services_current_page = data.get('services_current_page', 0)  # Получаем текущую страницу сервисов
    services_with_numbers = data.get('services_with_numbers', set())
    total_services_with_numbers = data.get('total_services_with_numbers', 0)
    total_services_for_country = data.get('total_services_for_country', 0)
    country_name = data.get('country_name', 'Неизвестно')
    operator_name = data.get('operator_name', 'Любой')

    # Создаём клавиатуру для текущей страницы сервисов
    keyboard = build_service_keyboard(page_number=services_current_page, services_pages=services_pages, services_with_numbers=services_with_numbers)
    message_text = get_service_message_text(
        total_services_with_numbers, 
        total_services_for_country, 
        len(services_with_numbers),
        country_name,
        operator_name
    )

    # Редактируем сообщение
    await safe_edit_or_send_message(
        message=callback_query.message,
        new_text=message_text,
        state=state,
        reply_markup=keyboard.as_markup(),
        parse_mode="Markdown"
    )

    # Устанавливаем состояние ожидания выбора сервиса
    await state.set_state(SmsOrderState.waiting_for_service)

def register_sms_handlers(dp: Dispatcher):
    dp.callback_query.register(get_sms_handler, F.data == "get_sms")
    dp.callback_query.register(go_back_to_service_selection, F.data == "cancel_operation")
    dp.callback_query.register(back_to_main_menu, F.data == "back_to_main_menu")
    # Добавлено: Регистрация нового обработчика
    dp.callback_query.register(back_from_search_handler, F.data == "back_to_services")
    dp.callback_query.register(service_page_handler, F.data.startswith("service_page:"))
    dp.callback_query.register(service_selected_handler, F.data.startswith("service_select:"))
    dp.callback_query.register(handle_search_page_navigation, F.data.startswith("search_page:"))
    dp.callback_query.register(price_selected_handler, F.data.startswith("select_price:"))
    dp.callback_query.register(ignore_callback_handler, F.data == "ignore")
    dp.message.register(service_handler, SmsOrderState.waiting_for_service)
    dp.message.register(max_price_handler, SmsOrderState.waiting_for_max_price)