Metadata-Version: 2.5
Name: safesub
Version: 1.0.5
Summary: Клиент SafeMembers: проверка обязательных подписок в Telegram-ботах (с готовым middleware для aiogram 3)
Project-URL: Homepage, https://github.com/PGSAVE/safemembers
Project-URL: Repository, https://github.com/PGSAVE/safemembers
Author-email: PGSAVE <i@sxve.it>
License: MIT
Requires-Python: >=3.10
Requires-Dist: aiohttp>=3.9
Provides-Extra: aiogram
Requires-Dist: aiogram<4,>=3.0; extra == 'aiogram'
Description-Content-Type: text/markdown

# safesub

> Пакет переименован: раньше он назывался `safesubs`. Ставьте `safesub`, импорт `from safesub import SafeSubs`;
> старый `safesubs` удалите (`pip uninstall safesubs`), он больше не обновляется.

Клиент сервиса [SafeMembers](https://github.com/PGSAVE/safemembers) - проверка обязательных
подписок (свои каналы + SubGram + Flyer) в Telegram-ботах.

```
pip install safesub
```

## aiogram 3 - одна строка

```python
from safesub import SafeSubs
from safesub.aiogram import setup

subs = SafeSubs("sm_ваш_ключ", api_url="https://subs.example.com")

setup(dp, subs)
```

Middleware сам показывает пользователю кнопки "Подписаться" и "❇️ [Проверить]", обрабатывает
нажатие "Проверить" и пропускает подписанных дальше. Текст над кнопками по умолчанию берётся
из панели SafeMembers (настройка бота «Текст ОП»). Тексты можно задать и параметрами:

```python
setup(
    dp,
    subs,
    text="Для продолжения подпишитесь на каналы:",  # по умолчанию - текст из панели
    check_button="❇️ [Проверить]",
    not_yet_text="Вы подписались не на всё",
    ok_text="Спасибо! Продолжайте",
    only_private=True,  # в группах не проверять
    skip=lambda event: False,  # свои исключения (например, команда /help)
)
```

Точечная проверка в отдельных хендлерах - фильтр:

```python
from safesub.aiogram import SubscriptionFilter


@dp.message(Command("download"), SubscriptionFilter(subs))
async def download(message): ...
```

## Любой фреймворк

```python
result = await subs.check(user_id, language_code="ru")
if result.status:
    ...  # подписан на всё
else:
    for link in result.links:
        print(link.text, link.url)  # нарисуйте кнопки + кнопку "Проверить", по ней снова вызовите check()
```

По умолчанию каждый вызов обращается к серверу: холд применяется на стороне SafeMembers.
При холде 0 сервер проверяет подписки заново и возвращает `cache_ttl=0`.

В версии 1.0.4 журнал `safesub.audit` фиксирует запросы, ответы, локальный кэш,
пропуски по ошибке (`fail_open`) и результат обновления кнопок, без текстов сообщений и ключей.
`CheckResult.request_id` связывает запись с серверным `logs/checks.jsonl`.
Для сохранения журнала на диск (10 МиБ × 5 файлов, каталог должен быть постоянным):

```python
from safesub.audit import configure_file

configure_file("logs/client-checks.jsonl")
```
С версии 1.0.5 `CheckResult.text` содержит текст над кнопками из панели SafeMembers
(пустой у серверов старее и при пропуске по ошибке).

Локальный кэш можно включить явно: `SafeSubs(..., cache=True)`. Тогда положительные ответы
кэшируются на `cache_ttl` минут, и изменение холда в панели не будет видно до истечения
старого кэша или вызова `check(..., force=True)`. Для немедленного применения настроек
используйте `cache=False` (это также нужно явно указать в старых версиях клиента).
При недоступности сервера клиент по умолчанию **пропускает** пользователя (`fail_open=True`).

REST-API без библиотеки: `POST {api_url}/v1/check`, заголовок `Authorization: Bearer <key>`,
тело `{"user_id": 123}` → `{"status": true|false, "cache_ttl": 1440, "links": [...]}`.
