Compare commits
2 Commits
dev
...
1c403da75b
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c403da75b | |||
| 1b8e8c9a79 |
@ -1,18 +1,18 @@
|
||||
repos:
|
||||
- repo: https://github.com/crate-ci/typos
|
||||
rev: v1.42.0
|
||||
rev: v1.36.3
|
||||
hooks:
|
||||
- id: typos
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.14.13
|
||||
rev: v0.13.2
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [ --fix ]
|
||||
- id: ruff-format
|
||||
|
||||
- repo: https://github.com/RobertCraigie/pyright-python
|
||||
rev: v1.1.408
|
||||
rev: v1.1.405
|
||||
hooks:
|
||||
- id: pyright
|
||||
|
||||
|
||||
170
README.md
170
README.md
@ -1,170 +0,0 @@
|
||||
# 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)
|
||||
|
||||
---
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
[project]
|
||||
name = "twitchclient"
|
||||
version = "1.1.0"
|
||||
version = "1.0.1"
|
||||
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"]
|
||||
@ -13,10 +18,10 @@ build-backend = "uv_build"
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"ruff==0.14.13",
|
||||
"pyright==1.1.408",
|
||||
"poethepoet==0.40.0",
|
||||
"pre-commit==4.5.1",
|
||||
"ruff==0.14.9",
|
||||
"pyright==1.1.407",
|
||||
"poethepoet==0.38.0",
|
||||
"pre-commit==4.5.0",
|
||||
]
|
||||
|
||||
[[tool.uv.index]]
|
||||
|
||||
@ -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' if redis_url else None,
|
||||
limit=700 if redis_url else None,
|
||||
key='twitch',
|
||||
limit=700,
|
||||
logger='Twitch API',
|
||||
)
|
||||
|
||||
@ -67,7 +67,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.AdSchedule.model_validate(req.json())
|
||||
return s.AdSchedule.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -86,7 +86,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.SnoozeNextAd.model_validate(req.json())
|
||||
return s.SnoozeNextAd.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED | st.TOO_MANY_REQUESTS:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -122,7 +122,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.ExtensionAnalytics.model_validate(req.json())
|
||||
return s.ExtensionAnalytics.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -158,7 +158,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.GameAnalytics.model_validate(req.json())
|
||||
return s.GameAnalytics.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -190,7 +190,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.BitsLeaderboard.model_validate(req.json())
|
||||
return s.BitsLeaderboard.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -209,7 +209,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.Cheermotes.model_validate(req.json())
|
||||
return s.Cheermotes.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -241,7 +241,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.ExtensionTransactions.model_validate(req.json())
|
||||
return s.ExtensionTransactions.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -262,7 +262,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.ChannelsInformation.model_validate(req.json())
|
||||
return s.ChannelsInformation.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED | st.TOO_MANY_REQUESTS:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -328,7 +328,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.ChannelEditors.model_validate(req.json())
|
||||
return s.ChannelEditors.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED | st.TOO_MANY_REQUESTS:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -358,7 +358,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.FollowedChannels.model_validate(req.json())
|
||||
return s.FollowedChannels.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED | st.TOO_MANY_REQUESTS:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -390,7 +390,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.ChannelFollowers.model_validate(req.json())
|
||||
return s.ChannelFollowers.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED | st.TOO_MANY_REQUESTS:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -446,7 +446,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.CustomRewards.model_validate(req.json())
|
||||
return s.CustomRewards.model_validate(req.json()).data
|
||||
|
||||
case (
|
||||
st.BAD_REQUEST
|
||||
@ -506,7 +506,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.CustomRewards.model_validate(req.json())
|
||||
return s.CustomRewards.model_validate(req.json()).data
|
||||
|
||||
case (
|
||||
st.BAD_REQUEST
|
||||
@ -550,7 +550,9 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.CustomRewardRedemptions.model_validate(req.json())
|
||||
return s.CustomRewardRedemptions.model_validate(
|
||||
req.json()
|
||||
).data
|
||||
|
||||
case (
|
||||
st.BAD_REQUEST
|
||||
@ -613,7 +615,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.CustomRewards.model_validate(req.json())
|
||||
return s.CustomRewards.model_validate(req.json()).data
|
||||
|
||||
case (
|
||||
st.BAD_REQUEST
|
||||
@ -631,8 +633,8 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
self,
|
||||
access_token: str,
|
||||
broadcaster_id: int,
|
||||
reward_id: str,
|
||||
redemption_id: str | list[str],
|
||||
reward_id: int,
|
||||
redemption_id: int | list[int],
|
||||
status: Literal['CANCELED', 'FULFILLED'],
|
||||
):
|
||||
req = await self.post(
|
||||
@ -673,7 +675,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.CharityCampaign.model_validate(req.json())
|
||||
return s.CharityCampaign.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -702,14 +704,14 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.CharityDonations.model_validate(req.json())
|
||||
return s.CharityDonations.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -728,7 +730,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
cache_time: int | None = None,
|
||||
):
|
||||
req = await self.get(
|
||||
'/chat/chatters',
|
||||
'/chatters',
|
||||
params=self.clean_dict(
|
||||
{
|
||||
'broadcaster_id': broadcaster_id,
|
||||
@ -740,14 +742,14 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.Chatters.model_validate(req.json())
|
||||
return s.Chatters.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -767,7 +769,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params={'broadcaster_id': broadcaster_id},
|
||||
@ -791,7 +793,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
)
|
||||
@ -818,7 +820,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params={'emote_set_id': emote_set_id},
|
||||
@ -846,7 +848,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params={'broadcaster_id': broadcaster_id},
|
||||
@ -870,7 +872,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
)
|
||||
@ -898,7 +900,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -911,7 +913,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.ChatSettings.model_validate(req.json())
|
||||
return s.ChatSettings.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -931,7 +933,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params={'broadcaster_id': broadcaster_id},
|
||||
@ -939,7 +941,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.SharedChatSession.model_validate(req.json())
|
||||
return s.SharedChatSession.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -961,7 +963,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -1025,7 +1027,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.ChatSettings.model_validate(req.json())
|
||||
return s.ChatSettings.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -1121,7 +1123,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.Message.model_validate(req.json())
|
||||
return s.Message.model_validate(req.json()).data
|
||||
|
||||
case (
|
||||
st.BAD_REQUEST
|
||||
@ -1146,7 +1148,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params={'user_id': user_id},
|
||||
@ -1231,7 +1233,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -1251,7 +1253,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.OK:
|
||||
return s.Clips.model_validate(req.json())
|
||||
return s.Clips.model_validate(req.json()).data
|
||||
|
||||
case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND:
|
||||
raise s.ClientError(req.status_code, req.json()['message'])
|
||||
@ -1273,7 +1275,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -1308,7 +1310,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
)
|
||||
@ -1390,7 +1392,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -1498,7 +1500,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -1570,7 +1572,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {jwt_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -1703,7 +1705,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -1737,7 +1739,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {jwt_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params={'extension_id': extension_id},
|
||||
@ -1814,7 +1816,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {jwt_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -1848,7 +1850,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -1881,7 +1883,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -2023,7 +2025,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -2061,7 +2063,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -2097,7 +2099,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -2131,7 +2133,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params={'broadcaster_id': broadcaster_id},
|
||||
@ -2161,7 +2163,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params={'broadcaster_id': broadcaster_id},
|
||||
@ -2189,7 +2191,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params={'broadcaster_id': broadcaster_id},
|
||||
@ -2248,7 +2250,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params={
|
||||
@ -2509,7 +2511,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -2632,7 +2634,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -2669,7 +2671,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -2750,7 +2752,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -2867,7 +2869,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params={
|
||||
@ -2941,7 +2943,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -3045,7 +3047,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -3201,7 +3203,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -3239,7 +3241,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params={'broadcaster_id': broadcaster_id},
|
||||
@ -3409,7 +3411,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -3442,7 +3444,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -3477,7 +3479,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params={'broadcaster_id': broadcaster_id},
|
||||
@ -3512,7 +3514,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -3553,7 +3555,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -3623,7 +3625,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -3664,7 +3666,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -3700,7 +3702,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -3733,7 +3735,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params={'broadcaster_id': broadcaster_id},
|
||||
@ -3762,7 +3764,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict({'name': name, 'id': team_id}),
|
||||
@ -3791,7 +3793,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict({'id': user_id, 'login': login}),
|
||||
@ -3840,7 +3842,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params={'user_id': user_id},
|
||||
@ -3875,7 +3877,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
@ -3960,7 +3962,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
)
|
||||
@ -3987,7 +3989,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params={'user_id': user_id},
|
||||
@ -4044,7 +4046,7 @@ class TwitchAPIClient(AioHTTPXClient):
|
||||
headers=self.clean_dict(
|
||||
{
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'X-Cache-TTL': str(cache_time),
|
||||
'X-Cache-TTL': cache_time,
|
||||
}
|
||||
),
|
||||
params=self.clean_dict(
|
||||
|
||||
@ -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' if redis_url else None,
|
||||
limit=700 if redis_url else None,
|
||||
key='twitch',
|
||||
limit=700,
|
||||
logger='Twitch Auth',
|
||||
)
|
||||
|
||||
|
||||
@ -309,7 +309,7 @@ class CustomReward(BaseSchema):
|
||||
broadcaster_id: int
|
||||
broadcaster_login: str
|
||||
broadcaster_name: str
|
||||
id: str
|
||||
id: int
|
||||
title: str
|
||||
prompt: str
|
||||
cost: int
|
||||
@ -1263,7 +1263,6 @@ class Stream(BaseSchema):
|
||||
type: Literal['live']
|
||||
title: str
|
||||
tags: list[str]
|
||||
tag_ids: list[str] = Field(..., deprecated=True)
|
||||
viewer_count: int
|
||||
started_at: datetime
|
||||
language: str
|
||||
@ -1390,7 +1389,6 @@ class User(BaseSchema):
|
||||
offline_image_url: str
|
||||
email: str | None = None
|
||||
created_at: datetime
|
||||
view_count: int = Field(..., deprecated=True)
|
||||
|
||||
|
||||
class Users(BaseSchema):
|
||||
|
||||
Reference in New Issue
Block a user