Compare commits

...

9 Commits

3 changed files with 276 additions and 20 deletions

View File

@ -1,6 +1,6 @@
[project] [project]
name = "twitchclient" name = "twitchclient"
version = "0.1.0" version = "0.2.0"
description = "Client for Twitch API" description = "Client for Twitch API"
readme = "README.md" readme = "README.md"
authors = [ authors = [

View File

@ -29,7 +29,9 @@ class TwitchAPIClient(AioHTTPXClient):
logger='Twitch API', logger='Twitch API',
) )
async def start_commercial(self, access_token: str, broadcaster_id: int): async def start_commercial(
self, access_token: str, broadcaster_id: int | str
):
req = await self.get( req = await self.get(
'/channels/commercial', '/channels/commercial',
headers={'Authorization': f'Bearer {access_token}'}, headers={'Authorization': f'Bearer {access_token}'},
@ -53,7 +55,9 @@ class TwitchAPIClient(AioHTTPXClient):
case _: case _:
raise s.Error(req.status_code, 'Internal Server Error') raise s.Error(req.status_code, 'Internal Server Error')
async def get_ad_schedule(self, access_token: str, broadcaster_id: int): async def get_ad_schedule(
self, access_token: str, broadcaster_id: int | str
):
req = await self.get( req = await self.get(
'/channels/ads', '/channels/ads',
headers={'Authorization': f'Bearer {access_token}'}, headers={'Authorization': f'Bearer {access_token}'},
@ -72,7 +76,9 @@ class TwitchAPIClient(AioHTTPXClient):
case _: case _:
raise s.Error(req.status_code, 'Internal Server Error') raise s.Error(req.status_code, 'Internal Server Error')
async def snooze_next_ad(self, access_token: str, broadcaster_id: int): async def snooze_next_ad(
self, access_token: str, broadcaster_id: int | str
):
req = await self.post( req = await self.post(
'/channels/ads/schedule/snooze', '/channels/ads/schedule/snooze',
headers={'Authorization': f'Bearer {access_token}'}, headers={'Authorization': f'Bearer {access_token}'},
@ -193,7 +199,7 @@ class TwitchAPIClient(AioHTTPXClient):
raise s.Error(req.status_code, 'Internal Server Error') raise s.Error(req.status_code, 'Internal Server Error')
async def get_cheermotes( async def get_cheermotes(
self, access_token: str, broadcaster_id: int | None = None self, access_token: str, broadcaster_id: int | str | None = None
): ):
req = await self.get( req = await self.get(
'/bits/cheermotes', '/bits/cheermotes',
@ -243,7 +249,9 @@ class TwitchAPIClient(AioHTTPXClient):
raise s.Error(req.status_code, 'Internal Server Error') raise s.Error(req.status_code, 'Internal Server Error')
async def get_channel_information( async def get_channel_information(
self, access_token: str, broadcaster_id: int | list[int] self,
access_token: str,
broadcaster_id: int | list[int] | str | list[str],
): ):
req = await self.get( req = await self.get(
'/channels', '/channels',
@ -264,9 +272,9 @@ class TwitchAPIClient(AioHTTPXClient):
async def modify_channel_information( async def modify_channel_information(
self, self,
access_token: str, access_token: str,
broadcaster_id: int, broadcaster_id: int | str,
*, *,
game_id: int | None = None, game_id: int | str | None = None,
broadcaster_language: str | None = None, broadcaster_language: str | None = None,
title: str | None = None, title: str | None = None,
delay: int | None = None, delay: int | None = None,
@ -309,7 +317,7 @@ class TwitchAPIClient(AioHTTPXClient):
raise s.Error(req.status_code, 'Internal Server Error') raise s.Error(req.status_code, 'Internal Server Error')
async def get_channel_editors( async def get_channel_editors(
self, access_token: str, broadcaster_id: int self, access_token: str, broadcaster_id: int | str
): ):
req = await self.get( req = await self.get(
'/channels/editors', '/channels/editors',
@ -330,7 +338,7 @@ class TwitchAPIClient(AioHTTPXClient):
async def get_followed_channels( async def get_followed_channels(
self, self,
access_token: str, access_token: str,
broadcaster_id: int, broadcaster_id: int | str,
*, *,
first: int = 20, first: int = 20,
after: str | None = None, after: str | None = None,
@ -360,7 +368,7 @@ class TwitchAPIClient(AioHTTPXClient):
async def get_channel_followers( async def get_channel_followers(
self, self,
access_token: str, access_token: str,
broadcaster_id: int, broadcaster_id: int | str,
*, *,
user_id: int | None = None, user_id: int | None = None,
first: int = 20, first: int = 20,
@ -392,7 +400,7 @@ class TwitchAPIClient(AioHTTPXClient):
async def create_custom_rewards( async def create_custom_rewards(
self, self,
access_token: str, access_token: str,
broadcaster_id: int, broadcaster_id: int | str,
title: str, title: str,
cost: int, cost: int,
*, *,
@ -451,7 +459,7 @@ class TwitchAPIClient(AioHTTPXClient):
raise s.Error(req.status_code, 'Internal Server Error') raise s.Error(req.status_code, 'Internal Server Error')
async def delete_custom_reward( async def delete_custom_reward(
self, access_token: str, broadcaster_id: str, reward_id: str self, access_token: str, broadcaster_id: int | str, reward_id: str
): ):
req = await self.delete( req = await self.delete(
'/channel_points/custom_rewards', '/channel_points/custom_rewards',
@ -478,7 +486,7 @@ class TwitchAPIClient(AioHTTPXClient):
async def get_custom_rewards( async def get_custom_rewards(
self, self,
access_token: str, access_token: str,
broadcaster_id: int, broadcaster_id: int | str,
*, *,
reward_id: str | None = None, reward_id: str | None = None,
only_manageable_rewards: bool = False, only_manageable_rewards: bool = False,
@ -514,7 +522,7 @@ class TwitchAPIClient(AioHTTPXClient):
async def get_custom_reward_redemption( async def get_custom_reward_redemption(
self, self,
access_token: str, access_token: str,
broadcaster_id: int, broadcaster_id: int | str,
reward_id: str, reward_id: str,
status: Literal['CANCELED', 'FULFILLED', 'UNFULFILLED'], status: Literal['CANCELED', 'FULFILLED', 'UNFULFILLED'],
*, *,
@ -559,7 +567,7 @@ class TwitchAPIClient(AioHTTPXClient):
async def update_custom_reward( async def update_custom_reward(
self, self,
access_token: str, access_token: str,
broadcaster_id: int, broadcaster_id: int | str,
reward_id: str, reward_id: str,
*, *,
title: str | None = None, title: str | None = None,
@ -623,7 +631,7 @@ class TwitchAPIClient(AioHTTPXClient):
async def update_redemption_status( async def update_redemption_status(
self, self,
access_token: str, access_token: str,
broadcaster_id: int, broadcaster_id: int | str,
reward_id: int, reward_id: int,
redemption_id: int | list[int], redemption_id: int | list[int],
status: Literal['CANCELED', 'FULFILLED'], status: Literal['CANCELED', 'FULFILLED'],
@ -656,7 +664,7 @@ class TwitchAPIClient(AioHTTPXClient):
raise s.Error(req.status_code, 'Internal Server Error') raise s.Error(req.status_code, 'Internal Server Error')
async def get_charity_campaign( async def get_charity_campaign(
self, access_token: str, broadcaster_id: int self, access_token: str, broadcaster_id: int | str
): ):
req = await self.get( req = await self.get(
'/charity/campaigns', '/charity/campaigns',
@ -677,7 +685,7 @@ class TwitchAPIClient(AioHTTPXClient):
async def get_charity_campaign_donations( async def get_charity_campaign_donations(
self, self,
access_token: str, access_token: str,
broadcaster_id: int, broadcaster_id: int | str,
first: int = 20, first: int = 20,
after: str | None = None, after: str | None = None,
cache_time: int | None = None, cache_time: int | None = None,
@ -712,7 +720,7 @@ class TwitchAPIClient(AioHTTPXClient):
async def get_chatters( async def get_chatters(
self, self,
access_token: str, access_token: str,
broadcaster_id: int, broadcaster_id: int | str,
moderator_id: int, moderator_id: int,
first: int = 20, first: int = 20,
after: str | None = None, after: str | None = None,
@ -745,3 +753,163 @@ class TwitchAPIClient(AioHTTPXClient):
case _: case _:
raise s.Error(req.status_code, 'Internal Server Error') raise s.Error(req.status_code, 'Internal Server Error')
async def get_channel_emotes(
self,
access_token: str,
broadcaster_id: int | str,
cache_time: int | None = None,
):
req = await self.get(
'/chat/emotes',
headers=self.clean_dict(
{
'Authorization': f'Bearer {access_token}',
'X-Cache-TTL': cache_time,
}
),
params={'broadcaster_id': broadcaster_id},
)
match req.status_code:
case st.OK:
return s.ChannelEmotes.model_validate(req.json())
case st.BAD_REQUEST | st.UNAUTHORIZED:
raise s.Error(req.status_code, req.json()['message'])
case _:
raise s.Error(req.status_code, 'Internal Server Error')
async def get_global_emotes(
self, access_token: str, cache_time: int | None = None
):
req = await self.get(
'/chat/emotes/global',
headers=self.clean_dict(
{
'Authorization': f'Bearer {access_token}',
'X-Cache-TTL': cache_time,
}
),
)
match req.status_code:
case st.OK:
return s.GlobalEmotes.model_validate(req.json())
case st.UNAUTHORIZED:
raise s.Error(req.status_code, req.json()['message'])
case _:
raise s.Error(req.status_code, 'Internal Server Error')
async def get_emote_sets(
self,
access_token: str,
emote_set_id: int,
cache_time: int | None = None,
):
req = await self.get(
'/chat/emotes/set',
headers=self.clean_dict(
{
'Authorization': f'Bearer {access_token}',
'X-Cache-TTL': cache_time,
}
),
params={'emote_set_id': emote_set_id},
)
match req.status_code:
case st.OK:
return s.EmoteSets.model_validate(req.json())
case st.BAD_REQUEST | st.UNAUTHORIZED:
raise s.Error(req.status_code, req.json()['message'])
case _:
raise s.Error(req.status_code, 'Internal Server Error')
async def get_channel_chat_badges(
self,
access_token: str,
broadcaster_id: int | str,
cache_time: int | None = None,
):
req = await self.get(
'/chat/badges',
headers=self.clean_dict(
{
'Authorization': f'Bearer {access_token}',
'X-Cache-TTL': cache_time,
}
),
params={'broadcaster_id': broadcaster_id},
)
match req.status_code:
case st.OK:
return s.ChannelChatBadges.model_validate(req.json())
case st.BAD_REQUEST | st.UNAUTHORIZED:
raise s.Error(req.status_code, req.json()['message'])
case _:
raise s.Error(req.status_code, 'Internal Server Error')
async def get_global_chat_badges(
self, access_token: str, cache_time: int | None = None
):
req = await self.get(
'/chat/badges/global',
headers=self.clean_dict(
{
'Authorization': f'Bearer {access_token}',
'X-Cache-TTL': cache_time,
}
),
)
match req.status_code:
case st.OK:
return s.GlobalChatBadges.model_validate(req.json())
case st.UNAUTHORIZED:
raise s.Error(req.status_code, req.json()['message'])
case _:
raise s.Error(req.status_code, 'Internal Server Error')
async def get_chat_settings(
self,
access_token: str,
broadcaster_id: int | str,
moderator_id: int | str | None = None,
cache_time: int | None = None,
):
req = await self.get(
'/chat/settings',
headers=self.clean_dict(
{
'Authorization': f'Bearer {access_token}',
'X-Cache-TTL': cache_time,
}
),
params=self.clean_dict(
{
'broadcaster_id': broadcaster_id,
'moderator_id': moderator_id,
}
),
)
match req.status_code:
case st.OK:
return s.ChatSettings.model_validate(req.json()).data
case st.BAD_REQUEST | st.UNAUTHORIZED:
raise s.Error(req.status_code, req.json()['message'])
case _:
raise s.Error(req.status_code, 'Internal Server Error')

View File

@ -475,3 +475,91 @@ class Chatters(BaseModel):
data: list[ChattersData] data: list[ChattersData]
pagination: Pagination | dict[Any, Any] | None = None pagination: Pagination | dict[Any, Any] | None = None
total: int total: int
class ChannelEmoteImages(BaseModel):
model_config = ConfigDict(extra='forbid')
url_1x: str
url_2x: str
url_4x: str
class ChannelEmote(BaseModel):
model_config = ConfigDict(extra='forbid')
id: int
name: str
images: ChannelEmoteImages
tier: int
emote_type: Literal['bitstier', 'follower', 'subscriptions']
format: list[Literal['animated', 'static']]
scale: list[Literal['1.0', '2.0', '3.0']]
theme_mode: list[Literal['dark', 'light']]
class ChannelEmotes(BaseModel):
model_config = ConfigDict(extra='forbid')
data: list[ChannelEmote]
template: str
class GlobalEmotes(ChannelEmotes):
pass
class EmoteSets(ChannelEmotes):
pass
class ChannelChatBadgesVersion(BaseModel):
model_config = ConfigDict(extra='forbid')
id: str
image_url_1x: str
image_url_2x: str
image_url_4x: str
title: str
description: str
click_action: str | None
click_url: str | None
class ChannelChatBadgesData(BaseModel):
model_config = ConfigDict(extra='forbid')
set_id: str
versions: list[ChannelChatBadgesVersion]
class ChannelChatBadges(BaseModel):
model_config = ConfigDict(extra='forbid')
data: list[ChannelChatBadgesData]
class GlobalChatBadges(ChannelChatBadges):
pass
class ChatSettingsData(BaseModel):
model_config = ConfigDict(extra='forbid')
broadcaster_id: int
emote_mode: bool
follower_mode: bool
follower_mode_duration: int | None
moderator_id: int | None = None
non_moderator_chat_delay: bool | None = None
non_moderator_chat_delay_duration: int | None = None
slow_mode: bool
slow_mode_wait_time: int | None
subscriber_mode: bool
unique_chat_mode: bool
class ChatSettings(BaseModel):
model_config = ConfigDict(extra='forbid')
data: list[ChatSettingsData]