Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Funora для Python

Эталонная реализация контракта Funora.

status PyPI license FunPay

English


Неофициальный проект. Funora не аффилирована с FunPay, не одобрена ею и никак с ней не связана. Работает с приватным веб-интерфейсом, который может измениться в любой момент без предупреждения. Использование может привести к блокировке аккаунта и заморозке средств - этот риск несёте вы. Прочитайте DISCLAIMER.md прежде, чем строить на этом то, что приносит вам деньги.

Тестовый pre-alpha: 0.0.1.dev2

Установка из PyPI:

python -m pip install "funora==0.0.1.dev2"

Установка и ограничения описаны в заметке о выпуске. Контракт пока имеет статус draft; сборка пакета не означает проверку всех операций на действующем аккаунте. Новые версии на PyPI выходят на крупных этапах; текущая доработка идёт в ветках и PR.

Реализованы и проверяются тестами тридцать пять операций: двадцать четыре чтения и одиннадцать записей - отправка текста и картинки, отметка прочтения, отзыв и его снятие, правка цены лота, поднятие предложений, включение и выключение лота, смена валюты интерфейса и возврат по заказу.

Руководство: docs/index.md. Оно собирается в сайт (mkdocs serve) и проверяется тем же прогоном, что и код: примеры разбираются интерпретатором, ссылки разрешаются, а каждая упомянутая операция ищется на настоящем клиенте.

Что это

Python SDK для FunPay. Здесь протокол прорабатывается первым; остальные языки реализуют его заново по спецификации, а не портируют этот код построчно.

from funora import Client, EnvSecretProvider

# Секрет берётся из FUNORA_GOLDEN_KEY и в коде не появляется ни разу.
with Client(EnvSecretProvider()) as client:
    page = client.orders.list()
    for order in page.rows():
        print(order.order_id, order.description_text)

То же асинхронно. Фасада два, ядро одно: нормативный порядок шагов, политика повторов, расход бюджета и правила курсора написаны один раз и обоим достаются готовыми. Перевод бота сводится к await.

from funora import AsyncClient, EnvSecretProvider

async with AsyncClient(EnvSecretProvider()) as client:
    page = await client.orders.list()
    for order in page.rows():
        print(order.order_id, order.description_text)

Что уже работает

Операция Возвращает
client.orders.list() список продаж сокращёнными записями
client.orders.get(order_id) один заказ целиком
client.orders.details(*ids) заказы структурно: сумма числом, валюта кодом, стороны порознь
client.orders.refund(order_id) средства возвращены покупателю
client.chats.list() список диалогов
client.chats.thread(node_id) переписку с определением происхождения сообщений
client.chats.send_text(node_id, text) квитанцию отправки с исходом
client.chats.mark_read(node_id) диалог помечен прочитанным
client.chats.send_image(node_id, content, ...) картинка в переписке
client.chats.buyer_viewing(node_id, *buyer_ids) что покупатель смотрит сейчас
client.lots.list_own(node_id) свои лоты раздела с идентификаторами предложений
client.lots.form(node_id, offer_id) форму правки лота и признак показа в выдаче
client.lots.update_price(...) лот с новой ценой, всё прочее нетронутым
client.lots.promote(game_id, node_id) поднятие всех предложений раздела
client.lots.calculate_prices(node_id, price) что заплатит покупатель
client.lots.activate(...) лот в выдаче
client.lots.deactivate(...) лот снят с выдачи
client.lots.showcase(user_id) витрину продавца разделами
client.market.offers(node_id) публичные предложения раздела: чужие цены и продавцы
client.market.snapshot(node_id) снимок выдачи для сравнения во времени
client.market.chips(node_id) второй рынок: предложения по количеству
client.reviews.get(user_id, rating=None, cursor=None) страница отзывов, отбор по оценке 1..5 и курсор продолжения
client.reviews.leave(order_id, rating=..., text=...) отзыв к заказу
client.reviews.remove(order_id) отзыв снят
client.account.get() личность аккаунта
client.account.refresh() её же, перечитанную
client.account.health() пригодность сессии
client.account.balance() баланс и операции
client.account.switch_currency(code) валюта показа сменена
client.account.capabilities() что из объявленного доступно
client.catalog.categories(refresh=False) разделы площадки с кэшем
client.catalog.search(query) публичный поиск игр и разделов; полнота совпадений не подтверждена
client.catalog.field_schema(section_id) поля фильтров раздела, варианты выбора и диапазоны
client.chats.history_before(node_id, cursor=...) предыдущие сообщения и сохраняемый курсор
client.market.calculate_chip_prices(game_id, price) расчёт цены на рынке по количеству

Реакция за секунды, а не за минуты

У площадки есть собственный канал обновлений - POST /runner/, промежуток пять секунд, - и наблюдение его слушает. Пока канал молчит, страницы не читаются вовсе; сказал «изменилось» - читаются немедленно. Прежде изменение замечалось за время опроса: от трёх секунд при активности до двух минут в тишине.

Из канала берётся ОДНО решение: изменилось что-нибудь или нет. События по-прежнему собираются чтением страниц, тем же кодом и с теми же гарантиями. Так и задумано: поведения канала при истёкшей сессии и при исчерпании предела не наблюдал никто, а сигналу верить не нужно - ошибка в одну сторону стоит лишнего чтения страниц, в другую ловится сторожевым сроком в две минуты.

Непонятный ответ канала не роняет наблюдение: оно возвращается к опросу страниц и говорит об этом в журнал. Выключить быстрый путь целиком - client.watch(router, use_channel=False).

Для операций записи существенны исход запроса и сохранность прежнего состояния.

Отправка текста - глава в руководстве: исходов у неё три, а не два, и третий - «неизвестно» - это то, ради чего глава написана.

Правка цены - глава про лоты: отправляется прочитанное целиком, меняется ровно одно поле, а прежняя цена ложится в долговечный журнал раньше, чем уходит запрос. Без файла состояния операция отказывает: у площадки нет ни истории цен, ни отката, и «как было» знает только наша запись.

Поверх этого есть слой бота: funora.bot. Он даёт очередь исходящих, в которую можно класть из любого потока - скажем, из обработчика телеграм-бота, - а отправляет тот же поток, что ведёт наблюдение. Прямой вызов из чужого потока портит счёт ограничителя молча, и потому отвергается вслух.

Есть и вторая очередь - каталог с файлами, для телеграм-бота, поднятого ОТДЕЛЬНОЙ командой: до очереди в памяти он не дотягивается ничем. Задание, взятое умершим процессом, повторно не отправляется никогда - его судьба неизвестна, и решает о нём человек. Как это выглядит целиком - в главе про бота.

Полный реестр незавершённых механизмов с причинами лежит в Funora-spec/spec/conformance/not-implemented.yaml. Он включает ограничения планировщика, недостающие наблюдения и ещё не исполняемые части общего контракта. Наличие всех сервисных методов не означает, что весь межъязыковой контракт завершён.

Чего SDK пока не умеет

  • Вывод средств не реализован.
  • Догрузка длинных списков заказов и операций счёта: семантика continue ещё не установлена. Предыдущие сообщения чата читаются отдельной операцией.
  • Восстановление пропущенных событий канала по позиции: сейчас используются повторное чтение страниц и сохранённые курсоры наблюдения.
  • Отмена уже отправленного запроса ради более приоритетного.

Публичное чтение рынка уже использует отдельный транспорт без секрета аккаунта. Общий сетевой бюджет и пауза после HTTP 429 сохраняются.

Читаются состояния заказа paid, closed и refunded. Другие носители сохраняются как ненаблюдённое значение; выдачу нужно разрешать по конкретному состоянию, а не по условию «не закрыт». Сообщение в переписке не подтверждает оплату.

orders.details() читает сумму числом и код валюты из структурного ответа. Точное время нельзя восстановить там, где площадка даёт только текст для показа.

Возврат по заказу уже реализован: доступность формы проверяется перед запросом, а результат определяется по ответу. Проверка этой реализации на записанных ответах не заменяет проверку на действующем тестовом аккаунте.

Подробности: границы SDK, план наблюдений.

Как устроено

Три решения, которые видно в первом же вызове.

Результат - страница, а не список. Записи получают методом rows(), и при неполноте нужен явный accept_incomplete=True. Молча отданный неполный список неотличим от полного, и обработчик примет решение по данным, которых нет.

Поля различают «пусто» и «не наблюдалось». None одинаково выглядит для обоих случаев, а решения по ним противоположные: пустое описание перечитывать незачем, отсутствующее - повод заподозрить изменение вёрстки. Поэтому чтение .value у ненаблюдённого поля бросает исключение, а не возвращает None.

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

Подробнее - в docs/architecture.md, а как этим пользоваться - в руководстве.

Наблюдения за протоколом

Пакет содержит инструмент funora-observe, которым собраны все факты о протоколе, на которых стоит спецификация. Он сохраняет структурный скелет страницы: разметка целиком, текст и значения атрибутов заменены подписями.

Проект целиком

Funora - это один контракт, реализованный нативно на нескольких языках. Меняется язык, но не ментальная модель: Client, сервисы, события, роутер и таксономия ошибок означают одно и то же везде.

Репозиторий Что это Статус
Funora Один контракт, один набор тестовых векторов, нативный SDK на каждый язык. design
Funora-spec Канонический контракт, который реализует каждый SDK. design
Funora-codegen Генерирует скучную повторяющуюся часть каждого SDK. design
Funora-conformance Тестовый контракт между языками. design
Funora-python Эталонная реализация контракта Funora. draft
Funora-javascript Исходник на TypeScript, на выходе JavaScript и декларации типов. planned
Funora-java Java SDK. planned
Funora-dotnet .NET SDK. planned
Funora-cpp C++ SDK. planned
Funora-c C SDK - самый узкий контракт в проекте. planned
Funora-docs Документация всех SDK из одного источника. design
Funora-examples Сквозные примеры, которые реально прогоняет CI. planned

Участие в разработке

Сначала прочитайте CONTRIBUTING.md.

Полезнее всего сейчас три вещи.

Снимки страниц в состояниях, которых у нас нет: заказ в возврате или споре, непрочитанный диалог, длинный список с постраничной навигацией. Каждый такой снимок закрывает пункт в docs/protocol-questions.md.

Разбор спецификации в Funora-spec: она проверяется употреблением, и первая же попытка её применить дала восемнадцать мест, где она противоречила сама себе.

Реализация операций чтения по уже написанным правилам извлечения.

Безопасность

Никогда не вставляйте сессионный ключ, сырой HTML со страницы под авторизацией или содержимое личной переписки в публичный issue. Сессионный ключ FunPay - это доступ ко всему аккаунту. Сообщайте приватно через Security Advisories, подробности - в SECURITY.md.

Лицензия

Apache-2.0 © Funora Contributors

Release files for funora 0.0.1.dev2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for funora 0.0.1.dev2
File Size Uploaded
funora-0.0.1.dev2.tar.gz 1.6 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for funora 0.0.1.dev2
File Interpreter ABI Platform
funora-0.0.1.dev2-py3-none-any.whl Python 3 none any Details

Total release size: 2.1 MB

Release files / funora-0.0.1.dev2.tar.gz

Download URL funora-0.0.1.dev2.tar.gz
Size 1.6 MB
Tags Source
SHA-256 checksum
How to use checksums
65bf746f9731946e923e708c4afe8057b15db9432737bfda76d1998c940d03d2
BLAKE2b-256 checksum
How to use checksums
faa97883c20af8e4ed883b10256f4b94b27aeb3d9b64ba33cf93c786e9c1648f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.

Transparency log

Release files / funora-0.0.1.dev2-py3-none-any.whl

Download URL funora-0.0.1.dev2-py3-none-any.whl
Size 513.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d13c4029eca6bbab8b704e3834c3f1189d4b07e0ecff89280e744ad62e804db1
BLAKE2b-256 checksum
How to use checksums
51bce62578cd2643340a5b3ff1abf88bd0643be119923fdae3722b288460d11e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.0.1.dev2 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page