так, смотри, мне нужно внести некие исправления в мой код телеграм бота. когда я нажимаю "Получить смс" в главном меню у меня открывается список сервисов. Загружается сразу весь список сервисов, который находится в файле bot/constants/services.py, я хочу сделать так, чтобы загружался не весь список, а только те сервисы, котоыре доступны для данной странны в настройках пользовтаеля. т.е. когда я уже нажимаю на какой-то конкретный сервис, то происходит запрос по апи и выводится список всех сервисов и кол-во номеров для них, вот собственно исходя из этого я хочу, чтобы выводились только те сервисы, которые доступны.

/bot/api.py
import aiohttp
import logging
import json
from typing import Optional, Dict

logger = logging.getLogger(name)

BASE_URL = "https://smshub.org/stubs/handler_api.php"

class SmsHubAPI:
def init(self, api_key: str):
self.api_key = api_key


async def get_balance(self, currency: str = "643") -> Optional[str]:
    """Запрос баланса с заданной валютой (по умолчанию: RUB)."""
    logger.info(f"Запрос баланса с валютой: {currency}")
    params = {
        "api_key": self.api_key,
        "action": "getBalance",
        "currency": currency
    }
    async with aiohttp.ClientSession() as session:
        async with session.get(BASE_URL, params=params) as response:
            if response.status == 200:
                text = await response.text()
                logger.info(f"Ответ баланса: {text}")
                return text
            else:
                logger.error(f"Ошибка при запросе баланса: {response.status} - {text}")
                return None

async def get_number(self, service: str, country: str, operator: Optional[str] = None, max_price: Optional[float] = None, currency: str = "643") -> Optional[str]:
    """Получение номера для указанного сервиса и страны."""
    logger.info(f"Запрос номера для сервиса: {service}, страна: {country}, оператор: {operator}, макс. цена: {max_price}, валюта: {currency}")
    params = {
        "api_key": self.api_key,
        "action": "getNumber",
        "service": service,
        "country": country,
        "currency": currency
    }
    if operator and operator != "any":
        params["operator"] = operator
    if max_price:
        params["maxPrice"] = max_price

    async with aiohttp.ClientSession() as session:
        async with session.get(BASE_URL, params=params) as response:
            text = await response.text()
            if response.status == 200:
                logger.info(f"Ответ номера: {text}")
                return text
            logger.error(f"Ошибка при запросе номера: {response.status} - {text}")
            return None

async def get_price_for_service(self, service: str, country: str, currency: str = "643") -> Optional[Dict]:
    """Получение цен для указанного сервиса."""
    logger.info(f"Запрос цен для сервиса: {service}, страна: {country}, валюта: {currency}")
    params = {
        "api_key": self.api_key,
        "action": "getPrices",
        "service": service,
        "country": country,
        "currency": currency
    }

    async with aiohttp.ClientSession() as session:
        async with session.get(BASE_URL, params=params) as response:
            response_text = await response.text()
            if response.status == 200:
                try:
                    prices_data = json.loads(response_text)
                    logger.info(f"Цены для сервиса {service}: {prices_data}")
                    country_data = prices_data.get(country)
                    if not country_data:
                        logger.error(f"Страна '{country}' отсутствует в данных: {prices_data}")
                        return None
                    service_data = country_data.get(service)
                    if not service_data:
                        logger.error(f"Сервис '{service}' отсутствует среди доступных для страны '{country}'. Данные: {country_data}")
                        return None
                    return service_data  # Возвращает словарь { "Цена": Количество }
                except json.JSONDecodeError:
                    logger.error(f"Ошибка парсинга JSON: {response_text}")
                    return None
            else:
                logger.error(f"Ошибка при запросе цен: {response.status} - {response_text}")
                return None

async def get_numbers_status(self, country: str, operator: Optional[str] = None) -> Optional[Dict]:
    """Получение статуса номеров по стране и оператору."""
    logger.info(f"Запрос статуса номеров для страны: {country}, оператор: {operator}")
    params = {
        "api_key": self.api_key,
        "action": "getNumbersStatus",
        "country": country
    }
    if operator and operator != "any":
        params["operator"] = operator
    async with aiohttp.ClientSession() as session:
        async with session.get(BASE_URL, params=params) as response:
            response_text = await response.text()
            if response.status == 200:
                try:
                    numbers_status = json.loads(response_text)
                    logger.info(f"Статус номеров: {numbers_status}")
                    return numbers_status  # Возвращает словарь { "SERVICE_CODE": Количество }
                except json.JSONDecodeError:
                    logger.error(f"Ошибка парсинга JSON: {response_text}")
                    return None
            else:
                logger.error(f"Ошибка при запросе статуса номеров: {response.status} - {response_text}")
                return None

async def get_status(self, activation_id: str) -> Optional[str]:
    """Получение статуса активации по ID."""
    logger.info(f"Запрос статуса для активации: {activation_id}")
    params = {
        "api_key": self.api_key,
        "action": "getStatus",
        "id": activation_id
    }
    async with aiohttp.ClientSession() as session:
        async with session.get(BASE_URL, params=params) as response:
            text = await response.text()
            if response.status == 200:
                logger.info(f"Ответ статуса: {text}")
                return text
            logger.error(f"Ошибка при запросе статуса: {response.status} - {text}")
            return None

async def set_status(self, activation_id: str, status: str) -> Optional[str]:
    """Установка статуса для активации."""
    logger.info(f"Изменение статуса активации: {activation_id} на статус {status}")
    params = {
        "api_key": self.api_key,
        "action": "setStatus",
        "id": activation_id,
        "status": status
    }
    async with aiohttp.ClientSession() as session:
        async with session.get(BASE_URL, params=params) as response:
            text = await response.text()
            if response.status == 200:
                logger.info(f"Ответ изменения статуса: {text}")
                return text
            logger.error(f"Ошибка при изменении статуса: {response.status} - {text}")
            return None

async def cancel_activation(self, activation_id: str) -> Optional[str]:
    """Отмена активации."""
    return await self.set_status(activation_id, status="8")
import asyncio
import logging
import time
import json
from pathlib import Path
from typing import Optional, List, Tuple
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():
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):
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():
popularity_data = load_popularity_data()
sorted_services = sorted(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 = 0) -> InlineKeyboardBuilder:
sorted_services = get_sorted_services()
services_pages = get_service_pages(sorted_services)
keyboard = InlineKeyboardBuilder()


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

    # Добавляет кнопки сервисов в сетке 3x6
    for code, name in current_page:
        keyboard.button(text=name, callback_data=f"service_select:{code}")
    
    # Настраивает кнопки навигации
    if page_number == 0:
        # Первая страница - только кнопка "Вперед"
        keyboard.adjust(3)  # Устраиваем сервисные кнопки в 3 столбца
        keyboard.row(types.InlineKeyboardButton(text="➡️ Вперед", callback_data=f"service_page:{page_number + 1}"))
    elif page_number == len(services_pages) - 1:
        # Последняя страница - только кнопка "Назад"
        keyboard.adjust(3)
        keyboard.row(types.InlineKeyboardButton(text="⬅️ Назад", callback_data=f"service_page:{page_number - 1}"))
    else:
        # Промежуточные страницы - кнопки "Назад" и "Вперед"
        keyboard.adjust(3)
        keyboard.row(
            types.InlineKeyboardButton(text="⬅️ Назад", callback_data=f"service_page:{page_number - 1}"),
            types.InlineKeyboardButton(text="➡️ Вперед", callback_data=f"service_page:{page_number + 1}")
        )

# Кнопка "Назад в меню" на всех страницах
keyboard.row(types.InlineKeyboardButton(text="🔙 Назад в меню", callback_data="back_to_main_menu"))

return keyboard
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)
if not user_api_key:
    keyboard = InlineKeyboardBuilder()
    keyboard.button(text="⚙️ Настройки", callback_data="settings")
    keyboard.button(text="🔙 Назад", callback_data="back_to_main_menu")
    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} начал процесс получения номера.")
keyboard = build_service_keyboard()

await state.update_data(cancel_request=False, current_task=None)

await safe_edit_or_send_message(
    message=callback_query.message,
    new_text=(
        "Выберите сервис, нажав на кнопку ниже.\n"
        "Если вы хотите, вы также можете ввести полное название сервиса или хотя бы часть его названия, чтобы получить результаты поиска."
    ),
    state=state,
    reply_markup=keyboard.as_markup()
)
await state.set_state(SmsOrderState.waiting_for_service)
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()

await safe_edit_or_send_message(
    message=callback_query.message,
    new_text=(
        "Выберите сервис, нажав на кнопку ниже.\n"
        "Если вы хотите, вы также можете ввести полное название сервиса или хотя бы часть его названия, чтобы получить результаты поиска."
    ),
    state=state,
    reply_markup=build_service_keyboard().as_markup()
)

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 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 = int(callback_query.data.split(":")[1])
keyboard = build_service_keyboard(page_number)

await safe_edit_or_send_message(
    message=callback_query.message,
    new_text=(
        "Выберите сервис, нажав на кнопку ниже.\n"
        "Если вы хотите, вы также можете ввести полное название сервиса или хотя бы часть его названия, чтобы получить результаты поиска."
    ),
    state=state,
    reply_markup=keyboard.as_markup()
)
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, service_code: Optional[str] = None, 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}")

service_code = next((code for code, name in SERVICE_DISPLAY_NAMES.items() if name.lower() == user_input or code.lower() == user_input), None)

if service_code is None:
    matched_services = [(code, name) for code, name in SERVICE_DISPLAY_NAMES.items() if user_input in name.lower()]

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

        keyboard.adjust(3)
        keyboard.button(text="🔙 Назад", callback_data="service_page:0")
        await safe_edit_or_send_message(
            message=message,
            new_text="Пожалуйста, выберите один из представленных сервисов:",
            state=state,
            reply_markup=keyboard.as_markup()
        )
        return
    else:
        keyboard = InlineKeyboardBuilder()
        keyboard.button(text="🔙 Назад к сервисам", callback_data="service_page:0")
        await safe_edit_or_send_message(
            message=message,
            new_text="Сервис не найден. Попробуйте ввести код или полное название сервиса.",
            state=state,
            reply_markup=keyboard.as_markup()
        )
        return
else:
    await state.update_data(selected_service=service_code, cancel_request=False)
    task = asyncio.create_task(service_handler_logic(message, state, user_id=user_id, service_code=service_code))
    await state.update_data(current_task=task)
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

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

        try:
            progress_text = f"Идет запрос {attempt}/{max_attempts}..."
            keyboard = InlineKeyboardBuilder()
            keyboard.button(text="❌ Отмена", callback_data="cancel_operation")
            keyboard.adjust(1)

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

            price_info = await smshub_api.get_price_for_service(service_code, country=user_country_code)
            numbers_status = await smshub_api.get_numbers_status(country=user_country_code, operator=user_operator)

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

            if price_info and numbers_status:
                service_key = f"{service_code}_0"
                available_numbers = int(numbers_status.get(service_key, 0) or 0)
                
                if available_numbers > 0:
                    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="cancel_operation"))

                        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}: Цены не найдены.")
                        last_exception = "Цены не найдены"
                else:
                    logger.info(f"Попытка {attempt}: Нет доступных номеров.")
                    last_exception = "Нет доступных номеров"

        except Exception as e:
            logger.error(f"Ошибка при получении данных для сервиса '{service_code}', попытка {attempt}: {e}")
            last_exception = e

        await asyncio.sleep(5)

except asyncio.CancelledError:
    logger.info(f"Задача для пользователя {user_id} была отменена.")
    return

if not success:
    keyboard = InlineKeyboardBuilder()
    keyboard.button(text="Назад", callback_data="service_page:0")
    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 = float(callback_query.data.split(":")[1])
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')
    max_price_usd = user_data.get('max_price_usd')
    min_price_rub = user_data.get('min_price_rub')
    max_price_rub = user_data.get('max_price_rub')

    keyboard = InlineKeyboardBuilder()
    keyboard.button(text="❌ Отмена", callback_data="cancel_operation")
    keyboard.adjust(1)
    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'):
    keyboard = InlineKeyboardBuilder()
    keyboard.button(text="❌ Отмена", callback_data="cancel_operation")
    keyboard.adjust(1)
    await safe_edit_or_send_message(
        message=message,
        new_text=f"Максимальная стоимость должна быть не менее `{data.get('min_price'):.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

try:
    for attempt in range(1, max_attempts + 1):
        data = await state.get_data()

        if data.get("cancel_request"):
            logger.info(f"Запрос на получение номера для пользователя {user_id} был отменен.")
            return
        
        progress_text = f"Идет запрос {attempt}/{max_attempts} на получение номера..."
        keyboard = InlineKeyboardBuilder()
        keyboard.button(text="❌ Отмена", callback_data="cancel_operation")
        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"

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

        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} из {max_attempts}: нет доступных номеров.")
            last_exception = result
            if attempt == max_attempts:
                suggestion = (
                    "Попробуйте выбрать 'Любой оператор' в настройках или увеличить максимальную цену."
                    if user_operator != "any" else
                    "Попробуйте увеличить максимальную цену или повторите попытку позже."
                )
                keyboard = InlineKeyboardBuilder()
                keyboard.button(text="Назад", callback_data="service_page:0")
                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("Неожиданный ответ API")

        await asyncio.sleep(5)

except asyncio.CancelledError:
    logger.info(f"Задача для пользователя {user_id} была отменена.")
    return

if last_exception and last_exception != "NO_NUMBERS":
    keyboard = InlineKeyboardBuilder()
    keyboard.button(text="Назад", callback_data="service_page:0")
    await safe_edit_or_send_message(
        message=message,
        new_text=f"Произошла ошибка при обработке запроса: {last_exception}. Попробуйте снова.",
        state=state,
        reply_markup=keyboard.as_markup()
    )
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(service_page_handler, F.data.startswith("service_page:"))
dp.callback_query.register(service_selected_handler, F.data.startswith("service_select:"))
dp.callback_query.register(price_selected_handler, F.data.startswith("select_price:"))
dp.message.register(service_handler, SmsOrderState.waiting_for_service)
dp.message.register(max_price_handler, SmsOrderState.waiting_for_max_price)