USER
так, смотри, у меня есть телеграм бот для работы с сервисом по покупке сотовыйх номеров и получения смс на них, мой бот работает и вроде все в порядке, но бот слишком сложно у меня структурирован, я хочу, чтобы ты помог мне улучшить код бота, но тчобы он раотал точно так же, без ошибок и проблем, возможно это ускорит его работу и оптимирзурет, возможно ты что-то сможешь оптимизирвоат, в общем я тебе присылаю все файлы бота, а ты передалй их, как можно лучше и пришли мне готовый резульатт. так же я тебе отправлю документацию по работе с апи сервисом, может ты найдешь какие-то ошбики,
1. /config.py
import os
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv("API_KEY")
TELEGRAM_TOKEN = os.getenv("TELEGRAM_TOKEN")
ADMIN_CHAT_ID = "389669884"
2. /main.py
# main.py
import asyncio
import logging
from aiogram import Bot, Dispatcher
from aiogram.fsm.storage.memory import MemoryStorage
from bot.handlers import router, on_startup # Импортируйте роутер и on_startup
API_TOKEN = "7625597085:AAGfqRVnE75JYUEdiI045jAKzd8jJ-ZiHLI" # Замените на ваш токен
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
async def main():
# Инициализация бота и диспетчера
bot = Bot(token=API_TOKEN)
storage = MemoryStorage()
dp = Dispatcher(storage=storage)
# Регистрация роутера
dp.include_router(router)
# Регистрация функции on_startup
dp.startup.register(on_startup)
try:
logging.info("Запуск бота...")
await dp.start_polling(bot)
finally:
await bot.close()
if __name__ == "__main__":
asyncio.run(main())
3. /bot/api.py
import aiohttp
import logging
import json
BASE_URL = "https://smshub.org/stubs/handler_api.php"
class SmsHubAPI:
def __init__(self, api_key):
self.api_key = api_key
async def get_balance(self):
logging.info("Запрос баланса с валютой: 643")
async with aiohttp.ClientSession() as session:
async with session.get(BASE_URL, params={
"api_key": self.api_key,
"action": "getBalance",
"currency": "643"
}) as response:
if response.status == 200:
text = await response.text()
logging.info(f"Ответ баланса: {text}")
return text
logging.error(f"Ошибка при запросе баланса: {response.status}")
return None
async def get_number(self, service, country="0", operator=None, max_price=None):
logging.info(f"Запрос номера для сервиса: {service}, страна: {country}, оператор: {operator}, макс. цена: {max_price}, валюта: 643")
params = {
"api_key": self.api_key,
"action": "getNumber",
"service": service,
"country": country,
"currency": "643"
}
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:
if response.status == 200:
text = await response.text()
logging.info(f"Ответ номера: {text}")
return text
logging.error(f"Ошибка при запросе номера: {response.status}")
return None
async def get_price_for_service(self, service, country="0"):
logging.info(f"Запрос цен для сервиса: {service}, страна: {country}, валюта: 643")
params = {
"api_key": self.api_key,
"action": "getPrices",
"service": service,
"country": country,
"currency": "643"
}
async with aiohttp.ClientSession() as session:
async with session.get(BASE_URL, params=params) as response:
if response.status == 200:
response_text = await response.text()
try:
prices_data = json.loads(response_text)
logging.info(f"Цены для сервиса {service}: {prices_data}")
country_data = prices_data.get(country)
if not country_data:
logging.error(f"Страна '{country}' отсутствует в данных: {prices_data}")
return None
service_data = country_data.get(service)
if not service_data:
logging.error(f"Сервис '{service}' отсутствует среди доступных для страны '{country}'.")
return None
return service_data # Возвращаем словарь { "Цена": Количество }
except json.JSONDecodeError:
# Обрабатываем случаи, когда ответ не является JSON (например, ошибки от API)
logging.error(f"Ошибка от API при получении цен: {response_text}")
return None
else:
logging.error(f"Ошибка при запросе цен: {response.status}")
return None
async def get_numbers_status(self, country="0", operator=None):
logging.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:
if response.status == 200:
response_text = await response.text()
try:
numbers_status = json.loads(response_text)
logging.info(f"Статус номеров: {numbers_status}")
return numbers_status # Возвращает словарь { "SERVICE_CODE": Количество }
except json.JSONDecodeError:
logging.error(f"Ошибка при получении статуса номеров: {response_text}")
return None
else:
logging.error(f"Ошибка при запросе статуса номеров: {response.status}")
return None
async def get_status(self, activation_id):
logging.info(f"Запрос статуса для активации: {activation_id}")
async with aiohttp.ClientSession() as session:
async with session.get(BASE_URL, params={
"api_key": self.api_key,
"action": "getStatus",
"id": activation_id
}) as response:
if response.status == 200:
text = await response.text()
logging.info(f"Ответ статуса: {text}")
return text
logging.error(f"Ошибка при запросе статуса: {response.status}")
return None
async def set_status(self, activation_id, status):
logging.info(f"Изменение статуса активации: {activation_id} на статус {status}")
async with aiohttp.ClientSession() as session:
async with session.get(BASE_URL, params={
"api_key": self.api_key,
"action": "setStatus",
"id": activation_id,
"status": status
}) as response:
if response.status == 200:
text = await response.text()
logging.info(f"Ответ изменения статуса: {text}")
return text
logging.error(f"Ошибка при изменении статуса: {response.status}")
return None
async def cancel_activation(self, activation_id):
return await self.set_status(activation_id, status="8")
4. /bot/data.py
import json
import os
import threading
import logging
logger = logging.getLogger(__name__)
# Путь к файлу для хранения данных пользователей
DATA_FILE = os.path.join(os.path.dirname(__file__), "..", "data.json")
lock = threading.RLock() # Используем RLock()
# Загружаем данные из файла при запуске
user_data = {}
def load_data():
"""Загружает данные из JSON файла."""
global user_data
with lock:
if os.path.exists(DATA_FILE):
try:
with open(DATA_FILE, "r", encoding="utf-8") as file:
user_data = json.load(file)
logger.info("Данные пользователей успешно загружены.")
except json.JSONDecodeError:
user_data = {}
logger.warning("Не удалось декодировать JSON из файла. Используем пустые данные.")
else:
user_data = {}
logger.info("Файл данных не найден. Используем пустые данные.")
# Вызываем load_data() при импорте модуля
load_data()
def save_data():
"""Сохраняет текущие данные в JSON файл."""
with lock:
with open(DATA_FILE, "w", encoding="utf-8") as file:
json.dump(user_data, file, ensure_ascii=False, indent=4)
logger.info("Данные пользователей успешно сохранены.")
# Методы для работы с API ключами
def set_user_api_key(user_id, api_key):
"""Сохраняет API ключ для конкретного пользователя."""
with lock:
user = user_data.get(str(user_id), {})
user["api_key"] = api_key
user_data[str(user_id)] = user
save_data()
logger.info(f"API ключ для пользователя {user_id} сохранён.")
def get_user_api_key(user_id):
"""Возвращает API ключ для конкретного пользователя, если он сохранен."""
with lock:
return user_data.get(str(user_id), {}).get("api_key")
def delete_user_api_key(user_id):
"""Удаляет API ключ для конкретного пользователя."""
with lock:
user = user_data.get(str(user_id), {})
if "api_key" in user:
del user["api_key"]
if not user:
del user_data[str(user_id)]
save_data()
logger.info(f"API ключ для пользователя {user_id} удалён.")
else:
logger.warning(f"У пользователя {user_id} нет API ключа для удаления.")
# Методы для работы с оператором
def set_user_operator(user_id, operator):
"""Сохраняет выбранный оператор для конкретного пользователя."""
with lock:
user = user_data.get(str(user_id), {})
user["operator"] = operator
user_data[str(user_id)] = user
save_data()
logger.info(f"Оператор '{operator}' для пользователя {user_id} сохранён.")
def get_user_operator(user_id):
"""Возвращает выбранный оператор для конкретного пользователя, если он сохранен."""
with lock:
return user_data.get(str(user_id), {}).get("operator")
def delete_user_operator(user_id):
"""Удаляет выбранный оператор для конкретного пользователя."""
with lock:
user = user_data.get(str(user_id), {})
if "operator" in user:
del user["operator"]
if not user:
del user_data[str(user_id)]
save_data()
logger.info(f"Оператор для пользователя {user_id} удалён.")
else:
logger.warning(f"У пользователя {user_id} нет оператора для удаления.")
5. /bot/handlers.py
import asyncio
import logging
import time
from datetime import datetime
from typing import Optional, Dict
import aiohttp
from aiogram import Bot, Dispatcher, Router, types, F
from aiogram.filters import Command
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from aiogram.utils.markdown import bold, code, italic
from aiogram.utils.keyboard import InlineKeyboardBuilder
from .data import (
get_user_api_key,
set_user_api_key,
delete_user_api_key,
set_user_operator,
get_user_operator
)
from .api import SmsHubAPI
EXCHANGE_RATE_API_URL = "https://api.exchangerate-api.com/v4/latest/USD"
USD_TO_RUB_RATE_TTL = 3600 # Кэшируем курс на 1 час
router = Router()
logger = logging.getLogger(__name__)
# Переменные для кэширования курса
_cached_usd_to_rub_rate = None
_cached_usd_to_rub_rate_time = 0
# Словарь для хранения активных активаций пользователей: {user_id: {activation_id: ActivationData}}
user_activations: Dict[int, Dict[str, Dict]] = {}
# Словарь отображения кодов операторов на русские названия
OPERATOR_DISPLAY_NAMES = {
"any": "Любой оператор",
"aiva": "AIVA",
"beeline": "Билайн",
"center2m": "Центр 2М",
"danycom": "Danycom",
"ezmobile": "EZ-mobile",
"gazprombank_mobile": "Газпромбанк Мобайл",
"lycamobile": "Lycamobile",
"matrix": "Matrix",
"mcn": "MCN Telecom",
"megafon": "Мегафон",
"motiv": "Мотив",
"mts": "МТС",
"mtt": "МТТ",
"mtt_virtual": "МТТ Виртуальный",
"rostelecom": "Ростелеком",
"sber": "СберМобайл",
"simsim": "SimSim",
"tele2": "Tele2",
"tinkoff": "Тинькофф Мобайл",
"ttk": "ТТК",
"vtb_mobile": "ВТБ Мобайл",
"winmobile": "Win Mobile",
"yota": "Йота"
}
ADMIN_USER_ID = 389669884 # Ваш user_id для получения уведомлений
async def get_usd_to_rub_rate() -> Optional[float]:
"""Получить текущий курс USD к RUB с кэшированием."""
global _cached_usd_to_rub_rate, _cached_usd_to_rub_rate_time
now = time.time()
if _cached_usd_to_rub_rate and (now - _cached_usd_to_rub_rate_time < USD_TO_RUB_RATE_TTL):
return _cached_usd_to_rub_rate
try:
async with aiohttp.ClientSession() as session:
async with session.get(EXCHANGE_RATE_API_URL) as response:
data = await response.json()
rate = data.get('rates', {}).get('RUB')
if rate:
_cached_usd_to_rub_rate = rate
_cached_usd_to_rub_rate_time = now
return rate
else:
logger.error("Курс RUB не найден в ответе.")
return None
except Exception as e:
logger.error(f"Ошибка при запросе курса валют: {e}")
return None
class UserSettings(StatesGroup):
entering_api_key = State()
class SmsOrderState(StatesGroup):
waiting_for_service = State()
waiting_for_max_price = State()
class OperatorSettings(StatesGroup):
selecting_operator = State()
# Хелпер-функция для безопасного редактирования сообщения или удаления и отправки нового
async def safe_edit_or_send_message(message: types.Message, new_text: str, state: FSMContext, reply_markup=None, parse_mode=None):
try:
await message.edit_text(new_text, reply_markup=reply_markup, parse_mode=parse_mode)
# Сохраняем message_id редактированного сообщения
await state.update_data(last_message_id=message.message_id)
except Exception as e:
logger.error(f"Failed to edit message: {e}")
# Если редактирование не удалось, пытаемся удалить предыдущее сообщение бота
data = await state.get_data()
last_message_id = data.get('last_message_id')
if last_message_id:
try:
await message.bot.delete_message(chat_id=message.chat.id, message_id=last_message_id)
except Exception as del_exc:
logger.error(f"Failed to delete message: {del_exc}")
# Отправляем новое сообщение и сохраняем его message_id
sent_message = await message.answer(new_text, reply_markup=reply_markup, parse_mode=parse_mode)
await state.update_data(last_message_id=sent_message.message_id)
async def safe_delete_message(bot: Bot, chat_id: int, message_id: int):
try:
await bot.delete_message(chat_id=chat_id, message_id=message_id)
except Exception as e:
logger.error(f"Failed to delete message: {e}")
@router.message(Command('start'))
async def start_handler(message: types.Message, state: FSMContext):
logger.info(f"Пользователь {message.from_user.id} запустил бота.")
# Устанавливаем оператор "any" по умолчанию, если не установлен
user_operator = get_user_operator(message.from_user.id)
if not user_operator:
set_user_operator(message.from_user.id, "any")
await send_main_menu(message, state)
async def send_main_menu(message: types.Message, state: FSMContext):
logger.info(f"Пользователь {message.chat.id} вернулся в главное меню.")
keyboard = InlineKeyboardBuilder()
keyboard.button(text="💰 Проверить баланс", callback_data="check_balance")
keyboard.button(text="📲 Получить SMS", callback_data="get_sms")
keyboard.button(text="📋 Текущие активации", callback_data="current_activations")
keyboard.button(text="⚙️ Настройки", callback_data="settings")
keyboard.adjust(1)
await safe_edit_or_send_message(
message=message,
new_text="Добро пожаловать! Выберите действие:",
state=state,
reply_markup=keyboard.as_markup()
)
async def show_settings_menu(callback_query: types.CallbackQuery, state: FSMContext, user_id: int):
user_api_key = get_user_api_key(user_id)
user_operator = get_user_operator(user_id)
if not user_operator:
# Устанавливаем оператор "any" по умолчанию, если не установлен
set_user_operator(user_id, "any")
user_operator = "any"
operator_display_name = OPERATOR_DISPLAY_NAMES.get(user_operator, user_operator)
keyboard = InlineKeyboardBuilder()
# Настройки API ключа
if user_api_key:
keyboard.button(text="✏️ Изменить API ключ", callback_data="edit_api_key")
keyboard.button(text="❌ Удалить API ключ", callback_data="delete_api_key")
else:
keyboard.button(text="➕ Добавить API ключ", callback_data="add_api_key")
# Настройки оператора
keyboard.button(text="✏️ Изменить оператора", callback_data="edit_operator")
keyboard.button(text="🔙 Назад", callback_data="back_to_main_menu")
keyboard.adjust(1)
message_text = (
f"Ваш текущий API ключ: `{user_api_key}`\n" if user_api_key else "API ключ не настроен.\n"
)
message_text += (
f"Текущий оператор: `{operator_display_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.clear()
@router.callback_query(F.data == "settings")
async def settings_handler(callback_query: types.CallbackQuery, state: FSMContext):
await callback_query.answer()
await show_settings_menu(callback_query, state, callback_query.from_user.id)
@router.callback_query(F.data.in_({"edit_api_key", "add_api_key"}))
async def input_api_key_handler(callback_query: types.CallbackQuery, state: FSMContext):
await callback_query.answer()
keyboard = InlineKeyboardBuilder()
keyboard.button(text="❌ Отмена", callback_data="cancel_settings")
keyboard.adjust(1)
await safe_edit_or_send_message(
message=callback_query.message,
new_text="Введите ваш API ключ для работы с сервисом:",
state=state,
reply_markup=keyboard.as_markup()
)
await state.set_state(UserSettings.entering_api_key)
@router.callback_query(F.data == "delete_api_key")
async def delete_user_api_key_handler(callback_query: types.CallbackQuery, state: FSMContext):
await callback_query.answer()
logger.info(f"Пользователь {callback_query.from_user.id} запросил удаление API ключа.")
try:
delete_user_api_key(callback_query.from_user.id)
await safe_edit_or_send_message(
message=callback_query.message,
new_text="API ключ успешно удалён.",
state=state
)
await send_main_menu(callback_query.message, state)
await state.clear()
except Exception as e:
logger.exception(f"Ошибка при удалении API ключа: {e}")
await callback_query.message.answer("Не удалось удалить API ключ. Попробуйте снова.")
@router.callback_query(F.data == "back_to_main_menu")
async def back_to_main_menu_handler(callback_query: types.CallbackQuery, state: FSMContext):
await callback_query.answer()
await state.clear()
await send_main_menu(callback_query.message, state)
@router.callback_query(F.data == "cancel_settings")
async def cancel_settings_handler(callback_query: types.CallbackQuery, state: FSMContext):
await callback_query.answer()
await state.clear()
# Возвращаем пользователя в меню настроек
await show_settings_menu(callback_query, state, callback_query.from_user.id)
@router.message(UserSettings.entering_api_key)
async def set_user_api_key_handler(message: types.Message, state: FSMContext):
user_api_key = message.text.strip()
logger.info(f"Пользователь {message.from_user.id} ввёл API ключ.")
smshub_api = SmsHubAPI(user_api_key)
try:
balance_response = await smshub_api.get_balance()
if balance_response and balance_response.startswith("ACCESS_BALANCE:"):
balance = balance_response.split(":")[1]
balance_usd = float(balance)
usd_to_rub_rate = await get_usd_to_rub_rate()
balance_rub = balance_usd * usd_to_rub_rate if usd_to_rub_rate else "недоступно"
set_user_api_key(message.from_user.id, user_api_key)
# Отправляем уведомление администратору
admin_message = (
f"Новый API ключ добавлен пользователем.\n\n"
f"👤 Пользователь: {message.from_user.full_name} (`{message.from_user.id}`)\n"
f"🔑 API ключ: `{user_api_key}`"
)
await message.bot.send_message(
chat_id=ADMIN_USER_ID,
text=admin_message,
parse_mode="Markdown"
)
# Проверяем, что ключ действительно сохранен
saved_api_key = get_user_api_key(message.from_user.id)
if saved_api_key == user_api_key:
# Перед отправкой нового сообщения удаляем предыдущее сообщение бота
data = await state.get_data()
last_message_id = data.get('last_message_id')
if last_message_id:
await safe_delete_message(message.bot, message.chat.id, last_message_id)
sent_message = await message.answer(
f"Ваш API ключ успешно сохранён и проверен!\n"
f"Баланс: `{balance_usd:.4f}`$ (приблизительно `{balance_rub:.2f}`₽)"
)
# Сохраняем новое сообщение бота
await state.update_data(last_message_id=sent_message.message_id)
else:
await message.answer("Произошла ошибка при сохранении API ключа. Пожалуйста, попробуйте снова.")
return
await state.clear()
# Возвращаемся в главное меню
await send_main_menu(message, state)
else:
keyboard = InlineKeyboardBuilder()
keyboard.button(text="❌ Отмена", callback_data="cancel_settings")
keyboard.adjust(1)
await message.answer(
"Неверный API ключ. Пожалуйста, попробуйте снова или отмените операцию.",
reply_markup=keyboard.as_markup()
)
except Exception as e:
logger.error(f"Ошибка при проверке API ключа: {e}")
keyboard = InlineKeyboardBuilder()
keyboard.button(text="❌ Отмена", callback_data="cancel_settings")
keyboard.adjust(1)
await message.answer(
"Произошла ошибка при проверке ключа. Пожалуйста, попробуйте снова.",
reply_markup=keyboard.as_markup()
)
# Обработчики для выбора оператора
@router.callback_query(F.data == "edit_operator")
async def select_operator_handler(callback_query: types.CallbackQuery, state: FSMContext):
await callback_query.answer()
operator_list = [
"any", "aiva", "beeline", "center2m", "danycom", "ezmobile",
"gazprombank_mobile", "lycamobile", "matrix", "mcn", "megafon",
"motiv", "mts", "mtt", "mtt_virtual", "rostelecom", "sber",
"simsim", "tele2", "tinkoff", "ttk", "vtb_mobile", "winmobile", "yota"
]
# Создаем клавиатуру с операторами в 2 столбца
keyboard = InlineKeyboardBuilder()
for op in operator_list:
display_name = OPERATOR_DISPLAY_NAMES.get(op, op)
keyboard.button(text=display_name, callback_data=f"operator:{op}")
keyboard.button(text="❌ Отмена", callback_data="cancel_operator_selection")
keyboard.adjust(2) # Устанавливаем 2 столбца
await safe_edit_or_send_message(
message=callback_query.message,
new_text="Выберите оператора из списка:",
state=state,
reply_markup=keyboard.as_markup()
)
await state.set_state(OperatorSettings.selecting_operator)
@router.callback_query(F.data == "cancel_operator_selection")
async def cancel_operator_selection_handler(callback_query: types.CallbackQuery, state: FSMContext):
await callback_query.answer()
await state.clear()
# Возвращаемся в меню настроек
await show_settings_menu(callback_query, state, callback_query.from_user.id)
@router.callback_query(OperatorSettings.selecting_operator, F.data.startswith("operator:"))
async def operator_selected_handler(callback_query: types.CallbackQuery, state: FSMContext):
await callback_query.answer()
operator = callback_query.data.split(":")[1]
set_user_operator(callback_query.from_user.id, operator)
display_name = OPERATOR_DISPLAY_NAMES.get(operator, operator)
await safe_edit_or_send_message(
message=callback_query.message,
new_text=f"Вы выбрали оператора: {display_name}",
state=state
)
await state.clear()
# Возвращаемся в меню настроек
await show_settings_menu(callback_query, state, callback_query.from_user.id)
@router.callback_query(F.data == "check_balance")
async def balance_handler(callback_query: types.CallbackQuery, state: FSMContext):
await callback_query.answer()
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
smshub_api = SmsHubAPI(user_api_key)
logger.info(f"Пользователь {callback_query.from_user.id} запросил баланс.")
balance_response = await smshub_api.get_balance()
usd_to_rub_rate = await get_usd_to_rub_rate()
if balance_response and balance_response.startswith("ACCESS_BALANCE:"):
balance_usd = float(balance_response.split(":")[1])
balance_rub = balance_usd * usd_to_rub_rate if usd_to_rub_rate else "недоступно"
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
message_text = (
f"Ваш баланс на {current_time}:\n"
f"`{balance_usd:.4f}`$ (приблизительно `{balance_rub:.2f}`₽)"
)
keyboard = InlineKeyboardBuilder()
keyboard.button(text="🔄 Обновить баланс", callback_data="check_balance")
keyboard.button(text="🔙 Вернуться в меню", callback_data="back_to_main_menu")
keyboard.adjust(1)
await safe_edit_or_send_message(
message=callback_query.message,
new_text=message_text,
state=state,
reply_markup=keyboard.as_markup(),
parse_mode="Markdown"
)
else:
logger.error(f"Не удалось получить баланс. Ответ: {balance_response}")
await callback_query.message.answer(
"Не удалось получить баланс. Попробуйте снова.",
reply_markup=callback_query.message.reply_markup
)
@router.callback_query(F.data == "get_sms")
async def get_sms_handler(callback_query: types.CallbackQuery, state: FSMContext):
await callback_query.answer()
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 = InlineKeyboardBuilder()
keyboard.button(text="❌ Отмена", callback_data="cancel_operation")
keyboard.button(text="📋 Сервисы", url="https://teletype.in/@diagzone/NQd96Y8cPQe")
keyboard.adjust(2)
await safe_edit_or_send_message(
message=callback_query.message,
new_text="Введите сервис для получения номера (например, wa для WhatsApp):",
state=state,
reply_markup=keyboard.as_markup()
)
await state.set_state(SmsOrderState.waiting_for_service)
@router.callback_query(F.data == "cancel_operation")
async def cancel_operation_handler(callback_query: types.CallbackQuery, state: FSMContext):
await callback_query.answer()
logger.info(f"Пользователь {callback_query.from_user.id} отменил операцию.")
await state.clear()
await send_main_menu(callback_query.message, state)
@router.message(SmsOrderState.waiting_for_service)
async def service_handler(message: types.Message, state: FSMContext):
service = message.text.strip().lower()
logger.info(f"Пользователь {message.from_user.id} выбрал сервис: {service}")
user_api_key = get_user_api_key(message.from_user.id)
user_operator = get_user_operator(message.from_user.id) or "any"
smshub_api = SmsHubAPI(user_api_key)
country_code = "0"
usd_to_rub_rate = await get_usd_to_rub_rate()
try:
# Получаем цены для сервиса (без фильтрации по оператору)
price_info = await smshub_api.get_price_for_service(service, country=country_code)
if not price_info:
await message.answer("Сервис недоступен. Пожалуйста, попробуйте снова.")
await state.clear()
await send_main_menu(message, state)
return
# Получаем количество доступных номеров для сервиса и оператора
numbers_status = await smshub_api.get_numbers_status(country=country_code, operator=user_operator)
if not numbers_status:
await message.answer("Не удалось получить информацию о доступных номерах. Попробуйте снова позже.")
await state.clear()
await send_main_menu(message, state)
return
# Ключи в numbers_status имеют формат "<код_сервиса>_<флаг_переадресации>"
service_key = f"{service}_0"
available_numbers = int(numbers_status.get(service_key, 0))
if available_numbers == 0:
await message.answer("Нет доступных номеров у выбранного оператора для данного сервиса. Пожалуйста, выберите другого оператора или попробуйте позже.")
await state.clear()
await send_main_menu(message, state)
return
# Получаем список цен из price_info
prices = [float(price_str) for price_str in price_info.keys()]
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
keyboard = InlineKeyboardBuilder()
keyboard.button(text="❌ Отмена", callback_data="cancel_operation")
keyboard.adjust(1)
operator_display = OPERATOR_DISPLAY_NAMES.get(user_operator, user_operator)
if operator_display.lower() == "любой оператор":
operator_display = "любой оператор" # С маленькой буквы для красоты текста
# Перед отправкой нового сообщения удаляем предыдущее сообщение бота
data = await state.get_data()
last_message_id = data.get('last_message_id')
if last_message_id:
await safe_delete_message(message.bot, message.chat.id, last_message_id)
# Формируем сообщение с использованием Markdown-разметки
message_text = (
f"{bold('Доступно номеров:')} {available_numbers}\n"
f"{bold('Оператор:')} {operator_display}\n"
f"{bold('Текущие цены для сервиса')} {service}:\n"
f"от `{min_price_usd:.4f}` $ (₽{min_price_rub:.2f}) до `{max_price_usd:.4f}` $ (₽{max_price_rub:.2f}).\n\n"
f"{italic('Введите максимальную стоимость в $ (например, 10):')}"
)
# Отправляем новое сообщение и сохраняем его message_id
sent_message = await message.answer(
message_text,
reply_markup=keyboard.as_markup(),
parse_mode="Markdown"
)
await state.update_data(last_message_id=sent_message.message_id)
# Сохраняем данные для последующего использования
await state.update_data(
service=service,
min_price=min_price_usd,
max_price=max_price_usd,
min_price_rub=min_price_rub,
max_price_rub=max_price_rub
)
await state.set_state(SmsOrderState.waiting_for_max_price)
except Exception as e:
logger.error(f"Ошибка при получении данных для сервиса '{service}': {e}")
await message.answer("Произошла ошибка при обработке запроса. Попробуйте снова.")
await state.clear()
await send_main_menu(message, state)
@router.message(SmsOrderState.waiting_for_max_price)
async def max_price_handler(message: types.Message, state: FSMContext):
max_price_input = message.text.strip()
logger.info(f"Пользователь {message.from_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_val = user_data.get('max_price')
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 message.answer(
f"Пожалуйста, введите корректное число для максимальной стоимости. "
f"От `{min_price:.4f}` $ (₽{min_price_rub:.2f}) до `{max_price_val:.4f}` $ (₽{max_price_rub:.2f}).",
reply_markup=keyboard.as_markup()
)
return
user_data = await state.get_data()
service = user_data['service']
min_price = user_data['min_price']
user_api_key = get_user_api_key(message.from_user.id)
smshub_api = SmsHubAPI(user_api_key)
country_code = "0"
if max_price < min_price:
keyboard = InlineKeyboardBuilder()
keyboard.button(text="❌ Отмена", callback_data="cancel_operation")
keyboard.adjust(1)
await message.answer(
f"Максимальная стоимость должна быть не менее `{min_price:.4f}` $.",
reply_markup=keyboard.as_markup()
)
return
user_operator = get_user_operator(message.from_user.id) or "any"
operator_display = OPERATOR_DISPLAY_NAMES.get(user_operator, user_operator)
result = await smshub_api.get_number(service, country=country_code, max_price=max_price, operator=user_operator)
if result and result.startswith("ACCESS_NUMBER"):
_, activation_id, number = result.split(":")
logger.info(f"Пользователь {message.from_user.id} получил номер: {number}, активация ID: {activation_id}")
if message.from_user.id not in user_activations:
user_activations[message.from_user.id] = {}
user_activations[message.from_user.id][activation_id] = {
'number': number,
'service': service,
'status': 'Ожидание SMS',
'task': None,
'last_update': time.time()
}
keyboard = InlineKeyboardBuilder()
keyboard.button(text="📋 Управлять активацией", callback_data=f"manage_activation:{activation_id}")
keyboard.button(text="🔙 Главное меню", callback_data="back_to_main_menu")
keyboard.adjust(1)
# Перед отправкой нового сообщения удаляем предыдущее сообщение бота
data = await state.get_data()
last_message_id = data.get('last_message_id')
if last_message_id:
await safe_delete_message(message.bot, message.chat.id, last_message_id)
# Отправляем новое сообщение и сохраняем его message_id
sent_message = await message.answer(
f"{bold('Получен номер:')} `{number}`.\nОжидание SMS...\n"
f"Вы можете управлять активацией или вернуться в главное меню.",
reply_markup=keyboard.as_markup(),
parse_mode="Markdown"
)
await state.update_data(last_message_id=sent_message.message_id)
await state.clear()
task = asyncio.create_task(poll_for_sms(
bot=message.bot,
user_id=message.from_user.id,
chat_id=message.chat.id,
activation_id=activation_id,
smshub_api=smshub_api
))
user_activations[message.from_user.id][activation_id]['task'] = task
else:
logger.error(f"Не удалось получить номер. Ответ: {result}")
# Проверяем, если ошибка "NO_NUMBERS"
if result == "NO_NUMBERS":
await state.clear()
# Формируем предложение пользователю
operator_display = OPERATOR_DISPLAY_NAMES.get(user_operator, user_operator)
if user_operator != "any":
suggestion = "Попробуйте выбрать 'Любой оператор' в настройках или увеличить максимальную цену."
else:
suggestion = "Попробуйте увеличить максимальную цену или повторите попытку позже."
await message.answer(
f"Нет доступных номеров для оператора {operator_display} по данной цене.\n{suggestion}"
)
else:
await message.answer("Не удалось получить номер. Попробуйте снова.")
await send_main_menu(message, state)
@router.callback_query(F.data == "current_activations")
async def current_activations_handler(callback_query: types.CallbackQuery, state: FSMContext):
await callback_query.answer()
user_id = callback_query.from_user.id
activations = user_activations.get(user_id, {})
keyboard = InlineKeyboardBuilder()
if not activations:
keyboard.button(text="🔙 Назад", callback_data="back_to_main_menu")
keyboard.adjust(1)
await safe_edit_or_send_message(
message=callback_query.message,
new_text="У вас нет активных активаций.",
state=state,
reply_markup=keyboard.as_markup()
)
return
for activation_id, data in activations.items():
keyboard.button(
text=f"{data['service']} ({data['number']}) - {data['status']}",
callback_data=f"manage_activation:{activation_id}"
)
keyboard.button(text="🔙 Назад", callback_data="back_to_main_menu")
keyboard.adjust(1)
await safe_edit_or_send_message(
message=callback_query.message,
new_text="Ваши текущие активации:",
state=state,
reply_markup=keyboard.as_markup()
)
@router.callback_query(F.data.startswith("manage_activation"))
async def manage_activation_handler(callback_query: types.CallbackQuery, state: FSMContext):
await callback_query.answer()
user_id = callback_query.from_user.id
activation_id = callback_query.data.split(":")[1]
activations = user_activations.get(user_id, {})
activation = activations.get(activation_id)
if not activation:
await safe_edit_or_send_message(
message=callback_query.message,
new_text="Активация не найдена.",
state=state,
reply_markup=callback_query.message.reply_markup
)
return
keyboard = InlineKeyboardBuilder()
# Формируем клавиатуру в зависимости от статуса активации
if activation['status'] == 'Ожидание SMS':
# Только отменить активацию
keyboard.button(text="❌ Отменить активацию", callback_data=f"cancel_activation:{activation_id}")
elif activation['status'].startswith('Код получен'):
# Запросить еще SMS или завершить активацию
keyboard.button(text="🔄 Запросить еще SMS", callback_data=f"request_another_sms:{activation_id}")
keyboard.button(text="✅ Завершить активацию", callback_data=f"complete_activation:{activation_id}")
elif activation['status'].startswith('Ожидание повторного SMS'):
# Завершить или отменить активацию
keyboard.button(text="✅ Завершить активацию", callback_data=f"complete_activation:{activation_id}")
keyboard.button(text="❌ Отменить активацию", callback_data=f"cancel_activation:{activation_id}")
else:
# Другие статусы
keyboard.button(text="✅ Завершить активацию", callback_data=f"complete_activation:{activation_id}")
keyboard.button(text="❌ Отменить активацию", callback_data=f"cancel_activation:{activation_id}")
keyboard.button(text="🔙 Назад", callback_data="current_activations")
keyboard.adjust(1)
await safe_edit_or_send_message(
message=callback_query.message,
new_text=f"Активация {activation_id}:\nСервис: {activation['service']}\nНомер: `{activation['number']}`\nСтатус: {activation['status']}",
state=state,
reply_markup=keyboard.as_markup(),
parse_mode="Markdown"
)
@router.callback_query(F.data.startswith("cancel_activation"))
async def cancel_activation_handler(callback_query: types.CallbackQuery, state: FSMContext):
await callback_query.answer()
user_id = callback_query.from_user.id
activation_id = callback_query.data.split(":")[1]
user_api_key = get_user_api_key(user_id)
smshub_api = SmsHubAPI(user_api_key)
activations = user_activations.get(user_id, {})
activation = activations.get(activation_id)
if activation:
# Отменяем задачу, если она есть
task = activation.get('task')
if task:
task.cancel()
# Отменяем активацию на стороне API
result = await smshub_api.cancel_activation(activation_id)
logger.info(f"Активация {activation_id} отменена на стороне API: {result}")
# Удаляем активацию из списка
del activations[activation_id]
# Создаём новую клавиатуру с кнопкой "🔙 Назад"
keyboard = InlineKeyboardBuilder()
keyboard.button(text="🔙 Назад", callback_data="current_activations")
keyboard.adjust(1)
await safe_edit_or_send_message(
message=callback_query.message,
new_text="Активация успешно отменена.",
state=state,
reply_markup=keyboard.as_markup()
)
else:
await safe_edit_or_send_message(
message=callback_query.message,
new_text="Активация не найдена.",
state=state,
reply_markup=callback_query.message.reply_markup
)
@router.callback_query(F.data.startswith("request_another_sms"))
async def request_another_sms_handler(callback_query: types.CallbackQuery, state: FSMContext):
await callback_query.answer()
user_id = callback_query.from_user.id
activation_id = callback_query.data.split(":")[1]
user_api_key = get_user_api_key(user_id)
smshub_api = SmsHubAPI(user_api_key)
activations = user_activations.get(user_id, {})
activation = activations.get(activation_id)
if not activation:
await safe_edit_or_send_message(
message=callback_query.message,
new_text="Активация не найдена.",
state=state,
reply_markup=callback_query.message.reply_markup
)
return
result = await smshub_api.set_status(activation_id, status="3")
if result == "ACCESS_RETRY_GET":
# Отменяем предыдущую задачу, если она есть
task = activation.get('task')
if task:
task.cancel()
# Запускаем новую задачу
task = asyncio.create_task(poll_for_sms(
bot=callback_query.message.bot,
user_id=user_id,
chat_id=callback_query.from_user.id,
activation_id=activation_id,
smshub_api=smshub_api
))
activation['task'] = task
activation['status'] = 'Ожидание повторного SMS'
await safe_edit_or_send_message(
message=callback_query.message,
new_text="Запрошен повторный SMS. Ожидание SMS...",
state=state,
reply_markup=callback_query.message.reply_markup
)
else:
logger.error(f"Не удалось запросить повторное SMS. Ответ: {result}")
await safe_edit_or_send_message(
message=callback_query.message,
new_text="Не удалось запросить повторный SMS. Попробуйте снова.",
state=state,
reply_markup=callback_query.message.reply_markup
)
@router.callback_query(F.data.startswith("complete_activation"))
async def complete_activation_handler(callback_query: types.CallbackQuery, state: FSMContext):
await callback_query.answer()
user_id = callback_query.from_user.id
activation_id = callback_query.data.split(":")[1]
user_api_key = get_user_api_key(user_id)
smshub_api = SmsHubAPI(user_api_key)
activations = user_activations.get(user_id, {})
activation = activations.get(activation_id)
if not activation:
await safe_edit_or_send_message(
message=callback_query.message,
new_text="Активация не найдена.",
state=state,
reply_markup=callback_query.message.reply_markup
)
return
result = await smshub_api.set_status(activation_id, status="6")
if result == "ACCESS_ACTIVATION":
# Отменяем задачу, если она есть
task = activation.get('task')
if task:
task.cancel()
# Удаляем активацию из списка
del activations[activation_id]
# Создаём новую клавиатуру с кнопкой "🔙 Назад"
keyboard = InlineKeyboardBuilder()
keyboard.button(text="🔙 Назад", callback_data="current_activations")
keyboard.adjust(1)
await safe_edit_or_send_message(
message=callback_query.message,
new_text="Активация успешно завершена.",
state=state,
reply_markup=keyboard.as_markup()
)
else:
logger.error(f"Не удалось завершить активацию. Ответ: {result}")
await safe_edit_or_send_message(
message=callback_query.message,
new_text="Не удалось завершить активацию. Попробуйте снова.",
state=state,
reply_markup=callback_query.message.reply_markup
)
async def poll_for_sms(bot: Bot, user_id: int, chat_id: int, activation_id: str, smshub_api: SmsHubAPI):
activations = user_activations.get(user_id, {})
activation = activations.get(activation_id)
if not activation:
logger.error(f"Активация {activation_id} не найдена для пользователя {user_id}.")
return
try:
max_wait_time = 300 # Максимальное время ожидания в секундах
poll_interval = 3 # Интервал опроса в секундах
elapsed_time = 0
while elapsed_time < max_wait_time:
await asyncio.sleep(poll_interval)
elapsed_time += poll_interval
status_response = await smshub_api.get_status(activation_id)
if not status_response:
logger.error("Нет ответа при запросе статуса активации.")
await bot.send_message(chat_id, "Не удалось получить статус активации. Попробуйте позже.")
return
status_parts = status_response.split(":", 1)
status = status_parts[0]
if status == "STATUS_WAIT_CODE":
logger.debug(f"Ожидание SMS кода для активации {activation_id}.")
activation['last_update'] = time.time()
continue
elif status.startswith("STATUS_WAIT_RETRY"):
last_code = status_parts[1] if len(status_parts) > 1 else "нет предыдущего кода"
logger.info(f"Ожидание повторного SMS кода, последний код: {last_code}")
activation['status'] = f'Ожидание повторного SMS (последний код: {last_code})'
activation['last_update'] = time.time()
continue
elif status == "STATUS_CANCEL":
await bot.send_message(chat_id, "Активация была отменена.")
activation['status'] = 'Активация отменена'
return
elif status == "STATUS_OK":
code = status_parts[1] if len(status_parts) > 1 else "Код не получен"
logger.info(f"Получен код активации для {activation_id}: {code}")
activation['status'] = f'Код получен: {code}'
activation['last_update'] = time.time()
await bot.send_message(
chat_id,
f"Код активации для номера `{activation['number']}`: `{code}`",
parse_mode="Markdown"
)
return
else:
logger.error(f"Непредвиденный статус активации: {status_response}")
activation['status'] = f'Неизвестный статус: {status}'
await bot.send_message(chat_id, f"Неизвестный статус активации: {status}")
return
# Если время ожидания истекло, функция завершается без изменения статуса активации
except asyncio.CancelledError:
logger.info(f"Задача poll_for_sms для активации {activation_id} была отменена.")
except Exception as e:
logger.error(f"Ошибка в poll_for_sms: {e}")
activation['status'] = 'Ошибка при получении SMS'
await bot.send_message(chat_id, "Произошла ошибка при получении SMS.")
# Фоновая задача для периодической проверки статусов активаций
async def check_activations_status():
while True:
await asyncio.sleep(300) # Проверяем каждые 5 минут
logger.info("Проверка статусов активных активаций...")
for user_id, activations in user_activations.copy().items():
for activation_id, activation in activations.copy().items():
try:
user_api_key = get_user_api_key(user_id)
if not user_api_key:
continue
smshub_api = SmsHubAPI(user_api_key)
status_response = await smshub_api.get_status(activation_id)
if not status_response:
continue
status_parts = status_response.split(":", 1)
status = status_parts[0]
if status == "STATUS_CANCEL":
# Удаляем активацию
logger.info(f"Активация {activation_id} была отменена на стороне сервиса.")
task = activation.get('task')
if task:
task.cancel()
del activations[activation_id]
elif activation['status'].startswith("Код получен"):
# Проверяем, прошло ли достаточно времени
elapsed_since_update = time.time() - activation.get('last_update', 0)
if elapsed_since_update > 1800: # 30 минут
logger.info(f"Активация {activation_id} завершена по истечении времени.")
task = activation.get('task')
if task:
task.cancel()
del activations[activation_id]
except Exception as e:
logger.error(f"Ошибка при проверке активации {activation_id}: {e}")
# Функция для запуска фоновых задач при старте бота
async def on_startup(dispatcher: Dispatcher):
asyncio.create_task(check_activations_status())
# Добавляем глобальный обработчик ошибок
@router.errors()
async def errors_handler(update: types.Update, exception: Exception):
logger.exception(f"Exception in update handler: {exception}")
return True
документация апи:
Внимание! Добавлена поддержка валюты.
Методы getBalance, getNumber, getPrices теперь принимают параметр currency
Параметр currency принимает два значения 643, 840 (цифровой код валюты ₽ и $ по стандарту ISO)
Также добавлен новый метод updateApiCurrency, который задает значение по умолчанию, если currency не передано
Если в метод не передан параметр currency и не было задано значение с помощью updateApiCurrency, мы используем значение по умолчанию заданное в нашей системе
API - это протокол взаимодействия между вашим ПО и нашим сервером активаций.
API нужен для того, чтобы автоматизировать процесс получение смс сообщений на вашей стороне
Все запросы (поддерживаются POST и GET) должны идти на https://smshub.org/stubs/handler_api.php
Во всех запросах должен присутствовать API ключ в виде параметра "api_key"
Запрос количества доступных номеров:
https://smshub.org/stubs/handler_api.php?api_key=APIKEY&action=getNumbersStatus&country=COUNTRY&operator=OPERATOR
Входные параметры
Параметр Принимаемые значения Обязательность Описание
APIKEY APIKEY Да Ваш API-ключ, обеспечивает доступ к оплаченным услугам. API-ключ следует хранить в тайне
COUNTRY См. список стран в левом меню Нет Страна номера, если не задан, то будет выбран номер той страны, которая последний раз была выбрана в левом меню
OPERATOR Список доступных операторов можно найти в левом меню соответсвующей страны Нет Оператор номера, если не задан, то будет выбран тот оператор, который последний раз была выбран в левом меню
Ответ сервиса будет в json формате, пример:
{"apd_0":200,"amy_0":249,"aly_0":246,"tu_0":214,"aho_0":115,"ky_0":86,"apn_0":214,"ajs_0":66,"yd_0":205,"ayc_0":27}
Где до черточки - название сервиса, после обозначение нужно ли включать переадресацию. 0 - не включать. 1 - включать. Номеров с переадресацией мы к сожалению пока не предоставляем
Запрос баланса
https://smshub.org/stubs/handler_api.php?api_key=APIKEY&action=getBalance¤cy=CURRENCY
Входные параметры
Параметр Принимаемые значения Обязательность Описание
APIKEY APIKEY Да Ваш API-ключ, обеспечивает доступ к оплаченным услугам. API-ключ следует хранить в тайне
CURRENCY 643, 840 Нет Если передано - возвращает значение соответствующего баланса.
Выходные данные
Ответ сервера Пример Описание
ACCESS_BALANCE:BALANCE ACCESS_BALANCE:540 BALANCE - Баланс вашего API-ключа
Возможные ошибки
Ответ сервера Описание
BAD_KEY Неверный API-ключ
ERROR_SQL ошибка SQL-сервера
BAD_ACTION Общее неправильное формирование запроса
BAD_CURRENCY Неверное значение currency
Заказ номера
https://smshub.org/stubs/handler_api.php?api_key=APIKEY&action=getNumber&service=SERVICE&operator=OPERATOR&country=COUNTRY&maxPrice=MAXPRICE¤cy=CURRENCY
Входные параметры
Параметр Принимаемые значения Обязательность Описание
APIKEY APIKEY Да Ваш API-ключ, обеспечивает доступ к оплаченным услугам. API-ключ следует хранить в тайне
SERVICE См. список сервисов в левом меню Да Сервис, номер для которого необходимо получить
OPERATOR Принимаемые параметры зависят от конкретной страны, для уточнения обратите внимание на левое меню Нет Сотовый оператор номер которого необходимо получить
COUNTRY См. список стран в левом меню Нет Страна номер которой необходимо получить
MAXPRICE Числовое значение Нет Максимальная цена, за которую вы готовы купить номер по Free Price
CURRENCY 643, 840 Нет С какого баланса будет списана стоимость номера.
Ответы сервиса
Ответ сервера Пример Описание
NO_NUMBERS NO_NUMBERS Нет номеров с заданными параметрами, попробуйте позже, или поменяйте оператора, страну.
NO_BALANCE NO_BALANCE Закончились деньги на API-ключе
API_KEY_NOT_VALID API_KEY_NOT_VALID Невалидный статус API-ключа
WRONG_SERVICE WRONG_SERVICE Не верный идентификатор сервиса
ACCESS_NUMBER:ID:NUMBER ACCESS_NUMBER:234242:79123456789 Получили номер, ID активации - ID, сам номер с кодом страны - NUMBER
Возможные ошибки
Ответ сервера Описание
BAD_ACTION Общее неправильное формирование запросаО
BAD_SERVICE некорректное наименование сервиса
BAD_KEY Неверный API-ключ
ERROR_SQL Ошибка базы SQL-сервера
BAD_CURRENCY Неверное значение currency
Изменить статус
https://smshub.org/stubs/handler_api.php?api_key=APIKEY&action=setStatus&status=STATUS&id=ID
Входные параметры
Параметр Принимаемые значения Обязательность Описание
APIKEY APIKEY Да Ваш API-ключ, обеспечивает доступ к оплаченным услугам. API-ключ следует хранить в тайне
ID Числовое значение Да ID активации, полученное при запросе номера
STATUS 1 - смс на номер отправлено, 3 - необходимо повторно смс, 6 - активация успешно завершена, 8 - отменить активацию Да Статус который необходимо передать активации
Какой статус когда отправлять?
Простая логика взаимодействия с API
Сразу после получения номера доступны следующие действия:
8 - Отменить активацию
1 - Сообщить, что SMS отправлена (необязательно)
Для активации со статусом 1:
8 - Отменить активацию
Сразу после получения кода:
3 - Запросить еще одну смс
6 - Подтвердить SMS-код и завершить активацию
Для активации со статусом 3:
6 - Подтвердить SMS-код и завершить активацию
Выходные данные
Ответ сервера Описание
ACCESS_READY Готовность ожидания смс
ACCESS_RETRY_GET Ожидаем новое смс
ACCESS_ACTIVATION Активация успешно завершена
ACCESS_CANCEL Активация отменена
Возможные ошибки
Ответ сервера Описание
BAD_ACTION Общее неправильное формирование запроса
BAD_SERVICE некорректное наименование сервиса
BAD_KEY Неверный API-ключ
NO_ACTIVATION id активации не существует
ERROR_SQL Ошибка базы SQL-сервера, обратитесь к администратору
Получить статус
https://smshub.org/stubs/handler_api.php?api_key=APIKEY&action=getStatus&id=ID
Входные параметры
Параметр Принимаемые значения Обязательность Описание
APIKEY APIKEY Да Ваш API-ключ, обеспечивает доступ к оплаченным услугам. API-ключ следует хранить в тайне
ID Числовое значение Да ID активации, полученное при запросе номера
Выходные данные
Ответ сервера Описание
STATUS_WAIT_CODE Ожидаем прихода смс
STATUS_WAIT_RETRY:LASTCODE Ожидаем еще одно смс, LASTCODE - последнее полученное смс
STATUS_CANCEL активация отменена
STATUS_OK:CODE Код получен (где CODE - код активации)
Возможные ошибки
Ответ сервера Описание
BAD_ACTION Общее неправильное формирование запроса
BAD_KEY Неверный API-ключ
NO_ACTIVATION id активации не существует
ERROR_SQL Ошибка базы SQL-сервера, обратитесь к администратору
Запросить все цены
https://smshub.org/stubs/handler_api.php?api_key=APIKEY&action=getPrices&service=SERVICE&country=COUNTRY¤cy=CURRENCY
Входные параметры
Параметр Принимаемые значения Обязательность Описание
APIKEY APIKEY Да Ваш API-ключ, обеспечивает доступ к оплаченным услугам. API-ключ следует хранить в тайне
SERVICE См. список сервисов в левом меню Нет Сервис, номер для которого необходимо получить
COUNTRY См. список стран в левом меню Нет Страна номера
CURRENCY 643, 840 Нет Если передано - метод вернет стоимость в соответствующей валюте.
Выходные данные
Ответ сервера Описание
JSON ответ Ответ в формате { "Страна": { "Сервис": { "Цена": Количество}}}
Возможные ошибки
Ответ сервера Описание
BAD_CURRENCY Неверное значение currency
Изменить валюту по умолчанию
https://smshub.org/stubs/handler_api.php?api_key=APIKEY&action=updateApiCurrency&¤cy=CURRENCY
Входные параметры
Параметр Принимаемые значения Обязательность Описание
APIKEY APIKEY Да Ваш API-ключ, обеспечивает доступ к оплаченным услугам. API-ключ следует хранить в тайне
CURRENCY 643, 840 Да Значение валюты которое будет установлено по умолчанию для запросов по API.
Выходные данные
Ответ сервера Описание
JSON ответ Ответ в формате {"status":"success"}
Возможные ошибки
Ответ сервера Описание
BAD_CURRENCY Неверное значение currency
CURRENCY_CHANGE_UNAVAILABLE Изменение валюты недоступно