Урок 8.2 — Интеграция с aiogram 2.x
Учимся интегрировать aiosend с aiogram 2.x: executor.start_polling, параллельный запуск и обработка платежей.
aiogram 2.x — более старая, но всё ещё популярная версия библиотеки. Интеграция с aiosend строится по тому же принципу, что и с 3.x, но с использованием executor.start_polling() и декораторов @dp.message_handler(). Основное отличие — синхронный запуск через executor.
Параллельный запуск с executor
В aiogram 2.x используется executor.start_polling(), который сам управляет event loop. Для интеграции с aiosend нужно запустить aiosend polling внутри aiogram-функции on_startup или через asyncio.ensure_future().
import asyncio
from aiogram import Bot, Dispatcher, types
from aiogram.contrib.middlewares.logging import LoggingMiddleware
from aiosend import CryptoPay
BOT_TOKEN = "123456:ABCdefGHIjklmNOP"
CP_TOKEN = "1234:ABCDefGHIJklmnOPQRST"
bot = Bot(token=BOT_TOKEN)
dp = Dispatcher(bot)
dp.middleware.setup(LoggingMiddleware())
cp = CryptoPay(token=CP_TOKEN)
@dp.message_handler(commands=["start"])
async def start(message: types.Message):
await message.answer(
"Привет! Я бот на aiogram 2.x + aiosend!\n"
"Используй /pay 10 USDT для оплаты."
)
@dp.message_handler(commands=["pay"])
async def pay(message: types.Message):
args = message.text.split()[1:] # ["10", "USDT"]
if len(args) < 2:
await message.answer("Использование: /pay 10 USDT")
return
amount, asset = float(args[0]), args[1].upper()
invoice = await cp.create_invoice(
amount=amount,
asset=asset,
payload=str(message.from_user.id),
)
await message.answer(
f"🧾 Счёт на {amount} {asset}:\n"
f"{invoice.bot_invoice_url}\n\n"
f"Ожидаем оплату..."
)
@cp.invoice_paid()
async def on_paid(invoice):
user_id = int(invoice.payload)
await bot.send_message(
user_id,
f"✅ Оплата {invoice.amount} {invoice.asset} получена!"
)
async def on_startup(_):
"""Запускает aiosend polling при старте aiogram."""
asyncio.ensure_future(cp.start_polling())
if __name__ == "__main__":
from aiogram import executor
executor.start_polling(
dp,
on_startup=on_startup,
skip_updates=True,
)
💡 Как это работает?
executor.start_polling() запускает aiogram и вызывает on_startup. Внутри on_startup мы через asyncio.ensure_future() запускаем aiosend polling в том же event loop. Оба polling-цикла работают параллельно.
Создание инвойса и ожидание оплаты
В aiogram 2.x invoice.poll() тоже поддерживает параметр message, но обрати внимание: объект Message в 2.x API имеет немного другую структуру. aiosend корректно работает с обеими версиями.
@dp.message_handler(commands=["buy"])
async def buy(message: types.Message):
# Создаём инвойс
invoice = await cp.create_invoice(
amount=15,
asset="USDT",
description="Премиум-доступ",
payload=str(message.from_user.id),
)
# Отправляем сообщение
msg = await message.answer(
f"💳 Премиум-доступ\n"
f"Сумма: 15 USDT\n\n"
f"🔗 {invoice.bot_invoice_url}\n\n"
f"⏳ Ожидаем оплату...",
parse_mode="HTML",
)
# Ожидаем оплату — сообщение будет отредактировано
try:
paid = await invoice.poll(message=msg)
await msg.edit_text(
f"✅ Оплачено!\n"
f"{paid.paid_amount} {paid.paid_asset}\n"
f"Спасибо!",
parse_mode="HTML",
)
except Exception as e:
await msg.edit_text(f"❌ Ошибка: {e}")
⚠️ Отличие от aiogram 3.x
В aiogram 2.x используется @dp.message_handler() вместо @dp.message(), а executor.start_polling() — вместо dp.start_polling(). Остальная логика интеграции с aiosend идентична.
Полный пример: магазин на aiogram 2.x
import asyncio
import logging
from aiogram import Bot, Dispatcher, types
from aiogram.contrib.middlewares.logging import LoggingMiddleware
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
from aiosend import CryptoPay
logging.basicConfig(level=logging.INFO)
BOT_TOKEN = "123456:ABCdefGHIjklmNOP"
CP_TOKEN = "1234:ABCDefGHIJklmnOPQRST"
bot = Bot(token=BOT_TOKEN)
dp = Dispatcher(bot)
dp.middleware.setup(LoggingMiddleware())
cp = CryptoPay(token=CP_TOKEN)
PRODUCTS = {
"premium": {"name": "Премиум", "price": 15, "asset": "USDT"},
"vip": {"name": "VIP-доступ", "price": 50, "asset": "USDT"},
"boost": {"name": "Буст", "price": 5, "asset": "TON"},
}
@dp.message_handler(commands=["start"])
async def start(message: types.Message):
kb = InlineKeyboardMarkup(row_width=2).add(
InlineKeyboardButton("💎 Премиум", callback_data="buy_premium"),
InlineKeyboardButton("⭐ VIP", callback_data="buy_vip"),
InlineKeyboardButton("🚀 Буст", callback_data="buy_boost"),
)
await message.answer(
"🛒 Добро пожаловать в магазин!\n"
"Выберите товар:",
reply_markup=kb,
)
@dp.callback_query_handler(lambda c: c.data.startswith("buy_"))
async def process_buy(callback: types.CallbackQuery):
product_key = callback.data.replace("buy_", "")
product = PRODUCTS.get(product_key)
if not product:
await callback.answer("Товар не найден")
return
await callback.answer("Создаём счёт...")
invoice = await cp.create_invoice(
amount=product["price"],
asset=product["asset"],
description=product["name"],
payload=f"{callback.from_user.id}:{product_key}",
expires_in=600,
)
msg = await callback.message.answer(
f"💳 {product['name']}\n"
f"Сумма: {product['price']} {product['asset']}\n\n"
f"🔗 {invoice.bot_invoice_url}\n\n"
f"⏳ Ожидаем оплату...",
parse_mode="HTML",
)
try:
paid = await invoice.poll(message=msg)
await msg.edit_text(
f"✅ Оплачено!\n"
f"{paid.paid_amount} {paid.paid_asset}\n"
f"Товар: {product['name']}",
parse_mode="HTML",
)
except Exception as e:
await msg.edit_text(f"❌ Ошибка: {e}")
@cp.invoice_paid()
async def on_paid(invoice):
user_id, product_key = invoice.payload.split(":")
product = PRODUCTS.get(product_key, {})
print(
f"Платёж #{invoice.invoice_id}: "
f"user={user_id} product={product.get('name')}"
)
await bot.send_message(
int(user_id),
f"✅ Ваш {product.get('name', 'товар')} активирован!"
)
async def on_startup(_):
asyncio.ensure_future(cp.start_polling())
async def on_shutdown(_):
await cp.stop_polling()
if __name__ == "__main__":
from aiogram import executor
executor.start_polling(
dp,
on_startup=on_startup,
on_shutdown=on_shutdown,
skip_updates=True,
)
🔍 Ключевые отличия от aiogram 3.x
@dp.message_handler()вместо@dp.message()@dp.callback_query_handler()вместо@dp.callback_query()executor.start_polling()вместоdp.start_polling()- Запуск aiosend через
asyncio.ensure_future()в on_startup - Остановка через
on_shutdown
Корректная остановка polling
При завершении работы бота важно корректно остановить оба polling-цикла. Для этого используй on_shutdown:
async def on_startup(_):
"""Запуск aiosend при старте."""
asyncio.ensure_future(cp.start_polling())
async def on_shutdown(_):
"""Остановка aiosend при завершении."""
await cp.stop_polling()
print("aiosend polling остановлен")
if __name__ == "__main__":
from aiogram import executor
executor.start_polling(
dp,
on_startup=on_startup,
on_shutdown=on_shutdown,
)
Что важно запомнить
cp.stop_polling().message_handler, callback_query_handler.Практическое задание
Задание: Бот для приёма платежей на aiogram 2.x
Портиpyй пример магазина из урока 8.1 на aiogram 2.x:
- Используй
executor.start_polling() - Запусти aiosend через
asyncio.ensure_future()вon_startup - Замени декораторы на
@dp.message_handler()и@dp.callback_query_handler() - Добавь
on_shutdownдля остановки aiosend
Подсказка:
import asyncio
from aiogram import Bot, Dispatcher, types
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
from aiosend import CryptoPay
bot = Bot(token="BOT_TOKEN")
dp = Dispatcher(bot)
cp = CryptoPay(token="CP_TOKEN")
@dp.message_handler(commands=["start"])
async def start(message: types.Message):
await message.answer(
"Напиши /buy 10 USDT для покупки"
)
@dp.message_handler(commands=["buy"])
async def buy(message: types.Message):
args = message.text.split()
amount, asset = float(args[1]), args[2].upper()
invoice = await cp.create_invoice(
amount=amount, asset=asset,
payload=str(message.from_user.id),
)
msg = await message.answer(
f"Счёт: {invoice.bot_invoice_url}"
)
paid = await invoice.poll(message=msg)
await msg.edit_text(f"✅ Оплачено: {paid.paid_amount}")
@cp.invoice_paid()
async def on_paid(invoice):
uid = int(invoice.payload)
await bot.send_message(uid, "Спасибо!")
async def on_startup(_):
asyncio.ensure_future(cp.start_polling())
async def on_shutdown(_):
await cp.stop_polling()
if __name__ == "__main__":
from aiogram import executor
executor.start_polling(dp, on_startup=on_startup, on_shutdown=on_shutdown)
Урок 8.2: Интеграция с aiogram 2.x
5 вопросов