Metadata-Version: 2.4
Name: fastsub
Version: 2.3.1
Summary: Официальный SDK FastSub — монетизация Telegram-ботов
Project-URL: Homepage, https://fastsub.org
Project-URL: Documentation, https://fastsub.org/docs
Project-URL: Source, https://github.com/Snowt69/fastsub
Author: FastSub
License: MIT
License-File: LICENSE
Keywords: aiogram,bot,fastsub,monetization,subscription,telegram
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: Chat
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.24
Provides-Extra: aiogram
Requires-Dist: aiogram>=3.0; extra == 'aiogram'
Provides-Extra: aiohttp
Requires-Dist: aiohttp>=3.9; extra == 'aiohttp'
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.100; extra == 'fastapi'
Provides-Extra: flask
Requires-Dist: flask>=2.2; extra == 'flask'
Provides-Extra: telebot
Requires-Dist: pytelegrambotapi>=4.14; extra == 'telebot'
Description-Content-Type: text/markdown

# fastsub

Официальный Python SDK [FastSub](https://fastsub.org) — монетизация Telegram-ботов
на обязательной подписке.

```bash
pip install fastsub
```

## Быстрый старт

```python
from fastsub import FastSub

async with FastSub(api_key="fsp_live_...") as fs:
    answer = await fs.request_op(user_id=123456789)
    for task in answer.tasks:
        print(task.title, task.link)
```

Ключа ещё нет? Его можно получить, не открывая бота:

```python
async with FastSub.bootstrap(confirm_key="fsc_...", bot_token="123:ABC") as fs:
    print(fs.api_key)
```

## Синхронный бот

```python
from fastsub import FastSubSync

with FastSubSync(api_key="fsp_live_...") as fs:
    answer = fs.request_op(user_id=123456789)
```

## aiogram — три строки вместо вызова в каждом хендлере

```python
dp.message.middleware(FastSubMiddleware(fs))
dp.callback_query.middleware(FastSubMiddleware(fs))
dp.include_router(fastsub_check_router(fs))
```

## Если что-то идёт не так

SDK намеренно не роняет вашего бота из-за наших проблем: middleware пропустит
юзера дальше, `telebot_gate` вернёт `True`. Раньше это происходило молча —
теперь есть логи:

```python
logging.getLogger("fastsub").setLevel(logging.DEBUG)
```

## Вебхуки

```python
from fastsub import WebhookEvents, fastsub_webhook_router

events = WebhookEvents()

@events.on("resource.verified")
async def paid(event):
    await credit(event.user_id, event.publisher_payout_rub)

@events.on("resource.unsubscribed", "resource.reverted")
async def taken_back(event):
    if event.money_was_taken:          # отписался, не дожив до конца холда
        await debit(event.user_id, event.publisher_payout_rub)

app.include_router(fastsub_webhook_router(SECRET, events))
```

Подпись проверяется до вызова обработчика. Вручную — `verify_webhook(secret, body, signature)`.

Обработчик получает `WebhookEvent`: `event.money_is_mine`, `event.money_was_taken`,
`event.in_hold` — вместо угадывания, что значит `payout_reversed`. Словарём он
быть не перестал, старые обработчики менять не нужно.

Повторы приёмник отсеивает сам по `X-FastSub-Delivery-Id`: доставка у нас
«хотя бы один раз», так что без этого бонус начисляется дважды. На нескольких
воркерах памяти одного процесса мало — передайте свою проверку:

```python
def seen(delivery_id: str) -> bool:
    return bool(redis.set(f"fs:{delivery_id}", 1, nx=True, ex=86400))

fastsub_webhook_router(SECRET, events, dedupe=seen)
```

Не на FastAPI — есть `fastsub_aiohttp_handler` и, для синхронных ботов,
`fastsub_flask_blueprint(SECRET, on_event)`.

## Рекламодателю

```python
from fastsub import FastSubAdvertiser

async with FastSubAdvertiser(api_key="fsa_live_...") as adv:
    order = await adv.order(123)
    print(order["forecast"]["text"], order["retention"]["summary"])
```

Документация: <https://fastsub.org/docs>
Что изменилось: `CHANGELOG.md` в пакете, он же — «Обновления» в нашем боте.
