$ sudo teach IT
Модуль 1 · Введение

Урок 1.3 — Первое приложение на aiosend

Напишем первое работающее приложение с aiosend: разберёмся с async/await, создадим клиента, получим информацию о приложении через get_me() и научимся правильно организовывать проект.

В этом уроке мы напишем наше первое работающее приложение на aiosend. Ты узнаешь, как работает асинхронность в Python, научишься создавать клиента CryptoPay, вызывать метод get_me() для получения информации о приложении и познакомишься со структурой реального проекта. К концу урока у тебя будет два полноценных скрипта — асинхронный и синхронный.

⚡

async/await — основы асинхронности

Библиотека aiosend полностью асинхронна. Это значит, что все методы, работающие с API Crypto Pay, являются корутинами (асинхронными функциями) и требуют использования синтаксиса async / await. Давай разберёмся, как это работает.

Что такое async/await?

async def — это ключевое слово для объявления асинхронной функции (корутины). Внутри такой функции можно использовать await для ожидания результата другой асинхронной операции. Пока одна корутина ожидает ответа от сети, event loop может выполнять другие задачи.

Python · Простейший асинхронный пример
import asyncio

async def say_hello():
    print("Привет!")
    await asyncio.sleep(1)  # имитация ожидания
    print("Прошла 1 секунда")

asyncio.run(say_hello())

🔍 Как это работает?

asyncio.run() создаёт event loop, выполняет корутину и закрывает loop. Внутри корутины await asyncio.sleep(1) приостанавливает выполнение на 1 секунду, но event loop может выполнять другие задачи. В контексте aiosend: await cp.get_me() отправляет HTTP-запрос и ждёт ответа, не блокируя весь поток.

Точка входа: asyncio.run()

Каждая асинхронная программа нуждается в точке входа — месте, где запускается event loop. В Python для этого используется asyncio.run(main()). Это стандартный паттерн для всех скриптов aiosend:

Python · Стандартная точка входа
import asyncio
from aiosend import CryptoPay

async def main():
    cp = CryptoPay(token="ВАШ_ТОКЕН")
    app = await cp.get_me()
    print(f"Приложение: {app.name}")

if __name__ == "__main__":
    asyncio.run(main())

⚠️ Важно: конструктор CryptoPay() НЕ асинхронный

В отличие от многих других библиотек, CryptoPay(token) — это обычный синхронный конструктор. Только методы API (get_me(), create_invoice() и т.д.) являются асинхронными. Это сделано для удобства: ты можешь создать клиента на уровне модуля, не оборачивая в async.

Асинхронный контекстный менеджер

Хотя это и не обязательно (сессия создаётся автоматически), ты можешь использовать CryptoPay как асинхронный контекстный менеджер:

Python · Использование async with
async def main():
    async with CryptoPay(token="ТОКЕН") as cp:
        app = await cp.get_me()
        print(app.name)
    # Здесь сессия автоматически закрыта

Однако в большинстве примеров мы будем использовать обычное создание клиента — это проще и не требует дополнительной вложенности.

🔄

Sync vs Async — сравнительный анализ

aiosend поддерживает оба режима работы. По умолчанию все методы — асинхронные, но библиотека автоматически создаёт синхронные обёртки. Давай сравним оба подхода:

Ниже (блокирующие вызовы)
Характеристика Асинхронный (async) Синхронный (sync)
Синтаксис await cp.get_me() cp.get_me()
Класс клиента CryptoPay CryptoPay (тот же!)
Точка входа asyncio.run(main()) Обычный вызов функции
Механизм Event loop, корутины asyncio.run() внутри
Производительность Максимальная (конкурентность)
Когда использовать Боты, веб-серверы, highload Скрипты, прототипы, Flask
Доп. импорты Требуется import asyncio Не требуется

✅ Асинхронный подход

import asyncio
from aiosend import CryptoPay

async def main():
    cp = CryptoPay("TOKEN")
    app = await cp.get_me()
    print(app.name)

asyncio.run(main())

⚡ Синхронный подход

from aiosend import CryptoPay

cp = CryptoPay("TOKEN")
app = cp.get_me()  # без await!
print(app.name)

🔍 Как работает sync-режим?

В aiosend все публичные асинхронные методы автоматически оборачиваются синхронными прокси. Если внутри async-функции есть запущенный event loop — синхронный вызов вернёт корутину (нужен await). Если loop не запущен — он создаётся через asyncio.run() и результат возвращается синхронно.

🔧

Конструктор CryptoPay — краткий обзор

Класс CryptoPay — центральный класс библиотеки. Через него проходят все запросы к API. Мы уже подробно разбирали его в уроке 2.1, здесь же приведём краткую справку:

Python · Сигнатура конструктора
class CryptoPay:
    def __init__(
        self,
        token: str,                    # API-токен (обязательно)
        network: Network = MAINNET,    # MAINNET или TESTNET
        session: type[BaseSession] = AiohttpSession,
        timeout: int | float = 300,    # таймаут HTTP-запроса
        polling_config: PollingConfig = PollingConfig(),
        webhook_manager: WebhookManager | None = None,
    )

Для нашего первого приложения нам понадобится только token. Всё остальное останется по умолчанию.

💡 Подсказка

Для тестирования используй TESTNET-токен из @CryptoTestnetBot. Он безопасен и не требует реальных денег.

👤

Метод get_me() и объект App

Метод get_me() — самый простой метод API Crypto Pay. Он возвращает информацию о твоём приложении: его ID, название, обработчик платежей и другие данные. Это идеальный первый запрос для проверки, что клиент работает.

Python · Вызов get_me()
from aiosend import CryptoPay

cp = CryptoPay(token="ВАШ_ТОКЕН")

# Асинхронно:
app = await cp.get_me()

# Синхронно:
app = cp.get_me()

Метод возвращает объект App — Pydantic-модель с информацией о приложении. Вот как выглядят её поля:

Таблица полей объекта App

Поле Тип Описание
app_id int Уникальный идентификатор приложения в системе Crypto Pay
name str Название приложения (задаётся при создании в @CryptoBot)
payment_processing_bot_username str Username бота, который обрабатывает платежи (@CryptoBot или @CryptoTestnetBot)
supported_assets list[Asset] Список криптовалют, доступных для приёма платежей в этом приложении

Как видишь, App содержит всего 4 поля, но они дают полную информацию о приложении. Поле supported_assets особенно полезно — оно показывает, какие криптовалюты доступны для создания инвойсов.

Python · Вывод всех полей App
from aiosend import CryptoPay

cp = CryptoPay(token="ВАШ_ТОКЕН")
app = await cp.get_me()

print("Информация о приложении:")
print(f"  ID:         {app.app_id}")
print(f"  Название:   {app.name}")
print(f"  Бот:        @{app.payment_processing_bot_username}")
print(f"  Валюты:     {[a.asset_code for a in app.supported_assets]}")

# Пример вывода:
# Информация о приложении:
#   ID:         12345
#   Название:   My Crypto App
#   Бот:        @CryptoTestnetBot
#   Валюты:     ['USDT', 'TON', 'BTC', 'ETH', 'LTC', 'BNB', 'TRX', 'USDC', 'JET', 'SEND', 'SOL', 'XAUT']

⚠️ Важно про supported_assets

Список supported_assets зависит от настроек твоего приложения в @CryptoBot. Если ты не включил какие-то методы (например, Checks или Transfers), соответствующие валюты могут быть недоступны. Убедись, что в настройках приложения включены все нужные методы.

⚙️

Синхронный режим через .sync

В aiosend есть специальный объект .sync, который предоставляет доступ ко всем методам в синхронном виде. Это удобно, когда ты хочешь явно указать, что используешь синхронный режим:

Python · Использование .sync
from aiosend import CryptoPay

cp = CryptoPay(token="ВАШ_ТОКЕН")

# Явный синхронный вызов через .sync
app = cp.sync.get_me()
print(f"Приложение: {app.name} (ID: {app.app_id})")

# Такой же синхронный вызов, но без .sync (автоматическая обёртка)
app2 = cp.get_me()  # тоже работает синхронно

🔍 Как устроен .sync?

Объект .sync — это экземпляр класса SyncProxy, который содержит те же методы, что и основной клиент, но все они синхронные. Он создаётся автоматически при инициализации CryptoPay. Все публичные асинхронные методы CryptoPay также автоматически дублируются в синхронном виде — это сделано для удобства.

Разница между cp.get_me() и cp.sync.get_me():

  • cp.get_me() — метод автоматически определяет контекст: если есть event loop — возвращает корутину (нужен await), иначе — выполняет синхронно.
  • cp.sync.get_me() — всегда синхронный, всегда возвращает результат, никогда не требует await.

⚠️ Внимание: .sync в async-функции

Если вызвать cp.sync.get_me() внутри async-функции, это может привести к созданию вложенного event loop, что вызовет ошибку. Внутри async-функций всегда используй await cp.get_me().

🚀

Первое асинхронное приложение

Соберём всё вместе и напишем полноценное асинхронное приложение. Этот скрипт создаёт клиента, получает информацию о приложении и выводит балансы.

Python · first_app_async.py — полный пример
import asyncio
import os
from dotenv import load_dotenv

from aiosend import CryptoPay
from aiosend.exceptions import CryptoPayError

load_dotenv()

TOKEN = os.getenv("CRYPTOPAY_TOKEN")


async def main():
    """Главная асинхронная функция приложения."""

    print("Запуск первого aiosend приложения...")
    print(f"Используется токен: {TOKEN[:8]}...{TOKEN[-4:] if TOKEN else 'None'}")

    # Создаём клиента
    cp = CryptoPay(token=TOKEN)

    try:
        # Получаем информацию о приложении
        print("\nПолучаем информацию о приложении...")
        app = await cp.get_me()

        print(f"\n{'='*50}")
        print(f"  Приложение успешно подключено!")
        print(f"  ID:          {app.app_id}")
        print(f"  Название:    {app.name}")
        print(f"  Платёжный бот: @{app.payment_processing_bot_username}")
        print(f"{'='*50}")

        # Получаем балансы
        print("\nПолучаем балансы...")
        balances = await cp.get_balance()

        if balances:
            print(f"\n{'='*50}")
            print(f"  Балансы приложения:")
            for balance in balances:
                print(f"    {balance.currency_code}:")
                print(f"      Доступно:  {balance.available}")
                print(f"      Заморожено: {balance.onhold}")
            print(f"{'='*50}")
        else:
            print("\nБалансы пусты.")

        # Показываем поддерживаемые активы
        print(f"\nПоддерживаемые криптовалюты ({len(app.supported_assets)}):")
        assets_list = [a.asset_code for a in app.supported_assets]
        print(f"  {', '.join(assets_list)}")

    except CryptoPayError as e:
        print(f"\n❌ Ошибка Crypto Pay: {e}")
    except Exception as e:
        print(f"\n❌ Неожиданная ошибка: {e}")
    finally:
        print("\nПриложение завершило работу.")


if __name__ == "__main__":
    asyncio.run(main())

💡 Что делает этот скрипт?

1. Загружает переменные окружения из .env
2. Создаёт клиента CryptoPay с токеном
3. Вызывает get_me() — получает информацию о приложении
4. Вызывает get_balance() — получает балансы всех валют
5. Выводит список поддерживаемых криптовалют
6. Обрабатывает возможные ошибки через try/except

🏃

Первое синхронное приложение

А вот так выглядит тот же скрипт в синхронном варианте. Обрати внимание: никакого asyncio, никаких await — просто последовательные вызовы.

Python · first_app_sync.py — синхронный пример
import os
from dotenv import load_dotenv

from aiosend import CryptoPay
from aiosend.exceptions import CryptoPayError

load_dotenv()

TOKEN = os.getenv("CRYPTOPAY_TOKEN")


def main():
    """Главная синхронная функция приложения."""

    print("Запуск первого синхронного aiosend приложения...")

    # Создаём клиента
    cp = CryptoPay(token=TOKEN)

    try:
        # Получаем информацию о приложении (без await!)
        print("\nПолучаем информацию о приложении...")
        app = cp.get_me()

        print(f"\n{'='*50}")
        print(f"  Приложение успешно подключено!")
        print(f"  ID:          {app.app_id}")
        print(f"  Название:    {app.name}")
        print(f"  Платёжный бот: @{app.payment_processing_bot_username}")
        print(f"{'='*50}")

        # Получаем балансы (без await!)
        print("\nПолучаем балансы...")
        balances = cp.get_balance()

        if balances:
            print(f"\n{'='*50}")
            print(f"  Балансы приложения:")
            for b in balances:
                print(f"    {b.currency_code}:")
                print(f"      Доступно:  {b.available}")
                print(f"      Заморожено: {b.onhold}")
            print(f"{'='*50}")
        else:
            print("\nБалансы пусты.")

        # Поддерживаемые активы
        print(f"\nПоддерживаемые криптовалюты ({len(app.supported_assets)}):")
        print(f"  {', '.join(a.asset_code for a in app.supported_assets)}")

    except CryptoPayError as e:
        print(f"\n❌ Ошибка Crypto Pay: {e}")
    except Exception as e:
        print(f"\n❌ Неожиданная ошибка: {e}")
    finally:
        print("\nПриложение завершило работу.")


if __name__ == "__main__":
    main()

⚡ Ключевое отличие

В синхронной версии не нужен asyncio.run(). Мы просто вызываем main() как обычную функцию. Все методы CryptoPay работают без await — библиотека автоматически создаёт синхронные обёртки.

📁

Структура проекта и .env

В реальных проектах токены и конфиденциальные данные не хранят в коде. Вместо этого используют переменные окружения и файл .env. Давай разберём правильную структуру проекта.

Рекомендуемая структура файлов

my_crypto_bot/
├── .env                    # Токены и конфигурация (НЕ в git!)
├── .env.example            # Шаблон .env (в git, без токенов)
├── .gitignore              # .env добавлен в игнор
├── main.py                 # Точка входа
├── config.py               # Чтение переменных окружения
├── requirements.txt        # Зависимости (aiosend, python-dotenv)
└── README.md               # Документация

Файл .env

В файле .env хранятся все настройки, специфичные для окружения:

.env · Пример файла
# Токены Crypto Pay
CRYPTOPAY_TOKEN=1234:ABCdefGHIjklmNOPqrstUVwxyz
CRYPTOPAY_TEST_TOKEN=5678:XYZabcDEFghijklMNOpqrstu

# Настройки таймаутов
CRYPTOPAY_TIMEOUT=30
CRYPTOPAY_POLLING_TIMEOUT=300
CRYPTOPAY_POLLING_DELAY=2

# Режим отладки
DEBUG=true

python-dotenv — загрузка переменных

Библиотека python-dotenv загружает переменные из .env в os.environ. Установка:

bash · Установка python-dotenv
pip install python-dotenv
# или
poetry add python-dotenv
# или
echo "python-dotenv>=1.0" >> requirements.txt
Python · config.py — чтение конфигурации
import os
from dotenv import load_dotenv

load_dotenv()


class Config:
    """Конфигурация приложения из переменных окружения."""

    # Токены
    CRYPTOPAY_TOKEN: str | None = os.getenv("CRYPTOPAY_TOKEN")
    CRYPTOPAY_TEST_TOKEN: str | None = os.getenv("CRYPTOPAY_TEST_TOKEN")

    # Таймауты
    TIMEOUT: int = int(os.getenv("CRYPTOPAY_TIMEOUT", "30"))
    POLLING_TIMEOUT: int = int(os.getenv("CRYPTOPAY_POLLING_TIMEOUT", "300"))
    POLLING_DELAY: int = int(os.getenv("CRYPTOPAY_POLLING_DELAY", "2"))

    # Режим
    DEBUG: bool = os.getenv("DEBUG", "false").lower() == "true"

    @classmethod
    def validate(cls):
        """Проверяет, что обязательные переменные установлены."""
        if not cls.CRYPTOPAY_TOKEN:
            raise ValueError(
                "CRYPTOPAY_TOKEN не установлен. "
                "Создайте .env файл на основе .env.example"
            )


# Проверяем конфигурацию при импорте
Config.validate()

.gitignore — защита секретов

Обязательно добавь .env в .gitignore, чтобы случайно не закоммитить токены в репозиторий:

.gitignore · Обязательные правила
# Файлы с секретами
.env
.env.local

# Виртуальное окружение
venv/
.venv/
__pycache__/
*.pyc

# IDE
.vscode/
.idea/

‼️ Никогда не коммить .env в git!

Если токен попадёт в публичный репозиторий — любой сможет создавать инвойсы от твоего имени. Используй .env.example как шаблон, а реальный .env храни локально. Регулярно проверяй, что .env есть в .gitignore.

❌

Обработка ошибок при запуске

При запуске первого приложения ты можешь столкнуться с несколькими типичными ошибками. Рассмотрим каждую и способ её решения.

Ошибка 1: Неверный токен (APIError)

Токен содержит опечатку, повреждён, отозван или принадлежит другой сети.

# Ошибка:
cp = CryptoPay(token="1234:INVALID_TOKEN")
app = await cp.get_me()
# → aiosend.exceptions.APIError: [401] Unauthorized

# Решение:
# 1. Проверьте токен в @CryptoBot или @CryptoTestnetBot
# 2. Создайте новый токен, если старый отозван
# 3. Убедитесь, что используете правильную сеть

Ошибка 2: WrongNetworkError

Токен от MAINNET, а сеть указана как TESTNET (или наоборот).

# Ошибка:
cp = CryptoPay(token="MAINNET_TOKEN", network=TESTNET)
# → WrongNetworkError: Token is served by MAINNET, you are using TESTNET

# Решение: укажите правильную сеть
cp = CryptoPay(token="MAINNET_TOKEN", network=MAINNET)

Ошибка 3: APITimeoutError

Сервер не отвечает в течение таймаута. Часто из-за блокировок сети или слишком малого таймаута.

# Ошибка:
cp = CryptoPay(token="TOKEN", timeout=5)  # слишком мало
app = await cp.get_me()
# → APITimeoutError: Request exceeded timeout of 5 seconds

# Решение: увеличьте таймаут
cp = CryptoPay(token="TOKEN", timeout=30)

Ошибка 4: ValueError (токен не указан)

Забыли загрузить .env или переменная окружения не установлена.

# Ошибка:
TOKEN = os.getenv("CRYPTOPAY_TOKEN")
# TOKEN = None, потому что .env не загружен
cp = CryptoPay(token=TOKEN)
# → ValueError: token must be a string

# Решение: вызовите load_dotenv() перед чтением переменных
load_dotenv()
TOKEN = os.getenv("CRYPTOPAY_TOKEN")

Всегда оборачивай запуск приложения в try/except для обработки этих ошибок. Вот универсальный шаблон:

Python · Универсальная обработка ошибок
import asyncio
from aiosend import CryptoPay
from aiosend.exceptions import (
    APIError,
    APITimeoutError,
    WrongNetworkError,
)

async def main():
    try:
        cp = CryptoPay(token="ТОКЕН")
        app = await cp.get_me()
        print(f"Успех: {app.name}")
    except WrongNetworkError as e:
        print(f"Неверная сеть: {e}")
    except APIError as e:
        print(f"Ошибка API [{e.code}]: {e.name}")
    except APITimeoutError as e:
        print(f"Таймаут: {e}")
    except Exception as e:
        print(f"Неизвестная ошибка: {e}")

asyncio.run(main())
🧩

Полный пример: main.py с config.py

Финальный пример — правильно организованное приложение с разделением на модули. Этот шаблон можно использовать как основу для любого проекта на aiosend.

Python · main.py — точка входа
import asyncio
import logging

from aiosend import CryptoPay
from aiosend.exceptions import CryptoPayError

from config import Config

# Настройка логирования
logging.basicConfig(
    level=logging.DEBUG if Config.DEBUG else logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)


def create_client() -> CryptoPay:
    """Создаёт и возвращает настроенный клиент CryptoPay."""
    token = (
        Config.CRYPTOPAY_TEST_TOKEN
        if Config.DEBUG
        else Config.CRYPTOPAY_TOKEN
    )
    logger.info(
        "Создание клиента для %s",
        "TESTNET" if Config.DEBUG else "MAINNET",
    )
    return CryptoPay(
        token=token,
        timeout=Config.TIMEOUT,
    )


async def main():
    """Главная функция приложения."""
    logger.info("Запуск приложения aiosend")

    cp = create_client()

    try:
        # Проверяем подключение
        app = await cp.get_me()
        logger.info(
            "Подключено: %s (ID: %d, бот: @%s)",
            app.name,
            app.app_id,
            app.payment_processing_bot_username,
        )

        # Получаем баланс
        balances = await cp.get_balance()
        for b in balances:
            logger.info(
                "Баланс %s: доступно %s, заморожено %s",
                b.currency_code,
                b.available,
                b.onhold,
            )

    except CryptoPayError as e:
        logger.error("Ошибка Crypto Pay: %s", e)
        return 1
    except Exception as e:
        logger.exception("Неожиданная ошибка: %s", e)
        return 1

    logger.info("Приложение завершило работу успешно")
    return 0


if __name__ == "__main__":
    exit_code = asyncio.run(main())
    exit(exit_code)

💡 Что мы здесь сделали?

• Разделили конфигурацию (config.py) и логику приложения (main.py)
• Добавили логирование через стандартный модуль logging
• Используем TESTNET в DEBUG-режиме, MAINNET — в продакшене
• Возвращаем код возврата (0 — успех, 1 — ошибка)
• Все исключения обрабатываются и логируются

📌

Что важно запомнить

1️⃣
async/await — основа aiosend. Все методы API асинхронные. Используй await cp.method() внутри async-функций и asyncio.run(main()) для запуска.
2️⃣
Синхронный режим — без asyncio. Вызывай методы без await — cp.get_me(). Библиотека автоматически создаёт синхронные обёртки. Для явного синхронного вызова используй cp.sync.method().
3️⃣
get_me() — первый метод для проверки. Возвращает объект App с ID, названием, ботом и списком поддерживаемых активов. Используй его для проверки, что клиент работает.
4️⃣
.env и python-dotenv. Никогда не храни токены в коде. Используй .env с python-dotenv. Добавь .env в .gitignore.
5️⃣
Всегда обрабатывай ошибки. WrongNetworkError, APIError, APITimeoutError — типичные исключения. Оборачивай вызовы API в try/except.
6️⃣
Структурируй проект. Разделяй конфигурацию, логику и точку входа. Используй конфиг-класс для типизированного доступа к переменным окружения.
💻

Практическая задача

Задача: Скрипт для проверки подключения к Crypto Pay

Напиши скрипт check_connection.py, который:

  1. Загружает токен из переменной окружения CRYPTOPAY_TOKEN через python-dotenv.
  2. Создаёт клиента CryptoPay с таймаутом 15 секунд.
  3. Вызывает get_me() и выводит ID приложения, название и username платёжного бота.
  4. Вызывает get_balance() и выводит все балансы.
  5. Выводит список поддерживаемых криптовалют (supported_assets).
  6. Обрабатывает все возможные исключения (APIError, APITimeoutError, WrongNetworkError).
  7. В случае ошибки завершается с кодом 1, при успехе — с кодом 0.

Подсказка: используй следующий шаблон для начала:

Python · Шаблон решения
import asyncio
import os
import sys
from dotenv import load_dotenv

from aiosend import CryptoPay
from aiosend.exceptions import (
    APIError,
    APITimeoutError,
    WrongNetworkError,
)

load_dotenv()

TOKEN = os.getenv("CRYPTOPAY_TOKEN")
if not TOKEN:
    print("❌ Ошибка: CRYPTOPAY_TOKEN не установлен в .env")
    sys.exit(1)


async def main():
    cp = CryptoPay(token=TOKEN, timeout=15)
    try:
        app = await cp.get_me()
        print(f"✅ Подключено: {app.name} (ID: {app.app_id})")
        print(f"   Бот: @{app.payment_processing_bot_username}")

        balances = await cp.get_balance()
        for b in balances:
            print(f"   {b.currency_code}: {b.available} (hold: {b.onhold})")

        print(f"   Активы: {[a.asset_code for a in app.supported_assets]}")
        return 0
    except WrongNetworkError as e:
        print(f"❌ Неверная сеть: {e}")
    except APIError as e:
        print(f"❌ Ошибка API [{e.code}]: {e.name}")
    except APITimeoutError as e:
        print(f"❌ Таймаут: {e}")
    except Exception as e:
        print(f"❌ Ошибка: {e}")
    return 1

if __name__ == "__main__":
    sys.exit(asyncio.run(main()))
🔍

Диагностика первого запуска

После написания первого приложения полезно знать, как проверить его работу и диагностировать возможные проблемы. Вот чек-лист для первого запуска:

Шаг Действие Ожидаемый результат
1 Установить зависимости pip install aiosend python-dotenv
2 Создать .env с токеном CRYPTOPAY_TOKEN=1234:your_token
3 Запустить скрипт Вывод информации о приложении
4 Проверить балансы Список валют с балансами
5 Проверить supported_assets Список из 12 доступных криптовалют

⚠️ Частая проблема: модуль не найден

Если при запуске ты получаешь ModuleNotFoundError: No module named 'aiosend' — убедись, что библиотека установлена. Проверь: pip list | grep aiosend. Если нет — установи: pip install aiosend.

💡 Полезная команда для отладки

Добавь в начало скрипта print(f"aiosend version: {aiosend.__version__}") и print(f"Python version: {sys.version}") — это поможет при диагностике проблем с версиями.

Урок 1.3: Первое приложение на aiosend

9 вопросов