Релиз
This commit is contained in:
0
src/oxidedonationalerts/__init__.py
Normal file
0
src/oxidedonationalerts/__init__.py
Normal file
145
src/oxidedonationalerts/api.py
Normal file
145
src/oxidedonationalerts/api.py
Normal file
@ -0,0 +1,145 @@
|
||||
from typing import Literal, Never
|
||||
|
||||
from oxidehttp.client import OxideHTTP
|
||||
from oxidehttp.schema import CachedResponse, Response
|
||||
from pydantic import BaseModel
|
||||
|
||||
from . import schema as s
|
||||
|
||||
|
||||
class DonationAlertsAPIClient(OxideHTTP):
|
||||
def __init__(
|
||||
self,
|
||||
redis_url: str | None = None,
|
||||
proxy_url: str | None = None,
|
||||
) -> None:
|
||||
self.base_uri = 'https://www.donationalerts.com/api/v1'
|
||||
|
||||
super().__init__(
|
||||
base_url=self.base_uri,
|
||||
redis_url=redis_url,
|
||||
ratelimit_key='donation_alerts' if redis_url else None,
|
||||
ratelimit_limit=60 if redis_url else None,
|
||||
proxy_url=proxy_url,
|
||||
)
|
||||
|
||||
def _auth(self, access_token: str) -> dict[str, str]:
|
||||
return {'Authorization': f'Bearer {access_token}'}
|
||||
|
||||
async def _process[T: BaseModel](
|
||||
self, req: Response | CachedResponse, schema: type[T]
|
||||
) -> T:
|
||||
if req.status_code >= 500:
|
||||
raise s.InternalError(req.status_code, 'Internal Server Error')
|
||||
|
||||
if req.status_code >= 400 and req.status_code < 500:
|
||||
data = await req.json()
|
||||
message = data.get('message', 'DonationAlerts API Error')
|
||||
|
||||
raise s.ClientError(req.status_code, message)
|
||||
|
||||
data = await req.json()
|
||||
return schema.model_validate(data)
|
||||
|
||||
async def user_oauth(
|
||||
self, access_token: str, *, cache_ttl: int | None = None
|
||||
) -> s.UserOauth:
|
||||
req = await self.get(
|
||||
'/user/oauth', None, self._auth(access_token), cache_ttl
|
||||
)
|
||||
|
||||
return await self._process(req, s.UserOauth)
|
||||
|
||||
async def alerts_donations(
|
||||
self, access_token: str, *, page: int = 1, cache_ttl: int | None = None
|
||||
) -> s.AlertsDonations:
|
||||
req = await self.get(
|
||||
'/alerts/donations',
|
||||
{'page': page},
|
||||
self._auth(access_token),
|
||||
cache_ttl,
|
||||
)
|
||||
|
||||
return await self._process(req, s.AlertsDonations)
|
||||
|
||||
async def custom_alert(
|
||||
self,
|
||||
access_token: str,
|
||||
external_id: str,
|
||||
header: str,
|
||||
message: str,
|
||||
image_url: str,
|
||||
sound_url: str,
|
||||
is_shown: Literal[0, 1] = 0,
|
||||
) -> Never:
|
||||
raise NotImplementedError
|
||||
|
||||
async def merchandise(
|
||||
self,
|
||||
access_token: str,
|
||||
merchant_identifier: str,
|
||||
merchandise_identifier: str,
|
||||
title: dict[str, str],
|
||||
currency: str,
|
||||
price_user: int,
|
||||
price_service: str,
|
||||
url: str,
|
||||
img_url: str,
|
||||
end_at_ts: int,
|
||||
is_active: Literal[0, 1] = 0,
|
||||
is_percentage: Literal[0, 1] = 0,
|
||||
) -> Never:
|
||||
raise NotImplementedError
|
||||
|
||||
async def update_merchandise(
|
||||
self,
|
||||
access_token: str,
|
||||
merchant_identifier: str,
|
||||
merchandise_identifier: str,
|
||||
title: dict[str, str],
|
||||
currency: str,
|
||||
price_user: int,
|
||||
price_service: str,
|
||||
url: str,
|
||||
img_url: str,
|
||||
end_at_ts: int,
|
||||
is_active: Literal[0, 1] = 0,
|
||||
is_percentage: Literal[0, 1] = 0,
|
||||
) -> Never:
|
||||
raise NotImplementedError
|
||||
|
||||
async def get_user_data_from_promocode(
|
||||
self,
|
||||
access_token: str,
|
||||
promocode: str,
|
||||
*,
|
||||
cache_ttl: int | None = None,
|
||||
) -> Never:
|
||||
raise NotImplementedError
|
||||
|
||||
async def send_sale_alerts(
|
||||
self,
|
||||
access_token: str,
|
||||
user_id: int,
|
||||
external_id: str,
|
||||
merchant_identifier: str,
|
||||
merchandise_identifier: str,
|
||||
amount: float,
|
||||
currency: str,
|
||||
username: str,
|
||||
message: str,
|
||||
bought_amount: int = 1,
|
||||
) -> Never:
|
||||
raise NotImplementedError
|
||||
|
||||
async def centrifuge_subscribe(
|
||||
self, access_token: str, client: str, subscriptions: list[str]
|
||||
) -> s.CentrifugeSubscribe:
|
||||
req = await self.post(
|
||||
'/centrifuge/subscribe',
|
||||
None,
|
||||
self._auth(access_token),
|
||||
{'client': client, 'channels': subscriptions},
|
||||
)
|
||||
|
||||
return await self._process(req, s.CentrifugeSubscribe)
|
||||
78
src/oxidedonationalerts/auth.py
Normal file
78
src/oxidedonationalerts/auth.py
Normal file
@ -0,0 +1,78 @@
|
||||
from oxidehttp.client import OxideHTTP
|
||||
from oxidehttp.schema import CachedResponse, Response
|
||||
from pydantic import BaseModel
|
||||
|
||||
from . import schema as s
|
||||
from . import scopes as sp
|
||||
|
||||
|
||||
class DonationAlertsAuthClient(OxideHTTP):
|
||||
def __init__(
|
||||
self,
|
||||
client_id: str,
|
||||
client_secret: str,
|
||||
redirect_uri: str,
|
||||
redis_url: str | None = None,
|
||||
proxy_url: str | None = None,
|
||||
) -> None:
|
||||
self.base_uri = 'https://www.donationalerts.com/oauth'
|
||||
self.client_id = client_id
|
||||
self.client_secret = client_secret
|
||||
self.redirect_uri = redirect_uri
|
||||
|
||||
super().__init__(
|
||||
base_url=self.base_uri,
|
||||
redis_url=redis_url,
|
||||
ratelimit_key='donation_alerts' if redis_url else None,
|
||||
ratelimit_limit=60 if redis_url else None,
|
||||
proxy_url=proxy_url,
|
||||
)
|
||||
|
||||
def _auth(self, access_token: str) -> dict[str, str]:
|
||||
return {'Authorization': f'Bearer {access_token}'}
|
||||
|
||||
async def _process[T: BaseModel](
|
||||
self, req: Response | CachedResponse, schema: type[T]
|
||||
) -> T:
|
||||
if req.status_code >= 500:
|
||||
raise s.InternalError(req.status_code, 'Internal Server Error')
|
||||
|
||||
if req.status_code >= 400 and req.status_code < 500:
|
||||
data = await req.json()
|
||||
message = data.get('message', 'DonationAlerts API Error')
|
||||
|
||||
raise s.ClientError(req.status_code, message)
|
||||
|
||||
data = await req.json()
|
||||
return schema.model_validate(data)
|
||||
|
||||
async def user_access_token(self, code: str) -> s.UserAccessToken:
|
||||
req = await self.post(
|
||||
'/token',
|
||||
json={
|
||||
'client_id': self.client_id,
|
||||
'client_secret': self.client_secret,
|
||||
'redirect_uri': self.redirect_uri,
|
||||
'grant_type': 'authorization_code',
|
||||
'code': code,
|
||||
},
|
||||
)
|
||||
|
||||
return await self._process(req, s.UserAccessToken)
|
||||
|
||||
async def refresh_access_token(
|
||||
self, refresh_code: str, scopes: list[sp.Any]
|
||||
) -> s.UserAccessToken:
|
||||
req = await self.post(
|
||||
'/token',
|
||||
json={
|
||||
'client_id': self.client_id,
|
||||
'client_secret': self.client_secret,
|
||||
'redirect_uri': self.redirect_uri,
|
||||
'grant_type': 'refresh_token',
|
||||
'refresh_token': refresh_code,
|
||||
'scopes': ' '.join(scopes),
|
||||
},
|
||||
)
|
||||
|
||||
return await self._process(req, s.UserAccessToken)
|
||||
0
src/oxidedonationalerts/py.typed
Normal file
0
src/oxidedonationalerts/py.typed
Normal file
115
src/oxidedonationalerts/schema.py
Normal file
115
src/oxidedonationalerts/schema.py
Normal file
@ -0,0 +1,115 @@
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, EmailStr, Field, HttpUrl
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
status_code: int
|
||||
error: str
|
||||
|
||||
def __init__(self, status_code: int, error: str) -> None:
|
||||
self.status_code = status_code
|
||||
self.error = error
|
||||
super().__init__(f'{status_code}: {error}')
|
||||
|
||||
|
||||
class ClientError(Error):
|
||||
pass
|
||||
|
||||
|
||||
class InternalError(Error):
|
||||
pass
|
||||
|
||||
|
||||
class BaseSchema(BaseModel):
|
||||
model_config = ConfigDict(extra='forbid')
|
||||
|
||||
|
||||
class UserOauthData(BaseSchema):
|
||||
id: int
|
||||
code: str
|
||||
name: str
|
||||
avatar: HttpUrl
|
||||
email: EmailStr
|
||||
socket_connection_token: str
|
||||
|
||||
|
||||
class UserOauth(BaseSchema):
|
||||
data: UserOauthData
|
||||
|
||||
|
||||
class AlertsDonationsDataPayinSystem(BaseSchema):
|
||||
title: str
|
||||
|
||||
|
||||
class AlertsDonationsDataRecipient(BaseSchema):
|
||||
user_id: int
|
||||
code: str
|
||||
name: str
|
||||
avatar: HttpUrl
|
||||
|
||||
|
||||
class AlertsDonationsData(BaseSchema):
|
||||
id: int
|
||||
name: str
|
||||
username: str | None
|
||||
message: str | None
|
||||
message_type: Literal['text', 'audio']
|
||||
payin_system: AlertsDonationsDataPayinSystem | None
|
||||
amount: float
|
||||
currency: str
|
||||
is_shown: bool
|
||||
amount_in_user_currency: float
|
||||
recipient_name: str
|
||||
recipient: AlertsDonationsDataRecipient
|
||||
created_at: datetime
|
||||
created_at_ts: int
|
||||
shown_at: datetime | None
|
||||
shown_at_ts: int | None
|
||||
|
||||
|
||||
class AlertsDonationsLinks(BaseSchema):
|
||||
first: HttpUrl
|
||||
last: HttpUrl
|
||||
prev: HttpUrl | None
|
||||
next: HttpUrl | None
|
||||
|
||||
|
||||
class MetaLink(BaseSchema):
|
||||
url: HttpUrl | None
|
||||
label: str
|
||||
active: bool
|
||||
|
||||
|
||||
class AlertsDonationsMeta(BaseSchema):
|
||||
current_page: int
|
||||
from_page: int | None = Field(alias='from')
|
||||
last_page: int
|
||||
links: list[MetaLink]
|
||||
path: HttpUrl
|
||||
per_page: int
|
||||
to: int | None
|
||||
total: int
|
||||
|
||||
|
||||
class AlertsDonations(BaseSchema):
|
||||
data: list[AlertsDonationsData]
|
||||
links: AlertsDonationsLinks
|
||||
meta: AlertsDonationsMeta
|
||||
|
||||
|
||||
class CentrifugeSubscribeChannel(BaseSchema):
|
||||
channel: str
|
||||
token: str
|
||||
|
||||
|
||||
class CentrifugeSubscribe(BaseSchema):
|
||||
channels: list[CentrifugeSubscribeChannel]
|
||||
|
||||
|
||||
class UserAccessToken(BaseSchema):
|
||||
token_type: Literal['Bearer']
|
||||
access_token: str
|
||||
refresh_token: str
|
||||
expires_in: int
|
||||
18
src/oxidedonationalerts/scopes.py
Normal file
18
src/oxidedonationalerts/scopes.py
Normal file
@ -0,0 +1,18 @@
|
||||
from typing import Literal
|
||||
|
||||
OAUTH_USER_SHOW = 'oauth-user-show'
|
||||
OAUTH_DONATION_SUBSCRIBE = 'oauth-donation-subscribe'
|
||||
OAUTH_DONATION_INDEX = 'oauth-donation-index'
|
||||
OAUTH_CUSTOM_ALERT_STORE = 'oauth-custom_alert-store'
|
||||
OAUTH_GOAL_SUBSCRIBE = 'oauth-goal-subscribe'
|
||||
OAUTH_POLL_SUBSCRIBE = 'oauth-poll-subscribe'
|
||||
|
||||
|
||||
type Any = Literal[
|
||||
'oauth-user-show',
|
||||
'oauth-donation-subscribe',
|
||||
'oauth-donation-index',
|
||||
'oauth-custom_alert-store',
|
||||
'oauth-goal-subscribe',
|
||||
'oauth-poll-subscribe',
|
||||
]
|
||||
Reference in New Issue
Block a user