ASSISTANT
Конечно! Вот обновленный полный код, основанный на предложенных улучшениях:
```python
import asyncio
import logging
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")
service_popularity = {}
def load_popularity_data():
global service_popularity
if POPULARITY_FILE.exists():
with open(POPULARITY_FILE, 'r', encoding='utf-8') as f:
service_popularity = json.load(f)
def save_popularity_data():
with open(POPULARITY_FILE, 'w', encoding='utf-8') as f:
json.dump(service_popularity, f, ensure_ascii=False, indent=4)
load_popularity_data()
def update_service_popularity(service_code: str):
if service_code in service_popularity:
service_popularity[service_code] += 1
else:
service_popularity[service_code] = 1
save_popularity_data()
def get_sorted_services(available_services: List[str]) -> List[Tuple[str, str]]:
sorted_services = sorted(
[(code, name) for code, name in SERVICES if code in available_services],
key=lambda service: service_popularity.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]]]:
return [services[i:i + page_size] for i in range(0, len(services), page_size)]
def build_service_keyboard(page_number: int, available_services: List[str], services_with_numbers: set) -> InlineKeyboardBuilder:
sorted_services = get_sorted_services(available_services)
services_pages = get_service_pages(sorted_services)
keyboard = InlineKeyboardBuilder()
if page_number < len(services_pages):
current_page = services_pages[page_number]
for code, name in current_page:
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:
nav_buttons = []
if page_number > 0:
nav_buttons.append(types.InlineKeyboardButton(text="⬅️ Назад", callback_data=f"service_page:{page_number - 1}"))
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)
keyboard.row(types.InlineKeyboardButton(text="🔙 Назад в меню", callback_data="back_to_main_menu"))
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 handle_transient_state(state: FSMContext, key: str, attempt: int, max_attempts: int, progress_text: str, cancel_key: str):
data = await state.get_data()
if data.get(cancel_key):
logger.info("Операция отменена.")
return
keyboard = InlineKeyboardBuilder()
keyboard.button(text="❌ Отмена", callback_data="cancel_operation")
keyboard.adjust(1)
await safe_edit_or_send_message(
message=data.get(key),
new_text=f"{progress_text} (попытка {attempt + 1}/{max_attempts})...",
state=state,
reply_markup=keyboard.as_markup()
)
async def fetch_all_services(user_api_key: str, user_country_code: str, state: FSMContext) -> Tuple[Optional[List[str]], int, int]:
smshub_api = SmsHubAPI(user_api_key)
max_attempts = 3
available_services, services_with_numbers = [], set()
for attempt in range(max_attempts):
try:
numbers_status = await smshub_api.get_numbers_status(country=user_country_code)
if numbers_status:
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:
logger.error(f"Ошибка при запросе статуса всех сервисов, попытка {attempt + 1}: {e}")
await handle_transient_state(state, 'message', attempt, max_attempts, 'Получение списка сервисов...', "cancel_request")
await asyncio.sleep(5)
return available_services, 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_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} начал процесс получения номера.")
await state.update_data(message=callback_query.message, cancel_request=False)
available_services, total_services_with_numbers, total_services_for_country = await fetch_all_services(user_api_key, user_country_code, state)
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)
if available_services is None:
await send_main_menu(callback_query.message, state)
return
smshub_api = SmsHubAPI(user_api_key)
numbers_status = await smshub_api.get_numbers_status(country=user_country_code, operator=user_operator)
services_with_numbers = {service.split('_')[0] for service, count in numbers_status.items() if int(count) > 0}
services_with_operator = len(services_with_numbers)
keyboard = build_service_keyboard(page_number=0, available_services=available_services, services_with_numbers=services_with_numbers)
message_text = get_service_message_text(total_services_with_numbers, total_services_for_country, 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)
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', 'Любой')
smshub_api = SmsHubAPI(get_user_api_key(callback_query.from_user.id))
numbers_status = await smshub_api.get_numbers_status(country=get_user_country(callback_query.from_user.id), operator=get_user_operator(callback_query.from_user.id))
services_with_numbers = {service.split('_')[0] for service, count in numbers_status.items() if int(count) > 0}
services_with_operator = len(services_with_numbers)
keyboard = build_service_keyboard(page_number=0, available_services=available_services, services_with_numbers=services_with_numbers)
message_text = get_service_message_text(total_services_with_numbers, total_services_for_country, 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)
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])
data = await state.get_data()
available_services = data.get('available_services', [])
smshub_api = SmsHubAPI(get_user_api_key(callback_query.from_user.id))
numbers_status = await smshub_api.get_numbers_status(country=get_user_country(callback_query.from_user.id), operator=get_user_operator(callback_query.from_user.id))
services_with_numbers = {service.split('_')[0] for service, count in numbers_status.items() if int(count) > 0}
services_with_operator = len(services_with_numbers)
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', 'Любой')
keyboard = build_service_keyboard(page_number, available_services, services_with_numbers)
message_text = get_service_message_text(total_services_with_numbers, total_services_for_country, 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"
)
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', [])
smshub_api = SmsHubAPI(get_user_api_key(user_id))
numbers_status = await smshub_api.get_numbers_status(country=get_user_country(user_id), operator=get_user_operator(user_id))
services_with_numbers = {service.split('_')[0] for service, count in numbers_status.items() if int(count) > 0}
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_page_keyboard(pages, page_number, services_with_numbers)
await state.update_data(matched_services=matched_services)
await safe_edit_or_send_message(
message=message,
new_text="Пожалуйста, выберите один из представленных сервисов:\n_🔴 - нет номеров_",
state=state,
reply_markup=keyboard.as_markup(),
parse_mode="Markdown"
)
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()
)
def build_page_keyboard(service_pages, page_number: int, services_with_numbers: set) -> InlineKeyboardBuilder:
keyboard = InlineKeyboardBuilder()
if page_number < len(service_pages):
current_page = service_pages[page_number]
for code, name in current_page:
if code not in services_with_numbers:
name += " 🔴"
keyboard.button(text=name, callback_data=f"service_select:{code}")
keyboard.adjust(3)
if len(service_pages) > 1:
nav_buttons = []
if page_number > 0:
nav_buttons.append(types.InlineKeyboardButton(text="⬅️ Назад", callback_data=f"search_page:{page_number - 1}"))
nav_buttons.append(types.InlineKeyboardButton(text=f"Страница {page_number + 1}/{len(service_pages)}", callback_data="ignore"))
if page_number < len(service_pages) - 1:
nav_buttons.append(types.InlineKeyboardButton(text="➡️ Вперед", callback_data=f"search_page:{page_number + 1}"))
keyboard.row(*nav_buttons)
keyboard.row(types.InlineKeyboardButton(text="🔙 Назад к сервисам", callback_data="service_page:0"))
return keyboard
async def handle_search_page_navigation(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])
data = await state.get_data()
matched_services = data.get('matched_services', [])
smshub_api = SmsHubAPI(get_user_api_key(callback_query.from_user.id))
numbers_status = await smshub_api.get_numbers_status(country=get_user_country(callback_query.from_user.id), operator=get_user_operator(callback_query.from_user.id))
services_with_numbers = {service.split('_')[0] for service, count in numbers_status.items() if int(count) > 0}
pages = get_service_pages(matched_services)
keyboard = build_page_keyboard(pages, page_number, services_with_numbers)
await safe_edit_or_send_message(
message=callback_query.message,
new_text="Пожалуйста, выберите один из представленных сервисов:\n_🔴 - нет номеров_",
state=state,
reply_markup=keyboard.as_markup(),
parse_mode="Markdown"
)
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(max_attempts):
data = await state.get_data()
if data.get("cancel_request"):
logger.info(f"Запрос для пользователя {user_id} был отменен.")
return
try:
progress_text = f"Идет запрос {attempt + 1}/{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 + 1}: Цены не найдены.")
last_exception = "Цены не найдены"
else:
logger.info(f"Попытка {attempt + 1}: Нет доступных номеров.")
last_exception = "Нет доступных номеров"
except Exception as e:
logger.error(f"Ошибка при получении данных для сервиса '{service_code}', попытка {attempt + 1}: {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', 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)
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', 0):
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', 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
try:
for attempt in range(max_attempts):
data = await state.get_data()
if data.get("cancel_request"):
logger.info(f"Запрос на получение номера для пользователя {user_id} был отменен.")
return
progress_text = f"Идет запрос {attempt + 1}/{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 + 1} из {max_attempts}: нет доступных номеров.")
last_exception = result
if attempt == max_attempts - 1:
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()
)
# Add a handler for the "ignore" callback to silently ignore it
async def ignore_callback_handler(callback_query: types.CallbackQuery):
try:
await callback_query.answer()
except Exception as e:
logger.warning(f"Не удалось ответить на callback_query: {e}")
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(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)
```
Эта версия кода содержит ранее предложенные улучшения: оптимизированную работу с файлами, упрощение асинхронных операций и улучшенную обработку состояния. Код стал более организованным и понятным. Если у вас есть дополнительные вопросы или требуется помощь, пожалуйста, дайте знать!