From fc7cc89301b54b809347140d847b52c9d58c87cb Mon Sep 17 00:00:00 2001 From: Miwory Date: Fri, 16 Jan 2026 09:54:47 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9C=D0=B0=D0=BB=D0=B5=D0=BD=D1=8C=D0=BA?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20=D0=B8=20?= =?UTF-8?q?=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .pre-commit-config.yaml | 6 +- README.md | 170 +++++++++++++++++++++++++++++++++++++++ pyproject.toml | 19 ++--- src/twitchclient/api.py | 6 +- src/twitchclient/auth.py | 6 +- 5 files changed, 186 insertions(+), 21 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a42ca3c..cc364d5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,18 +1,18 @@ repos: - repo: https://github.com/crate-ci/typos - rev: v1.36.3 + rev: v1.42.0 hooks: - id: typos - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.13.2 + rev: v0.14.13 hooks: - id: ruff args: [ --fix ] - id: ruff-format - repo: https://github.com/RobertCraigie/pyright-python - rev: v1.1.405 + rev: v1.1.408 hooks: - id: pyright diff --git a/README.md b/README.md index e69de29..1b37421 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,170 @@ +# Twitch Client (Async) + +An internal **async Twitch API client** built on top of **AioHTTPX**, providing typed access to the Twitch **Helix API** and **OAuth2** endpoints with built-in rate limiting and optional caching. + +--- + +## What it provides + +* `TwitchAPIClient` — async client for Twitch **Helix API** +* `TwitchAuthClient` — async client for Twitch **OAuth2** +* Built on **AioHTTPX** (aiohttp transport, Redis rate limiting & caching) +* Strongly typed responses using **Pydantic models** +* Automatic request parameter cleanup (`None` values removed) + +--- + +## Installation (internal) + +Configure your private package index, then: + +```bash +pip install twitchclient +``` + +--- + +## Authentication + +### OAuth helper client + +```python +from twitchclient.auth import TwitchAuthClient +from twitchclient import scopes + +auth = TwitchAuthClient( + client_id="CLIENT_ID", + client_secret="CLIENT_SECRET", + redirect_uri="https://your.app/callback", +) + +url = await auth.create_authorization_code_grant_flow_url( + scope=[scopes.USER_READ_EMAIL, scopes.CHAT_READ], +) +``` + +### App access token + +```python +token = await auth.app_access_token() +print(token.access_token) +``` + +### User access token + +```python +token = await auth.user_access_token(code="AUTH_CODE") +``` + +--- + +## API Client + +```python +from twitchclient.api import TwitchAPIClient + +client = TwitchAPIClient( + client_id="CLIENT_ID", + client_secret="CLIENT_SECRET", + redirect_uri="https://your.app/callback", + redis_url="redis://localhost:6379", # optional +) +``` + +* Base URL: `https://api.twitch.tv/helix` +* `Client-Id` header is set automatically +* Rate limit defaults to **700 req/min** when Redis is enabled + +--- + +## Example usage + +### Get channel information + +```python +channels = await client.get_channel_information( + access_token=token.access_token, + broadcaster_id=123456, +) +``` + +### Start a commercial + +```python +await client.start_commercial( + access_token=token.access_token, + broadcaster_id=123456, +) +``` + +### Cached request + +Caching is controlled via the `X-Cache-TTL` header: + +```python +streams = await client.get_streams( + access_token=token.access_token, + game_id=509658, + cache_time=30, +) +``` + +--- + +## Rate limiting & caching + +* **Redis-backed** +* Shared key: `twitch` +* Enabled automatically when `redis_url` is provided +* Cache TTL is per-request (`X-Cache-TTL` header) + +Implementation is inherited from **AioHTTPX transports** . + +--- + +## Errors + +All API methods raise typed exceptions: + +* `ClientError(status_code, message)` +* `InternalError(status_code, message)` + +Defined in schema module . + +--- + +## Scopes + +OAuth scopes are defined as constants and typed literals: + +```python +from twitchclient import scopes + +scopes.CHANNEL_READ_SUBSCRIPTIONS +scopes.CHAT_READ +``` + +See `scopes.py` for the full list . + +--- + +## Typed responses + +All successful responses return **Pydantic models**, e.g.: + +* `ChannelsInformation` +* `Streams` +* `Users` +* `EventsubSubscriptions` + +Models live in `schema.py` . + +--- + +## Notes & limitations + +* Async-only (no sync client) +* Internal API, no stability guarantees +* Some endpoints still marked TODO (e.g. Guest Star) + +--- diff --git a/pyproject.toml b/pyproject.toml index a891b08..f79ee06 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,16 +1,11 @@ [project] name = "twitchclient" -version = "1.0.7" +version = "1.1.0" description = "Client for Twitch API" readme = "README.md" -authors = [ - { name = "Miwory", email = "miwory.uwu@gmail.com" } -] +authors = [{ name = "Miwory", email = "miwory.uwu@gmail.com" }] requires-python = ">=3.13" -dependencies = [ - "aiohttpx>=1.3,<=2.0", - "pydantic>=2.12,<=2.13", -] +dependencies = ["aiohttpx>=1.3,<=2.0", "pydantic>=2.12,<=2.13"] [build-system] requires = ["uv_build>=0.9.2,<0.10.0"] @@ -18,10 +13,10 @@ build-backend = "uv_build" [project.optional-dependencies] dev = [ - "ruff==0.14.9", - "pyright==1.1.407", - "poethepoet==0.38.0", - "pre-commit==4.5.0", + "ruff==0.14.13", + "pyright==1.1.408", + "poethepoet==0.40.0", + "pre-commit==4.5.1", ] [[tool.uv.index]] diff --git a/src/twitchclient/api.py b/src/twitchclient/api.py index 5fd92bc..66183b3 100644 --- a/src/twitchclient/api.py +++ b/src/twitchclient/api.py @@ -13,10 +13,10 @@ from .eventsub import types as sub_type class TwitchAPIClient(AioHTTPXClient): def __init__( self, - redis_url: str, client_id: str, client_secret: str, redirect_uri: str, + redis_url: str | None = None, ): self.base_uri = 'https://api.twitch.tv/helix' self.client_id = client_id @@ -27,8 +27,8 @@ class TwitchAPIClient(AioHTTPXClient): base_url=self.base_uri, headers={'Client-Id': self.client_id}, redis_url=redis_url, - key='twitch', - limit=700, + key='twitch' if redis_url else None, + limit=700 if redis_url else None, logger='Twitch API', ) diff --git a/src/twitchclient/auth.py b/src/twitchclient/auth.py index c00d83f..a383387 100644 --- a/src/twitchclient/auth.py +++ b/src/twitchclient/auth.py @@ -10,10 +10,10 @@ from . import scopes class TwitchAuthClient(AioHTTPXClient): def __init__( self, - redis_url: str, client_id: str, client_secret: str, redirect_uri: str, + redis_url: str | None = None, ): self.base_uri = 'https://id.twitch.tv/oauth2' self.client_id = client_id @@ -23,8 +23,8 @@ class TwitchAuthClient(AioHTTPXClient): super().__init__( base_url=self.base_uri, redis_url=redis_url, - key='twitch', - limit=700, + key='twitch' if redis_url else None, + limit=700 if redis_url else None, logger='Twitch Auth', ) -- 2.47.2