From 8b81c68de3876de12cf814aa9ce459de5b1d5bb5 Mon Sep 17 00:00:00 2001 From: Miwory Date: Thu, 26 Feb 2026 13:15:29 +0300 Subject: [PATCH 1/3] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=20=D0=BA=D0=B5=D1=88=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B4=D0=BB=D1=8F=20GET=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D1=81=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 5 +- src/oxidetwitch/api.py | 596 +++++++++++++++-------------------------- 2 files changed, 213 insertions(+), 388 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a530cc0..3726fb0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "oxidetwitch" -version = "1.1.0" +version = "1.2.0" description = "Client for Twitch API" readme = "README.md" authors = [{ name = "Miwory", email = "miwory.uwu@gmail.com" }] @@ -99,6 +99,3 @@ ignore-one-line-docstrings = true quote-style = "single" indent-style = "space" docstring-code-format = true - -[tool.uv.sources] -aiohttpx = { index = "Miwory" } diff --git a/src/oxidetwitch/api.py b/src/oxidetwitch/api.py index d8c0966..2448d53 100644 --- a/src/oxidetwitch/api.py +++ b/src/oxidetwitch/api.py @@ -65,10 +65,15 @@ class TwitchAPIClient(OxideHTTP): raise s.InternalError(req.status_code, 'Internal Server Error') async def get_ad_schedule( - self, access_token: str, broadcaster_id: int + self, + access_token: str, + broadcaster_id: int, + *, + cache_ttl: int | None = None, ) -> s.AdSchedule: req = await self.get( '/channels/ads', + cache_ttl=cache_ttl, headers={'Authorization': f'Bearer {access_token}'}, params={ 'broadcaster_id': broadcaster_id, @@ -119,12 +124,14 @@ class TwitchAPIClient(OxideHTTP): started_at: datetime | None = None, ended_at: datetime | None = None, first: int = 20, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.ExtensionAnalyticsData]: after = None while True: req = await self.get( '/analytics/extensions', + cache_ttl=cache_ttl, headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { @@ -171,12 +178,14 @@ class TwitchAPIClient(OxideHTTP): started_at: datetime | None = None, ended_at: datetime | None = None, first: int = 20, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.GameAnalyticsData]: after = None while True: req = await self.get( '/analytics/games', + cache_ttl=cache_ttl, headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { @@ -222,9 +231,11 @@ class TwitchAPIClient(OxideHTTP): *, started_at: datetime | None = None, user_id: int | None = None, + cache_ttl: int | None = None, ) -> s.BitsLeaderboard: req = await self.get( '/bits/leaderboard', + cache_ttl=cache_ttl, headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { @@ -249,10 +260,15 @@ class TwitchAPIClient(OxideHTTP): raise s.InternalError(req.status_code, 'Internal Server Error') async def get_cheermotes( - self, access_token: str, *, broadcaster_id: int | None = None + self, + access_token: str, + *, + broadcaster_id: int | None = None, + cache_ttl: int | None = None, ) -> s.Cheermotes: req = await self.get( '/bits/cheermotes', + cache_ttl=cache_ttl, headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict({'broadcaster_id': broadcaster_id}), ) @@ -276,12 +292,14 @@ class TwitchAPIClient(OxideHTTP): *, user_id: int | list[int] | None = None, first: int = 20, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.ExtensionTransactionsData]: after = None while True: req = await self.get( '/extensions/transactions', + cache_ttl=cache_ttl, headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { @@ -321,9 +339,12 @@ class TwitchAPIClient(OxideHTTP): self, access_token: str, broadcaster_id: int | list[int], + *, + cache_ttl: int | None = None, ) -> s.ChannelsInformation: req = await self.get( '/channels', + cache_ttl=cache_ttl, headers={'Authorization': f'Bearer {access_token}'}, params={'broadcaster_id': broadcaster_id}, ) @@ -390,10 +411,15 @@ class TwitchAPIClient(OxideHTTP): raise s.InternalError(req.status_code, 'Internal Server Error') async def get_channel_editors( - self, access_token: str, broadcaster_id: int + self, + access_token: str, + broadcaster_id: int, + *, + cache_ttl: int | None = None, ) -> s.ChannelEditors: req = await self.get( '/channels/editors', + cache_ttl=cache_ttl, headers={'Authorization': f'Bearer {access_token}'}, params={'broadcaster_id': broadcaster_id}, ) @@ -416,12 +442,14 @@ class TwitchAPIClient(OxideHTTP): broadcaster_id: int, *, first: int = 20, + cache_ttl: int | None = None, ) -> AsyncGenerator[tuple[int, s.FollowedChannel]]: after = None while True: req = await self.get( '/channels/followed', + cache_ttl=cache_ttl, headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { @@ -463,12 +491,14 @@ class TwitchAPIClient(OxideHTTP): *, user_id: int | None = None, first: int = 20, + cache_ttl: int | None = None, ) -> AsyncGenerator[tuple[int, s.ChannelFollower]]: after = None while True: req = await self.get( '/channels/followers', + cache_ttl=cache_ttl, headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { @@ -601,9 +631,11 @@ class TwitchAPIClient(OxideHTTP): *, reward_id: str | None = None, only_manageable_rewards: bool = False, + cache_ttl: int | None = None, ) -> s.CustomRewards: req = await self.get( '/channel_points/custom_rewards', + cache_ttl=cache_ttl, headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { @@ -642,12 +674,14 @@ class TwitchAPIClient(OxideHTTP): redemption_id: int | list[int] | None = None, sort: Literal['OLDEST', 'NEWEST'] = 'OLDEST', first: int = 20, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.CustomRewardRedemption]: after = None while True: req = await self.get( '/channel_points/custom_rewards/redemptions', + cache_ttl=cache_ttl, headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { @@ -797,10 +831,15 @@ class TwitchAPIClient(OxideHTTP): raise s.InternalError(req.status_code, 'Internal Server Error') async def get_charity_campaign( - self, access_token: str, broadcaster_id: int + self, + access_token: str, + broadcaster_id: int, + *, + cache_ttl: int | None = None, ) -> s.CharityCampaign: req = await self.get( '/charity/campaigns', + cache_ttl=cache_ttl, params={'broadcaster_id': broadcaster_id}, headers={'Authorization': f'Bearer {access_token}'}, ) @@ -823,13 +862,15 @@ class TwitchAPIClient(OxideHTTP): broadcaster_id: int, *, first: int = 20, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.CharityDonation]: after = None while True: req = await self.get( '/charity/donations', + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -837,12 +878,6 @@ class TwitchAPIClient(OxideHTTP): 'after': after, } ), - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), ) match req.status_code: @@ -876,13 +911,15 @@ class TwitchAPIClient(OxideHTTP): moderator_id: int, *, first: int = 20, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[tuple[int, s.ChattersData]]: after = None while True: req = await self.get( '/chat/chatters', + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -891,12 +928,6 @@ class TwitchAPIClient(OxideHTTP): 'after': after, } ), - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), ) match req.status_code: @@ -928,16 +959,12 @@ class TwitchAPIClient(OxideHTTP): access_token: str, broadcaster_id: int, *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.ChannelEmotes: req = await self.get( '/chat/emotes', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params={'broadcaster_id': broadcaster_id}, ) @@ -954,16 +981,12 @@ class TwitchAPIClient(OxideHTTP): raise s.InternalError(req.status_code, 'Internal Server Error') async def get_global_emotes( - self, access_token: str, *, cache_time: int | None = None + self, access_token: str, *, cache_ttl: int | None = None ) -> s.GlobalEmotes: req = await self.get( '/chat/emotes/global', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, ) match req.status_code: @@ -983,16 +1006,12 @@ class TwitchAPIClient(OxideHTTP): access_token: str, emote_set_id: int, *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.EmoteSets: req = await self.get( '/chat/emotes/set', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params={'emote_set_id': emote_set_id}, ) @@ -1013,16 +1032,12 @@ class TwitchAPIClient(OxideHTTP): access_token: str, broadcaster_id: int, *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.ChannelChatBadges: req = await self.get( '/chat/badges', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params={'broadcaster_id': broadcaster_id}, ) @@ -1039,16 +1054,12 @@ class TwitchAPIClient(OxideHTTP): raise s.InternalError(req.status_code, 'Internal Server Error') async def get_global_chat_badges( - self, access_token: str, *, cache_time: int | None = None + self, access_token: str, *, cache_ttl: int | None = None ) -> s.GlobalChatBadges: req = await self.get( '/chat/badges/global', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, ) match req.status_code: @@ -1069,16 +1080,12 @@ class TwitchAPIClient(OxideHTTP): broadcaster_id: int, *, moderator_id: int | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.ChatSettings: req = await self.get( '/chat/settings', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -1104,16 +1111,12 @@ class TwitchAPIClient(OxideHTTP): access_token: str, broadcaster_id: int, *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.SharedChatSession: req = await self.get( '/shared_chat/session', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params={'broadcaster_id': broadcaster_id}, ) @@ -1135,19 +1138,15 @@ class TwitchAPIClient(OxideHTTP): user_id: int, *, broadcaster_id: int | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[tuple[str, s.ChannelEmote]]: after = None while True: req = await self.get( '/chat/emotes/global', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'user_id': user_id, @@ -1345,16 +1344,12 @@ class TwitchAPIClient(OxideHTTP): access_token: str, user_id: int | list[int], *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.UserChatColor: req = await self.get( '/chat/colors', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params={'user_id': user_id}, ) @@ -1435,19 +1430,15 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, before: str | None = None, is_featured: bool | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.Clip]: after = None while True: req = await self.get( '/clips', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -1494,16 +1485,12 @@ class TwitchAPIClient(OxideHTTP): broadcaster_id: int, clip_id: str, *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.ClipsDownloads: req = await self.get( '/clips/downloads', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'editor_id': editor_id, @@ -1531,16 +1518,12 @@ class TwitchAPIClient(OxideHTTP): raise s.InternalError(req.status_code, 'Internal Server Error') async def get_conduits( - self, access_token: str, *, cache_time: int | None = None + self, access_token: str, *, cache_ttl: int | None = None ) -> s.Conduits: req = await self.get( '/eventsub/conduits', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, ) match req.status_code: @@ -1622,19 +1605,15 @@ class TwitchAPIClient(OxideHTTP): conduit_id: str, *, status: str | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.ConduitShard]: after = None while True: req = await self.get( '/eventsub/conduits/shards', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( {'id': conduit_id, 'status': status, 'after': after} ), @@ -1722,9 +1701,12 @@ class TwitchAPIClient(OxideHTTP): 'zh-CN', 'zh-TW', ] = 'en-US', + *, + cache_ttl: int | None = None, ) -> s.ContentClassificationLabels: req = await self.get( '/content_classification_labels', + cache_ttl=cache_ttl, headers={'Authorization': f'Bearer {access_token}'}, params={'locale': locale}, ) @@ -1752,19 +1734,15 @@ class TwitchAPIClient(OxideHTTP): game_id: int | None = None, fulfillment_status: Literal['CLAIMED', 'FULFILLED'] | None = None, first: int = 20, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.DropEntitlement]: after = None while True: req = await self.get( '/entitlements/drops', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'drop_id': drop_id, @@ -1845,16 +1823,12 @@ class TwitchAPIClient(OxideHTTP): broadcaster_id: int | None = None, extension_id: int | None = None, segment: Literal['broadcaster', 'developer', 'global'] | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.ExtensionConfigurationSegment: req = await self.get( '/extensions/configurations', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {jwt_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {jwt_token}'}, params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -1985,19 +1959,15 @@ class TwitchAPIClient(OxideHTTP): extension_id: str, *, first: int | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.ExtensionLiveChannel]: after = None while True: req = await self.get( '/extensions/live', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'extension_id': extension_id, @@ -2036,16 +2006,12 @@ class TwitchAPIClient(OxideHTTP): jwt_token: str, extension_id: str, *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.ExtensionSecrets: req = await self.get( '/extensions/jwt/secrets', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {jwt_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {jwt_token}'}, params={'extension_id': extension_id}, ) @@ -2119,16 +2085,12 @@ class TwitchAPIClient(OxideHTTP): extension_id: str, *, extension_version: str | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.Extensions: req = await self.get( '/extensions', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {jwt_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {jwt_token}'}, params=self.clean_dict( { 'extension_id': extension_id, @@ -2155,16 +2117,12 @@ class TwitchAPIClient(OxideHTTP): extension_id: str, *, extension_version: str | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.Extensions: req = await self.get( '/extensions/released', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'extension_id': extension_id, @@ -2190,16 +2148,12 @@ class TwitchAPIClient(OxideHTTP): access_token: str, *, should_include_all: bool | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.ExtensionBitsProducts: req = await self.get( '/bits/extensions', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'should_include_all': should_include_all, @@ -2341,19 +2295,15 @@ class TwitchAPIClient(OxideHTTP): sub_type: sub_type.Any | None = None, user_id: int | None = None, subscription_id: str | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[tuple[int, sub.Any]]: after = None while True: req = await self.get( '/eventsub/subscriptions', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'status': status, @@ -2397,19 +2347,15 @@ class TwitchAPIClient(OxideHTTP): *, first: int = 20, before: str | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.Game]: after = None while True: req = await self.get( '/games/top', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'first': first, @@ -2450,16 +2396,12 @@ class TwitchAPIClient(OxideHTTP): name: str, *, igdb_id: str | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.Games: req = await self.get( '/games', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'id': game_id, @@ -2486,16 +2428,12 @@ class TwitchAPIClient(OxideHTTP): access_token: str, broadcaster_id: int, *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.CreatorGoals: req = await self.get( '/creator_goals', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params={'broadcaster_id': broadcaster_id}, ) @@ -2518,16 +2456,12 @@ class TwitchAPIClient(OxideHTTP): access_token: str, broadcaster_id: int, *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.HypeTrainStatus: req = await self.get( '/hypetrain/status', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params={'broadcaster_id': broadcaster_id}, ) @@ -2548,16 +2482,12 @@ class TwitchAPIClient(OxideHTTP): access_token: str, broadcaster_id: int, *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.AutomodStatus: req = await self.get( '/moderation/enforcements/status', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params={'broadcaster_id': broadcaster_id}, ) @@ -2611,16 +2541,12 @@ class TwitchAPIClient(OxideHTTP): broadcaster_id: int, moderator_id: int, *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.AutomodSettings: req = await self.get( '/moderation/automod/settings', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params={ 'broadcaster_id': broadcaster_id, 'moderator_id': moderator_id, @@ -2694,13 +2620,14 @@ class TwitchAPIClient(OxideHTTP): user_id: int | list[int], *, first: int = 20, - before: str | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.BannedUser]: after = None while True: req = await self.get( '/moderation/banned', + cache_ttl=cache_ttl, headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { @@ -2708,7 +2635,6 @@ class TwitchAPIClient(OxideHTTP): 'user_id': user_id, 'first': first, 'after': after, - 'before': before, } ), ) @@ -2827,12 +2753,14 @@ class TwitchAPIClient(OxideHTTP): *, user_id: int | None = None, first: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.UnbanRequest]: after = None while True: req = await self.get( '/moderation/unbans', + cache_ttl=cache_ttl, headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { @@ -2913,19 +2841,15 @@ class TwitchAPIClient(OxideHTTP): moderator_id: int, *, first: int | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.BlockedTerm]: after = None while True: req = await self.get( '/moderation/blocked_terms', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -3058,19 +2982,15 @@ class TwitchAPIClient(OxideHTTP): user_id: int, *, first: int | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.ModeratedChannel]: after = None while True: req = await self.get( '/moderation/channels', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'user_id': user_id, @@ -3113,19 +3033,15 @@ class TwitchAPIClient(OxideHTTP): *, user_id: int | list[int] | None = None, first: int = 20, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.Moderator]: after = None while True: req = await self.get( '/moderation/moderators', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -3214,19 +3130,15 @@ class TwitchAPIClient(OxideHTTP): broadcaster_id: int, *, first: int = 20, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.VIP]: after = None while True: req = await self.get( '/channels/vips', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'user_id': user_id, @@ -3354,16 +3266,12 @@ class TwitchAPIClient(OxideHTTP): broadcaster_id: int, moderator_id: int, *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.ShieldModeStatus: req = await self.get( '/moderation/shield_mode', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params={ 'broadcaster_id': broadcaster_id, 'moderator_id': moderator_id, @@ -3431,19 +3339,15 @@ class TwitchAPIClient(OxideHTTP): *, poll_id: int | list[int] | None = None, first: int = 20, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.Poll]: after = None while True: req = await self.get( '/polls', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -3555,19 +3459,15 @@ class TwitchAPIClient(OxideHTTP): *, prediction_id: int | list[int] | None = None, first: int = 20, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.Prediction]: after = None while True: req = await self.get( '/predictions', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -3737,19 +3637,15 @@ class TwitchAPIClient(OxideHTTP): segment_id: int | list[int] | None = None, start_time: datetime | None = None, first: int = 20, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.Schedule]: after = None while True: req = await self.get( '/schedule', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -3795,16 +3691,12 @@ class TwitchAPIClient(OxideHTTP): access_token: str, broadcaster_id: int, *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> bytes: req = await self.get( '/schedule/icalendar', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params={'broadcaster_id': broadcaster_id}, ) @@ -3975,19 +3867,15 @@ class TwitchAPIClient(OxideHTTP): query: str, *, first: int = 20, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.Category]: after = None while True: req = await self.get( '/search/categories', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( {'query': query, 'first': first, 'after': after} ), @@ -4024,19 +3912,15 @@ class TwitchAPIClient(OxideHTTP): *, live_only: bool = False, first: int = 20, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.Channel]: after = None while True: req = await self.get( '/search/channels', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'query': query, @@ -4076,16 +3960,12 @@ class TwitchAPIClient(OxideHTTP): access_token: str, broadcaster_id: int, *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.StreamKeys: req = await self.get( '/streams/key', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params={'broadcaster_id': broadcaster_id}, ) @@ -4112,19 +3992,15 @@ class TwitchAPIClient(OxideHTTP): language: str | None = None, first: int = 20, before: str | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.Stream]: after = None while True: req = await self.get( '/streams', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'user_id': user_id, @@ -4169,19 +4045,15 @@ class TwitchAPIClient(OxideHTTP): user_id: int, *, first: int = 20, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.Stream]: after = None while True: req = await self.get( '/streams/followed', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'user_id': user_id, @@ -4257,19 +4129,15 @@ class TwitchAPIClient(OxideHTTP): *, first: int = 20, before: str | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.StreamMarkersData]: after = None while True: req = await self.get( '/streams/markers', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'user_id': user_id, @@ -4317,19 +4185,15 @@ class TwitchAPIClient(OxideHTTP): *, first: int = 20, before: str | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[tuple[int, int, s.BroadcasterSubscription]]: after = None while True: req = await self.get( '/subscriptions', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -4376,16 +4240,12 @@ class TwitchAPIClient(OxideHTTP): broadcaster_id: int, user_id: int, *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.UserSubscription: req = await self.get( '/subscriptions/user', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -4411,16 +4271,12 @@ class TwitchAPIClient(OxideHTTP): access_token: str, broadcaster_id: int, *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.ChannelTeams: req = await self.get( '/teams/channel', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params={'broadcaster_id': broadcaster_id}, ) @@ -4442,16 +4298,12 @@ class TwitchAPIClient(OxideHTTP): *, name: str | None = None, team_id: int | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.Teams: req = await self.get( '/teams', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict({'name': name, 'id': team_id}), ) @@ -4473,16 +4325,12 @@ class TwitchAPIClient(OxideHTTP): *, user_id: int | list[int] | None = None, login: str | list[str] | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.Users: req = await self.get( '/users', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict({'id': user_id, 'login': login}), ) @@ -4528,16 +4376,12 @@ class TwitchAPIClient(OxideHTTP): access_token: str, user_id: int | list[int], *, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.AuthorizationsByUser: req = await self.get( '/authorization/users', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params={'user_id': user_id}, ) @@ -4564,19 +4408,15 @@ class TwitchAPIClient(OxideHTTP): broadcaster_id: int, *, first: int = 20, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.UserBlock]: after = None while True: req = await self.get( '/users/blocks', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -4672,16 +4512,12 @@ class TwitchAPIClient(OxideHTTP): raise s.InternalError(req.status_code, 'Internal Server Error') async def get_user_extensions( - self, access_token: str, *, cache_time: int | None = None + self, access_token: str, *, cache_ttl: int | None = None ) -> s.UserExtensions: req = await self.get( '/users/extensions/list', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, ) match req.status_code: @@ -4701,16 +4537,12 @@ class TwitchAPIClient(OxideHTTP): access_token: str, *, user_id: int | None = None, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> s.UserActiveExtensions: req = await self.get( '/users/extensions', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params={'user_id': user_id}, ) @@ -4761,19 +4593,15 @@ class TwitchAPIClient(OxideHTTP): sort: Literal['time', 'trending', 'views'] = 'time', video_type: Literal['all', 'archive', 'highlight', 'upload'] = 'all', first: int = 20, - cache_time: int | None = None, + cache_ttl: int | None = None, ) -> AsyncGenerator[s.Video]: after = None while True: req = await self.get( '/videos', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - 'X-Cache-TTL': str(cache_time), - } - ), + cache_ttl=cache_ttl, + headers={'Authorization': f'Bearer {access_token}'}, params=self.clean_dict( { 'video_id': video_id, -- 2.47.2 From 8cc2b53376cbe7f67b44c35b01be9b7ef88fdcb8 Mon Sep 17 00:00:00 2001 From: Miwory Date: Thu, 26 Feb 2026 13:24:16 +0300 Subject: [PATCH 2/3] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D1=85=D0=B5=D0=BB=D0=BF=D0=B5=D1=80=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=B0=D0=B2=D1=82=D0=BE=D1=80=D0=B8=D0=B7=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/oxidetwitch/api.py | 281 +++++++++++++++++++---------------------- 1 file changed, 130 insertions(+), 151 deletions(-) diff --git a/src/oxidetwitch/api.py b/src/oxidetwitch/api.py index 2448d53..4272c4f 100644 --- a/src/oxidetwitch/api.py +++ b/src/oxidetwitch/api.py @@ -35,12 +35,15 @@ class TwitchAPIClient(OxideHTTP): proxy_url=proxy_url, ) + def _auth(self, access_token: str) -> dict[str, str]: + return {'Authorization': f'Bearer {access_token}'} + async def start_commercial( self, access_token: str, broadcaster_id: int, length: int ) -> s.StartCommercial: req = await self.post( '/channels/commercial', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), json={ 'broadcaster_id': broadcaster_id, 'length': length, @@ -74,7 +77,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/channels/ads', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={ 'broadcaster_id': broadcaster_id, }, @@ -97,7 +100,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.SnoozeNextAd: req = await self.post( '/channels/ads/schedule/snooze', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={ 'broadcaster_id': broadcaster_id, }, @@ -132,7 +135,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/analytics/extensions', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'extension_id': extension_id, @@ -186,7 +189,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/analytics/games', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'game_id': game_id, @@ -236,7 +239,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/bits/leaderboard', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'count': count, @@ -269,7 +272,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/bits/cheermotes', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict({'broadcaster_id': broadcaster_id}), ) @@ -300,7 +303,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/extensions/transactions', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'extension_id': extension_id, @@ -345,7 +348,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/channels', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id}, ) @@ -388,7 +391,7 @@ class TwitchAPIClient(OxideHTTP): ) req = await self.patch( '/channels', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id}, json=data, ) @@ -420,7 +423,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/channels/editors', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id}, ) @@ -450,7 +453,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/channels/followed', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -499,7 +502,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/channels/followers', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -576,7 +579,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.post( '/channel_points/custom_rewards', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), json=data, ) @@ -602,7 +605,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.delete( '/channel_points/custom_rewards', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id, 'id': reward_id}, ) @@ -636,7 +639,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/channel_points/custom_rewards', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -682,7 +685,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/channel_points/custom_rewards/redemptions', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -770,7 +773,7 @@ class TwitchAPIClient(OxideHTTP): ) req = await self.patch( '/channel_points/custom_rewards', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id, 'id': reward_id}, json=data, ) @@ -803,7 +806,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.post( '/channel_points/custom_rewards/redemptions', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={ 'broadcaster_id': broadcaster_id, 'reward_id': reward_id, @@ -841,7 +844,7 @@ class TwitchAPIClient(OxideHTTP): '/charity/campaigns', cache_ttl=cache_ttl, params={'broadcaster_id': broadcaster_id}, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), ) match req.status_code: @@ -870,7 +873,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/charity/donations', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -919,7 +922,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/chat/chatters', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -964,7 +967,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/chat/emotes', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id}, ) @@ -986,7 +989,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/chat/emotes/global', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), ) match req.status_code: @@ -1011,7 +1014,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/chat/emotes/set', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'emote_set_id': emote_set_id}, ) @@ -1037,7 +1040,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/chat/badges', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id}, ) @@ -1059,7 +1062,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/chat/badges/global', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), ) match req.status_code: @@ -1085,7 +1088,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/chat/settings', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -1116,7 +1119,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/shared_chat/session', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id}, ) @@ -1146,7 +1149,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/chat/emotes/global', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'user_id': user_id, @@ -1198,7 +1201,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.ChatSettings: req = await self.put( '/chat/settings', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={ 'broadcaster_id': broadcaster_id, 'moderator_id': moderator_id, @@ -1244,7 +1247,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.post( '/chat/announcements', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={ 'broadcaster_id': broadcaster_id, 'moderator_id': moderator_id, @@ -1273,7 +1276,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.post( '/chat/shoutouts', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={ 'from_broadcaster_id': from_broadcaster_id, 'to_broadcaster_id': to_broadcaster_id, @@ -1310,7 +1313,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.Message: req = await self.post( '/chat/messages', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), json=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -1349,7 +1352,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/chat/colors', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'user_id': user_id}, ) @@ -1373,7 +1376,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.put( '/chat/color', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'user_id': user_id, 'color': color}, ) @@ -1398,7 +1401,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.post( '/clips', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( {'broadcaster_id': broadcaster_id, 'has_delay': has_delay} ), @@ -1438,7 +1441,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/clips', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -1490,7 +1493,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/clips/downloads', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'editor_id': editor_id, @@ -1523,7 +1526,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/eventsub/conduits', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), ) match req.status_code: @@ -1543,7 +1546,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.Conduits: req = await self.post( '/eventsub/conduits', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), json={'shard_count': shard_count}, ) @@ -1564,7 +1567,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.Conduits: req = await self.patch( '/eventsub/conduits', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), json={'shard_count': shard_count, 'id': conduit_id}, ) @@ -1583,7 +1586,7 @@ class TwitchAPIClient(OxideHTTP): async def delete_conduit(self, access_token: str, conduit_id: str) -> bool: req = await self.delete( '/eventsub/conduits', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'id': conduit_id}, ) @@ -1613,7 +1616,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/eventsub/conduits/shards', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( {'id': conduit_id, 'status': status, 'after': after} ), @@ -1651,7 +1654,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.ConduitShards: req = await self.patch( '/eventsub/conduits/shards', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), json={'conduit_id': conduit_id, 'shards': shards}, ) @@ -1707,7 +1710,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/content_classification_labels', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'locale': locale}, ) @@ -1742,7 +1745,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/entitlements/drops', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'drop_id': drop_id, @@ -1793,7 +1796,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.UpdateDropsEntitlements: req = await self.patch( '/entitlements/drops', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), json=self.clean_dict( { 'entitlement_ids': entitlement_ids, @@ -1828,7 +1831,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/extensions/configurations', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {jwt_token}'}, + headers=self._auth(jwt_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -1864,7 +1867,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.put( '/extensions/configurations', - headers={'Authorization': f'Bearer {jwt_token}'}, + headers=self._auth(jwt_token), json=self.clean_dict( { 'extension_id': extension_id, @@ -1898,7 +1901,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.put( '/extensions/configurations/required', - headers={'Authorization': f'Bearer {jwt_token}'}, + headers=self._auth(jwt_token), json={ 'broadcaster_id': broadcaster_id, 'extension_id': extension_id, @@ -1930,7 +1933,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.post( '/extensions/pubsub', - headers={'Authorization': f'Bearer {jwt_token}'}, + headers=self._auth(jwt_token), json=self.clean_dict( { 'target': target, @@ -1967,7 +1970,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/extensions/live', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'extension_id': extension_id, @@ -2011,7 +2014,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/extensions/jwt/secrets', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {jwt_token}'}, + headers=self._auth(jwt_token), params={'extension_id': extension_id}, ) @@ -2032,7 +2035,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.ExtensionSecrets: req = await self.post( '/extensions/jwt/secrets', - headers={'Authorization': f'Bearer {jwt_token}'}, + headers=self._auth(jwt_token), params={'extension_id': extension_id, 'delay': delay}, ) @@ -2058,7 +2061,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.post( '/extensions/chat', - headers={'Authorization': f'Bearer {jwt_token}'}, + headers=self._auth(jwt_token), params={'broadcaster_id': broadcaster_id}, json={ 'text': text, @@ -2090,7 +2093,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/extensions', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {jwt_token}'}, + headers=self._auth(jwt_token), params=self.clean_dict( { 'extension_id': extension_id, @@ -2122,7 +2125,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/extensions/released', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'extension_id': extension_id, @@ -2153,7 +2156,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/bits/extensions', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'should_include_all': should_include_all, @@ -2186,7 +2189,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.ExtensionBitsProducts: req = await self.put( '/bits/extensions', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), json=self.clean_dict( { 'sku': sku, @@ -2229,7 +2232,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.EventsubBaseSubscriptions: req = await self.post( '/eventsub/subscriptions', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), json={ 'type': sub_type, 'version': version, @@ -2271,7 +2274,7 @@ class TwitchAPIClient(OxideHTTP): ) -> None: req = await self.delete( '/eventsub/subscriptions', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'id': sub_id}, ) @@ -2303,7 +2306,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/eventsub/subscriptions', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'status': status, @@ -2355,7 +2358,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/games/top', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'first': first, @@ -2401,7 +2404,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/games', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'id': game_id, @@ -2433,7 +2436,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/creator_goals', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id}, ) @@ -2461,7 +2464,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/hypetrain/status', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id}, ) @@ -2487,7 +2490,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/moderation/enforcements/status', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id}, ) @@ -2517,7 +2520,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.post( '/moderation/automod/message', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'action': action, 'msg_id': msg_id, 'user_id': user_id}, ) @@ -2546,7 +2549,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/moderation/automod/settings', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={ 'broadcaster_id': broadcaster_id, 'moderator_id': moderator_id, @@ -2583,7 +2586,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.AutomodSettings: req = await self.put( '/moderation/automod/settings', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), json=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -2628,7 +2631,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/moderation/banned', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -2675,7 +2678,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.BanUser: req = await self.post( '/moderation/banned', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={ 'broadcaster_id': broadcaster_id, 'moderator_id': moderator_id, @@ -2716,7 +2719,7 @@ class TwitchAPIClient(OxideHTTP): ) -> None: req = await self.delete( '/moderation/bans', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={ 'broadcaster_id': broadcaster_id, 'moderator_id': moderator_id, @@ -2761,7 +2764,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/moderation/unbans', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -2810,7 +2813,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.UnbanRequests: req = await self.patch( '/moderation/unban_requests', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -2849,7 +2852,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/moderation/blocked_terms', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -2893,7 +2896,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.BlockedTerms: req = await self.post( '/moderation/blocked_terms', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={ 'broadcaster_id': broadcaster_id, 'moderator_id': moderator_id, @@ -2922,7 +2925,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.delete( '/moderation/blocked_terms', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={ 'broadcaster_id': broadcaster_id, 'moderator_id': moderator_id, @@ -2952,7 +2955,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.delete( '/moderation/chat', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -2990,7 +2993,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/moderation/channels', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'user_id': user_id, @@ -3041,7 +3044,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/moderation/moderators', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -3081,7 +3084,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.post( '/moderation/moderators', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id, 'user_id': user_id}, ) @@ -3107,7 +3110,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.delete( '/moderation/moderators', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id, 'user_id': user_id}, ) @@ -3138,7 +3141,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/channels/vips', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'user_id': user_id, @@ -3178,7 +3181,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.post( '/channels/vips', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id, 'user_id': user_id}, ) @@ -3207,7 +3210,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.delete( '/channels/vips', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id, 'user_id': user_id}, ) @@ -3240,7 +3243,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.ShieldModeStatus: req = await self.put( '/moderation/shield_mode', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={ 'broadcaster_id': broadcaster_id, 'moderator_id': moderator_id, @@ -3271,7 +3274,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/moderation/shield_mode', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={ 'broadcaster_id': broadcaster_id, 'moderator_id': moderator_id, @@ -3300,7 +3303,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.UserWarns: req = await self.post( '/moderation/warnings', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={ 'broadcaster_id': broadcaster_id, 'moderator_id': moderator_id, @@ -3347,7 +3350,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/polls', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -3407,7 +3410,7 @@ class TwitchAPIClient(OxideHTTP): ) req = await self.post( '/polls', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), json=body, ) @@ -3432,7 +3435,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.Polls: req = await self.patch( '/polls', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), json={ 'broadcaster_id': broadcaster_id, 'id': poll_id, @@ -3467,7 +3470,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/predictions', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -3520,7 +3523,7 @@ class TwitchAPIClient(OxideHTTP): ) req = await self.post( '/predictions', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), json=body, ) @@ -3547,7 +3550,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.Predictions: req = await self.patch( '/predictions', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), json=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -3578,7 +3581,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.Raids: req = await self.post( '/raids', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), json={ 'from_broadcaster_id': from_broadcaster_id, 'to_broadcaster_id': to_broadcaster_id, @@ -3608,7 +3611,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.patch( '/raids', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id}, ) @@ -3645,7 +3648,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/schedule', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -3696,7 +3699,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/schedule/icalendar', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id}, ) @@ -3725,7 +3728,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.patch( '/schedule/settings', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -3763,7 +3766,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.Schedules: req = await self.post( '/schedule/segment', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id}, json=self.clean_dict( { @@ -3804,7 +3807,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.Schedules: req = await self.patch( '/schedule/segment', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -3840,7 +3843,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.delete( '/schedule/segment', - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -3875,7 +3878,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/search/categories', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( {'query': query, 'first': first, 'after': after} ), @@ -3920,7 +3923,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/search/channels', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'query': query, @@ -3965,7 +3968,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/streams/key', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id}, ) @@ -4000,7 +4003,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/streams', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'user_id': user_id, @@ -4053,7 +4056,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/streams/followed', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'user_id': user_id, @@ -4096,9 +4099,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.CreateStreamMarkers: req = await self.post( '/streams/markers', - headers={ - 'Authorization': f'Bearer {access_token}', - }, + headers=self._auth(access_token), json=self.clean_dict( { 'user_id': user_id, @@ -4137,7 +4138,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/streams/markers', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'user_id': user_id, @@ -4193,7 +4194,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/subscriptions', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -4245,7 +4246,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/subscriptions/user', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -4276,7 +4277,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/teams/channel', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'broadcaster_id': broadcaster_id}, ) @@ -4303,7 +4304,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/teams', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict({'name': name, 'id': team_id}), ) @@ -4330,7 +4331,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/users', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict({'id': user_id, 'login': login}), ) @@ -4351,11 +4352,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.Users: req = await self.put( '/users', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - } - ), + headers=self._auth(access_token), json={'description': description}, ) @@ -4381,7 +4378,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/authorization/users', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'user_id': user_id}, ) @@ -4416,7 +4413,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/users/blocks', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'broadcaster_id': broadcaster_id, @@ -4460,11 +4457,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.put( '/users/blocks', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - } - ), + headers=self._auth(access_token), json=self.clean_dict( { 'target_user_id': target_user_id, @@ -4491,11 +4484,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.delete( '/users/blocks', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - } - ), + headers=self._auth(access_token), params={'target_user_id': target_user_id}, ) @@ -4517,7 +4506,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/users/extensions/list', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), ) match req.status_code: @@ -4542,7 +4531,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/users/extensions', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params={'user_id': user_id}, ) @@ -4563,9 +4552,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.put( '/users/extensions', - headers={ - 'Authorization': f'Bearer {access_token}', - }, + headers=self._auth(access_token), json=extensions.model_dump(), ) @@ -4601,7 +4588,7 @@ class TwitchAPIClient(OxideHTTP): req = await self.get( '/videos', cache_ttl=cache_ttl, - headers={'Authorization': f'Bearer {access_token}'}, + headers=self._auth(access_token), params=self.clean_dict( { 'video_id': video_id, @@ -4646,11 +4633,7 @@ class TwitchAPIClient(OxideHTTP): ) -> s.DeleteVideos: req = await self.delete( '/videos', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - } - ), + headers=self._auth(access_token), params={'id': video_id}, ) @@ -4675,11 +4658,7 @@ class TwitchAPIClient(OxideHTTP): ) -> bool: req = await self.post( '/whispers', - headers=self.clean_dict( - { - 'Authorization': f'Bearer {access_token}', - } - ), + headers=self._auth(access_token), params={'from_user_id': from_user_id, 'to_user_id': to_user_id}, json={'message': message}, ) -- 2.47.2 From ab914bf58ca17babc8a43ff63d7d91c848924637 Mon Sep 17 00:00:00 2001 From: Miwory Date: Thu, 26 Feb 2026 15:17:40 +0300 Subject: [PATCH 3/3] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D1=85=D0=B5=D0=BB=D0=BF=D0=B5=D1=80=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=BF=D0=B0=D0=B3=D0=B8=D0=BD=D0=B0=D1=86=D0=B8=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/oxidetwitch/api.py | 3095 ++++++++----------------------------- src/oxidetwitch/schema.py | 102 +- 2 files changed, 647 insertions(+), 2550 deletions(-) diff --git a/src/oxidetwitch/api.py b/src/oxidetwitch/api.py index 4272c4f..860ba19 100644 --- a/src/oxidetwitch/api.py +++ b/src/oxidetwitch/api.py @@ -1,10 +1,12 @@ from collections.abc import AsyncGenerator from datetime import datetime -from typing import Literal +from typing import Any, Literal, overload from zoneinfo import ZoneInfo from oxidehttp import status as st from oxidehttp.client import OxideHTTP +from oxidehttp.schema import CachedResponse, Response +from pydantic import BaseModel from . import schema as s from .eventsub import statuses as sub_status @@ -38,6 +40,62 @@ class TwitchAPIClient(OxideHTTP): def _auth(self, access_token: str) -> dict[str, str]: return {'Authorization': f'Bearer {access_token}'} + @overload + async def _process( + self, req: Response | CachedResponse, schema: None + ) -> bool: ... + + @overload + async def _process[T: BaseModel]( + self, req: Response | CachedResponse, schema: type[T] + ) -> T: ... + + async def _process[T: BaseModel]( + self, req: Response | CachedResponse, schema: type[T] | None + ) -> T | bool: + 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', 'Twitch API Error') + + raise s.ClientError(req.status_code, message) + + if schema is None: + return True + + data = await req.json() + return schema.model_validate(data) + + async def _paginate[S: s.BasePaginated]( + self, + path: str, + schema: type[S], + headers: dict[str, str] | None, + params: dict[str, Any], + cache_ttl: int | None = None, + ) -> AsyncGenerator[S]: + after = None + + while True: + current_params = {**params, 'after': after} if after else params + + req = await self.get( + path, + headers=headers, + params=current_params, + cache_ttl=cache_ttl, + ) + result = await self._process(req, schema) + yield result + + if isinstance(result.pagination, s.Pagination): + after = result.pagination.cursor + + if after is None: + break + async def start_commercial( self, access_token: str, broadcaster_id: int, length: int ) -> s.StartCommercial: @@ -50,22 +108,7 @@ class TwitchAPIClient(OxideHTTP): }, ) - match req.status_code: - case st.OK: - return s.StartCommercial.model_validate(await req.json()) - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.NOT_FOUND - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.StartCommercial) async def get_ad_schedule( self, @@ -78,22 +121,10 @@ class TwitchAPIClient(OxideHTTP): '/channels/ads', cache_ttl=cache_ttl, headers=self._auth(access_token), - params={ - 'broadcaster_id': broadcaster_id, - }, + params={'broadcaster_id': broadcaster_id}, ) - match req.status_code: - case st.OK: - return s.AdSchedule.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.AdSchedule) async def snooze_next_ad( self, access_token: str, broadcaster_id: int @@ -106,17 +137,7 @@ class TwitchAPIClient(OxideHTTP): }, ) - match req.status_code: - case st.OK: - return s.SnoozeNextAd.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.TOO_MANY_REQUESTS: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.SnoozeNextAd) async def get_extension_analytics( self, @@ -129,48 +150,23 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.ExtensionAnalyticsData]: - after = None - - while True: - req = await self.get( - '/analytics/extensions', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'extension_id': extension_id, - 'type': analytics_type, - 'started_at': started_at, - 'ended_at': ended_at, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - analytics = s.ExtensionAnalytics.model_validate(json) - - for data in analytics.data: - yield data - - if isinstance(analytics.pagination, s.Pagination): - after = analytics.pagination.cursor - - if not after: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/analytics/extensions', + s.ExtensionAnalytics, + self._auth(access_token), + self.clean_dict( + { + 'extension_id': extension_id, + 'type': analytics_type, + 'started_at': started_at, + 'ended_at': ended_at, + 'first': first, + } + ), + cache_ttl, + ): + for item in data.data: + yield item async def get_game_analytics( self, @@ -183,48 +179,23 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.GameAnalyticsData]: - after = None - - while True: - req = await self.get( - '/analytics/games', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'game_id': game_id, - 'type': analytics_type, - 'started_at': started_at, - 'ended_at': ended_at, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - analytics = s.GameAnalytics.model_validate(json) - - for data in analytics.data: - yield data - - if isinstance(analytics.pagination, s.Pagination): - after = analytics.pagination.cursor - - if not after: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/analytics/games', + s.GameAnalytics, + self._auth(access_token), + self.clean_dict( + { + 'game_id': game_id, + 'type': analytics_type, + 'started_at': started_at, + 'ended_at': ended_at, + 'first': first, + } + ), + cache_ttl, + ): + for item in data.data: + yield item async def get_bits_leaderboard( self, @@ -250,17 +221,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.BitsLeaderboard.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.BitsLeaderboard) async def get_cheermotes( self, @@ -276,17 +237,7 @@ class TwitchAPIClient(OxideHTTP): params=self.clean_dict({'broadcaster_id': broadcaster_id}), ) - match req.status_code: - case st.OK: - return s.Cheermotes.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Cheermotes) async def get_extension_transactions( self, @@ -297,46 +248,21 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.ExtensionTransactionsData]: - after = None - - while True: - req = await self.get( - '/extensions/transactions', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'extension_id': extension_id, - 'user_id': user_id, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - transactions = s.ExtensionTransactions.model_validate(json) - - for data in transactions.data: - yield data - - if isinstance(transactions.pagination, s.Pagination): - after = transactions.pagination.cursor - - if not after: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/extensions/transactions', + s.ExtensionTransactions, + self._auth(access_token), + self.clean_dict( + { + 'extension_id': extension_id, + 'user_id': user_id, + 'first': first, + } + ), + cache_ttl, + ): + for item in data.data: + yield item async def get_channel_information( self, @@ -352,17 +278,7 @@ class TwitchAPIClient(OxideHTTP): params={'broadcaster_id': broadcaster_id}, ) - match req.status_code: - case st.OK: - return s.ChannelsInformation.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.TOO_MANY_REQUESTS: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ChannelsInformation) async def modify_channel_information( self, @@ -396,22 +312,7 @@ class TwitchAPIClient(OxideHTTP): json=data, ) - match req.status_code: - case st.NO_CONTENT: - return True - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def get_channel_editors( self, @@ -427,17 +328,7 @@ class TwitchAPIClient(OxideHTTP): params={'broadcaster_id': broadcaster_id}, ) - match req.status_code: - case st.OK: - return s.ChannelEditors.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.TOO_MANY_REQUESTS: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ChannelEditors) async def get_followed_channels( self, @@ -447,45 +338,20 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[tuple[int, s.FollowedChannel]]: - after = None - - while True: - req = await self.get( - '/channels/followed', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'broadcaster_id': broadcaster_id, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - followed_channels = s.FollowedChannels.model_validate(json) - - for data in followed_channels.data: - yield followed_channels.total, data - - if isinstance(followed_channels.pagination, s.Pagination): - after = followed_channels.pagination.cursor - - if not after: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.TOO_MANY_REQUESTS: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/channels/followed', + s.FollowedChannels, + self._auth(access_token), + self.clean_dict( + { + 'broadcaster_id': broadcaster_id, + 'first': first, + } + ), + cache_ttl, + ): + for item in data.data: + yield data.total, item async def get_channel_followers( self, @@ -496,46 +362,21 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[tuple[int, s.ChannelFollower]]: - after = None - - while True: - req = await self.get( - '/channels/followers', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'broadcaster_id': broadcaster_id, - 'user_id': user_id, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - channel_followers = s.ChannelFollowers.model_validate(json) - - for data in channel_followers.data: - yield channel_followers.total, data - - if isinstance(channel_followers.pagination, s.Pagination): - after = channel_followers.pagination.cursor - - if not after: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.TOO_MANY_REQUESTS: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/channels/followers', + s.ChannelFollowers, + self._auth(access_token), + self.clean_dict( + { + 'broadcaster_id': broadcaster_id, + 'user_id': user_id, + 'first': first, + } + ), + cache_ttl, + ): + for item in data.data: + yield data.total, item async def create_custom_rewards( self, @@ -583,22 +424,7 @@ class TwitchAPIClient(OxideHTTP): json=data, ) - match req.status_code: - case st.OK: - return s.CustomRewards.model_validate(await req.json()) - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.CustomRewards) async def delete_custom_reward( self, access_token: str, broadcaster_id: int, reward_id: str @@ -649,23 +475,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.CustomRewards.model_validate(await req.json()) - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.NOT_FOUND - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.CustomRewards) async def get_custom_reward_redemption( self, @@ -679,56 +489,24 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.CustomRewardRedemption]: - after = None - - while True: - req = await self.get( - '/channel_points/custom_rewards/redemptions', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'broadcaster_id': broadcaster_id, - 'reward_id': reward_id, - 'status': status, - 'redemption_id': redemption_id, - 'sort': sort, - 'after': after, - 'first': first, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - redemptions = s.CustomRewardRedemptions.model_validate( - json - ) - - for data in redemptions.data: - yield data - - if isinstance(redemptions.pagination, s.Pagination): - after = redemptions.pagination.cursor - - if not after: - break - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.NOT_FOUND - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/channel_points/custom_rewards/redemptions', + s.CustomRewardRedemptions, + self._auth(access_token), + self.clean_dict( + { + 'broadcaster_id': broadcaster_id, + 'reward_id': reward_id, + 'status': status, + 'redemption_id': redemption_id, + 'sort': sort, + 'first': first, + } + ), + cache_ttl, + ): + for item in data.data: + yield item async def update_custom_reward( self, @@ -778,23 +556,7 @@ class TwitchAPIClient(OxideHTTP): json=data, ) - match req.status_code: - case st.OK: - return s.CustomRewards.model_validate(await req.json()) - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.NOT_FOUND - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.CustomRewards) async def update_redemption_status( self, @@ -815,23 +577,7 @@ class TwitchAPIClient(OxideHTTP): json={'status': status}, ) - match req.status_code: - case st.NO_CONTENT: - return True - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.NOT_FOUND - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def get_charity_campaign( self, @@ -847,17 +593,7 @@ class TwitchAPIClient(OxideHTTP): headers=self._auth(access_token), ) - match req.status_code: - case st.OK: - return s.CharityCampaign.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.CharityCampaign) async def get_charity_campaign_donations( self, @@ -867,45 +603,17 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.CharityDonation]: - after = None - - while True: - req = await self.get( - '/charity/donations', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'broadcaster_id': broadcaster_id, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - donations = s.CharityDonations.model_validate(json) - - for donation in donations.data: - yield donation - - if isinstance(donations.pagination, s.Pagination): - after = donations.pagination.cursor - - if not after: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/charity/donations', + s.CharityDonations, + self._auth(access_token), + self.clean_dict( + {'broadcaster_id': broadcaster_id, 'first': first} + ), + cache_ttl, + ): + for item in data.data: + yield item async def get_chatters( self, @@ -916,46 +624,21 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[tuple[int, s.ChattersData]]: - after = None - - while True: - req = await self.get( - '/chat/chatters', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'broadcaster_id': broadcaster_id, - 'moderator_id': moderator_id, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - chatters = s.Chatters.model_validate(json) - - for data in chatters.data: - yield chatters.total, data - - if isinstance(chatters.pagination, s.Pagination): - after = chatters.pagination.cursor - - if not after: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/chat/chatters', + s.Chatters, + self._auth(access_token), + self.clean_dict( + { + 'broadcaster_id': broadcaster_id, + 'moderator_id': moderator_id, + 'first': first, + } + ), + cache_ttl, + ): + for item in data.data: + yield data.total, item async def get_channel_emotes( self, @@ -971,17 +654,7 @@ class TwitchAPIClient(OxideHTTP): params={'broadcaster_id': broadcaster_id}, ) - match req.status_code: - case st.OK: - return s.ChannelEmotes.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ChannelEmotes) async def get_global_emotes( self, access_token: str, *, cache_ttl: int | None = None @@ -992,17 +665,7 @@ class TwitchAPIClient(OxideHTTP): headers=self._auth(access_token), ) - match req.status_code: - case st.OK: - return s.GlobalEmotes.model_validate(await req.json()) - - case st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.GlobalEmotes) async def get_emote_sets( self, @@ -1018,17 +681,7 @@ class TwitchAPIClient(OxideHTTP): params={'emote_set_id': emote_set_id}, ) - match req.status_code: - case st.OK: - return s.EmoteSets.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.EmoteSets) async def get_channel_chat_badges( self, @@ -1044,17 +697,7 @@ class TwitchAPIClient(OxideHTTP): params={'broadcaster_id': broadcaster_id}, ) - match req.status_code: - case st.OK: - return s.ChannelChatBadges.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ChannelChatBadges) async def get_global_chat_badges( self, access_token: str, *, cache_ttl: int | None = None @@ -1065,17 +708,7 @@ class TwitchAPIClient(OxideHTTP): headers=self._auth(access_token), ) - match req.status_code: - case st.OK: - return s.GlobalChatBadges.model_validate(await req.json()) - - case st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.GlobalChatBadges) async def get_chat_settings( self, @@ -1097,17 +730,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.ChatSettings.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ChatSettings) async def get_shared_chat_session( self, @@ -1123,17 +746,7 @@ class TwitchAPIClient(OxideHTTP): params={'broadcaster_id': broadcaster_id}, ) - match req.status_code: - case st.OK: - return s.SharedChatSession.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.SharedChatSession) async def get_user_emotes( self, @@ -1143,45 +756,17 @@ class TwitchAPIClient(OxideHTTP): broadcaster_id: int | None = None, cache_ttl: int | None = None, ) -> AsyncGenerator[tuple[str, s.ChannelEmote]]: - after = None - - while True: - req = await self.get( - '/chat/emotes/global', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'user_id': user_id, - 'after': after, - 'broadcaster_id': broadcaster_id, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - user_emotes = s.UserEmotes.model_validate(json) - - for data in user_emotes.data: - yield user_emotes.template, data - - if isinstance(user_emotes.pagination, s.Pagination): - after = user_emotes.pagination.cursor - - if not after: - break - - case st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/chat/emotes', + s.UserEmotes, + self._auth(access_token), + self.clean_dict( + {'user_id': user_id, 'broadcaster_id': broadcaster_id} + ), + cache_ttl, + ): + for item in data.data: + yield data.template, item async def update_chat_settings( self, @@ -1223,17 +808,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.ChatSettings.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ChatSettings) async def send_chat_announcement( self, @@ -1255,17 +830,7 @@ class TwitchAPIClient(OxideHTTP): json=self.clean_dict({'message': message, 'color': color}), ) - match req.status_code: - case st.NO_CONTENT: - return True - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.TOO_MANY_REQUESTS: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def send_shoutout( self, @@ -1284,22 +849,7 @@ class TwitchAPIClient(OxideHTTP): }, ) - match req.status_code: - case st.NO_CONTENT: - return True - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def send_chat_message( self, @@ -1325,22 +875,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.Message.model_validate(await req.json()) - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Message) async def get_user_chat_color( self, @@ -1356,17 +891,7 @@ class TwitchAPIClient(OxideHTTP): params={'user_id': user_id}, ) - match req.status_code: - case st.OK: - return s.UserChatColor.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.UserChatColor) async def update_user_chat_color( self, @@ -1380,17 +905,7 @@ class TwitchAPIClient(OxideHTTP): params={'user_id': user_id, 'color': color}, ) - match req.status_code: - case st.NO_CONTENT: - return True - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def create_clip( self, @@ -1407,19 +922,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.ACCEPTED: - return True - - case ( - st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN | st.NOT_FOUND - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def get_clips( self, @@ -1435,51 +938,26 @@ class TwitchAPIClient(OxideHTTP): is_featured: bool | None = None, cache_ttl: int | None = None, ) -> AsyncGenerator[s.Clip]: - after = None - - while True: - req = await self.get( - '/clips', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'broadcaster_id': broadcaster_id, - 'game_id': game_id, - 'clip_id': clip_id, - 'started_at': started_at, - 'ended_at': ended_at, - 'first': first, - 'before': before, - 'after': after, - 'is_featured': is_featured, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - clips = s.Clips.model_validate(json) - - for clip in clips.data: - yield clip - - if isinstance(clips.pagination, s.Pagination): - after = clips.pagination.cursor - - if after is None: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/clips', + s.Clips, + self._auth(access_token), + self.clean_dict( + { + 'broadcaster_id': broadcaster_id, + 'game_id': game_id, + 'clip_id': clip_id, + 'started_at': started_at, + 'ended_at': ended_at, + 'first': first, + 'before': before, + 'is_featured': is_featured, + } + ), + cache_ttl, + ): + for item in data.data: + yield item async def get_clips_downloads( self, @@ -1503,22 +981,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.ClipsDownloads.model_validate(await req.json()) - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.INTERNAL_SERVER_ERROR - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ClipsDownloads) async def get_conduits( self, access_token: str, *, cache_ttl: int | None = None @@ -1529,17 +992,7 @@ class TwitchAPIClient(OxideHTTP): headers=self._auth(access_token), ) - match req.status_code: - case st.OK: - return s.Conduits.model_validate(await req.json()) - - case st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Conduits) async def create_conduits( self, access_token: str, shard_count: int @@ -1550,17 +1003,7 @@ class TwitchAPIClient(OxideHTTP): json={'shard_count': shard_count}, ) - match req.status_code: - case st.OK: - return s.Conduits.model_validate(await req.json()) - - case st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Conduits) async def update_conduits( self, access_token: str, conduit_id: str, shard_count: int @@ -1571,17 +1014,7 @@ class TwitchAPIClient(OxideHTTP): json={'shard_count': shard_count, 'id': conduit_id}, ) - match req.status_code: - case st.OK: - return s.Conduits.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Conduits) async def delete_conduit(self, access_token: str, conduit_id: str) -> bool: req = await self.delete( @@ -1590,17 +1023,7 @@ class TwitchAPIClient(OxideHTTP): params={'id': conduit_id}, ) - match req.status_code: - case st.NO_CONTENT: - return True - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def get_conduit_shards( self, @@ -1610,41 +1033,15 @@ class TwitchAPIClient(OxideHTTP): status: str | None = None, cache_ttl: int | None = None, ) -> AsyncGenerator[s.ConduitShard]: - after = None - - while True: - req = await self.get( - '/eventsub/conduits/shards', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - {'id': conduit_id, 'status': status, 'after': after} - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - shards = s.ConduitShards.model_validate(json) - - for shard in shards.data: - yield shard - - if isinstance(shards.pagination, s.Pagination): - after = shards.pagination.cursor - - if after is None: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/eventsub/conduits/shards', + s.ConduitShards, + self._auth(access_token), + self.clean_dict({'id': conduit_id, 'status': status}), + cache_ttl, + ): + for item in data.data: + yield item async def update_conduit_shards( self, @@ -1658,17 +1055,7 @@ class TwitchAPIClient(OxideHTTP): json={'conduit_id': conduit_id, 'shards': shards}, ) - match req.status_code: - case st.ACCEPTED: - return s.ConduitShards.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ConduitShards) async def get_content_classification_labels( self, @@ -1714,19 +1101,7 @@ class TwitchAPIClient(OxideHTTP): params={'locale': locale}, ) - match req.status_code: - case st.OK: - return s.ContentClassificationLabels.model_validate( - await req.json() - ) - - case st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ContentClassificationLabels) async def get_drops_entitlements( self, @@ -1739,53 +1114,22 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.DropEntitlement]: - after = None - - while True: - req = await self.get( - '/entitlements/drops', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'drop_id': drop_id, - 'user_id': user_id, - 'game_id': game_id, - 'fulfillment_status': fulfillment_status, - 'after': after, - 'first': first, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - entitlements = s.DropsEntitlements.model_validate(json) - - for entitlement in entitlements.data: - yield entitlement - - if isinstance(entitlements.pagination, s.Pagination): - after = entitlements.pagination.cursor - - if after is None: - break - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.INTERNAL_SERVER_ERROR - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/entitlements/drops', + s.DropsEntitlements, + self._auth(access_token), + self.clean_dict( + { + 'drop_id': drop_id, + 'user_id': user_id, + 'game_id': game_id, + 'fulfillment_status': fulfillment_status, + } + ), + cache_ttl, + ): + for item in data.data: + yield item async def update_drops_entitlements( self, @@ -1805,19 +1149,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.UpdateDropsEntitlements.model_validate( - await req.json() - ) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.INTERNAL_SERVER_ERROR: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.UpdateDropsEntitlements) async def get_extension_configuration_segment( self, @@ -1841,19 +1173,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.ExtensionConfigurationSegment.model_validate( - await req.json() - ) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.TOO_MANY_REQUESTS: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ExtensionConfigurationSegment) async def set_extension_configuration_segment( self, @@ -1879,17 +1199,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.NO_CONTENT: - return True - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def set_extension_required_configurations( self, @@ -1910,17 +1220,7 @@ class TwitchAPIClient(OxideHTTP): }, ) - match req.status_code: - case st.NO_CONTENT: - return True - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def send_extension_pubsub_message( self, @@ -1944,17 +1244,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.NO_CONTENT: - return True - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.UNPROCESSABLE_ENTITY: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def get_extension_live_channels( self, @@ -1964,45 +1254,19 @@ class TwitchAPIClient(OxideHTTP): first: int | None = None, cache_ttl: int | None = None, ) -> AsyncGenerator[s.ExtensionLiveChannel]: - after = None - - while True: - req = await self.get( - '/extensions/live', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'extension_id': extension_id, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - extensions = s.ExtensionLiveChannels.model_validate(json) - - for extension in extensions.data: - yield extension - - if isinstance(extensions.paginaiton, s.Pagination): - after = extensions.paginaiton.cursor - - if after is None: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/extensions/live', + s.ExtensionLiveChannels, + self._auth(access_token), + self.clean_dict( + { + 'extension_id': extension_id, + } + ), + cache_ttl, + ): + for item in data.data: + yield item async def get_extension_secrets( self, @@ -2018,17 +1282,7 @@ class TwitchAPIClient(OxideHTTP): params={'extension_id': extension_id}, ) - match req.status_code: - case st.OK: - return s.ExtensionSecrets.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ExtensionSecrets) async def create_extension_secret( self, jwt_token: str, extension_id: str, delay: int = 300 @@ -2039,17 +1293,7 @@ class TwitchAPIClient(OxideHTTP): params={'extension_id': extension_id, 'delay': delay}, ) - match req.status_code: - case st.OK: - return s.ExtensionSecrets.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ExtensionSecrets) async def send_extension_chat_message( self, @@ -2070,17 +1314,7 @@ class TwitchAPIClient(OxideHTTP): }, ) - match req.status_code: - case st.NO_CONTENT: - return True - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def get_extensions( self, @@ -2102,17 +1336,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.Extensions.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Extensions) async def get_released_extension( self, @@ -2134,17 +1358,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.Extensions.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Extensions) async def get_extension_bits_products( self, @@ -2164,17 +1378,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.ExtensionBitsProducts.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ExtensionBitsProducts) async def update_extension_bits_product( self, @@ -2205,17 +1409,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.NO_CONTENT: - return s.ExtensionBitsProducts.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ExtensionBitsProducts) async def create_eventsub_subscription( self, @@ -2249,46 +1443,18 @@ class TwitchAPIClient(OxideHTTP): }, ) - match req.status_code: - case st.ACCEPTED: - return s.EventsubBaseSubscriptions.model_validate( - await req.json() - ) - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.CONFLICT - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.EventsubBaseSubscriptions) async def delete_eventsub_subscription( self, access_token: str, sub_id: str - ) -> None: + ) -> bool: req = await self.delete( '/eventsub/subscriptions', headers=self._auth(access_token), params={'id': sub_id}, ) - match req.status_code: - case st.NO_CONTENT: - return None - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def get_eventsub_subscriptions( self, @@ -2300,97 +1466,37 @@ class TwitchAPIClient(OxideHTTP): subscription_id: str | None = None, cache_ttl: int | None = None, ) -> AsyncGenerator[tuple[int, sub.Any]]: - after = None - - while True: - req = await self.get( - '/eventsub/subscriptions', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'status': status, - 'type': sub_type, - 'user_id': user_id, - 'subscription_id': subscription_id, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - subscriptions = s.EventsubBaseSubscriptions.model_validate( - json - ) - - for subscription in subscriptions.data: - yield subscriptions.total, subscription - - if isinstance(subscriptions.pagination, s.Pagination): - after = subscriptions.pagination.cursor - - if after is None: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/eventsub/subscriptions', + s.EventsubBaseSubscriptions, + self._auth(access_token), + { + 'status': status, + 'type': sub_type, + 'user_id': user_id, + 'subscription_id': subscription_id, + }, + cache_ttl, + ): + for item in data.data: + yield data.total, item async def get_top_games( self, access_token: str, *, first: int = 20, - before: str | None = None, cache_ttl: int | None = None, ) -> AsyncGenerator[s.Game]: - after = None - - while True: - req = await self.get( - '/games/top', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'first': first, - 'after': after, - 'before': before, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - games = s.Games.model_validate(json) - - for game in games.data: - yield game - - if isinstance(games.pagination, s.Pagination): - after = games.pagination.cursor - - if after is None: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/games/top', + s.Games, + self._auth(access_token), + {'first': first}, + cache_ttl, + ): + for item in data.data: + yield item async def get_games( self, @@ -2414,17 +1520,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.Games.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Games) async def get_creator_goals( self, @@ -2440,17 +1536,7 @@ class TwitchAPIClient(OxideHTTP): params={'broadcaster_id': broadcaster_id}, ) - match req.status_code: - case st.OK: - return s.CreatorGoals.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.CreatorGoals) # TODO: implement guest star endpoints @@ -2468,17 +1554,7 @@ class TwitchAPIClient(OxideHTTP): params={'broadcaster_id': broadcaster_id}, ) - match req.status_code: - case st.OK: - return s.HypeTrainStatus.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.INTERNAL_SERVER_ERROR: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.HypeTrainStatus) async def check_automod_status( self, @@ -2494,22 +1570,7 @@ class TwitchAPIClient(OxideHTTP): params={'broadcaster_id': broadcaster_id}, ) - match req.status_code: - case st.OK: - return s.AutomodStatus.model_validate(await req.json()) - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.AutomodStatus) async def manage_held_automod_messages( self, @@ -2524,19 +1585,7 @@ class TwitchAPIClient(OxideHTTP): params={'action': action, 'msg_id': msg_id, 'user_id': user_id}, ) - match req.status_code: - case st.NO_CONTENT: - return True - - case ( - st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN | st.NOT_FOUND - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def get_automod_settings( self, @@ -2556,17 +1605,7 @@ class TwitchAPIClient(OxideHTTP): }, ) - match req.status_code: - case st.OK: - return s.AutomodSettings.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.AutomodSettings) async def update_automod_settings( self, @@ -2604,17 +1643,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.NO_CONTENT: - return s.AutomodSettings.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.AutomodSettings) async def get_banned_users( self, @@ -2625,46 +1654,19 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.BannedUser]: - after = None - - while True: - req = await self.get( - '/moderation/banned', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'broadcaster_id': broadcaster_id, - 'user_id': user_id, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - banned = s.BannedUsers.model_validate(json) - - for user in banned.data: - yield user - - if isinstance(banned.pagination, s.Pagination): - after = banned.pagination.cursor - - if after is None: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/moderation/banned', + s.BannedUsers, + self._auth(access_token), + { + 'broadcaster_id': broadcaster_id, + 'user_id': user_id, + 'first': first, + }, + cache_ttl, + ): + for item in data.data: + yield item async def ban_user( self, @@ -2692,23 +1694,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.BanUser.model_validate(await req.json()) - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.CONFLICT - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.BanUser) async def unban_user( self, @@ -2716,7 +1702,7 @@ class TwitchAPIClient(OxideHTTP): broadcaster_id: int, moderator_id: int, user_id: int, - ) -> None: + ) -> bool: req = await self.delete( '/moderation/bans', headers=self._auth(access_token), @@ -2727,23 +1713,7 @@ class TwitchAPIClient(OxideHTTP): }, ) - match req.status_code: - case st.NO_CONTENT: - return - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.CONFLICT - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def get_unban_requests( self, @@ -2758,48 +1728,21 @@ class TwitchAPIClient(OxideHTTP): first: int | None = None, cache_ttl: int | None = None, ) -> AsyncGenerator[s.UnbanRequest]: - after = None - - while True: - req = await self.get( - '/moderation/unbans', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'broadcaster_id': broadcaster_id, - 'moderator_id': moderator_id, - 'status': status, - 'user_id': user_id, - 'after': after, - 'first': first, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - unban_requests = s.UnbanRequests.model_validate(json) - - for unban_request in unban_requests.data: - yield unban_request - - if isinstance(unban_requests.pagination, s.Pagination): - after = unban_requests.pagination.cursor - - if after is None: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/moderation/unbans', + s.UnbanRequests, + self._auth(access_token), + { + 'broadcaster_id': broadcaster_id, + 'moderator_id': moderator_id, + 'status': status, + 'user_id': user_id, + 'first': first, + }, + cache_ttl, + ): + for item in data.data: + yield item async def resolve_unban_request( self, @@ -2825,17 +1768,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.UnbanRequests.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.UnbanRequests) async def get_blocked_terms( self, @@ -2846,46 +1779,19 @@ class TwitchAPIClient(OxideHTTP): first: int | None = None, cache_ttl: int | None = None, ) -> AsyncGenerator[s.BlockedTerm]: - after = None - - while True: - req = await self.get( - '/moderation/blocked_terms', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'broadcaster_id': broadcaster_id, - 'moderator_id': moderator_id, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - blocked_terms = s.BlockedTerms.model_validate(json) - - for blocked_term in blocked_terms.data: - yield blocked_term - - if isinstance(blocked_terms.pagination, s.Pagination): - after = blocked_terms.pagination.cursor - - if after is None: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/moderation/blocked_terms', + s.BlockedTerms, + self._auth(access_token), + { + 'broadcaster_id': broadcaster_id, + 'moderator_id': moderator_id, + 'first': first, + }, + cache_ttl, + ): + for item in data.data: + yield item async def add_blocked_term( self, @@ -2904,17 +1810,7 @@ class TwitchAPIClient(OxideHTTP): }, ) - match req.status_code: - case st.OK: - return s.BlockedTerms.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.BlockedTerms) async def remove_blocked_term( self, @@ -2933,17 +1829,7 @@ class TwitchAPIClient(OxideHTTP): }, ) - match req.status_code: - case st.NO_CONTENT: - return True - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def delete_chat_message( self, @@ -2965,19 +1851,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.NO_CONTENT: - return True - - case ( - st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN | st.NOT_FOUND - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def get_moderated_channels( self, @@ -2987,47 +1861,18 @@ class TwitchAPIClient(OxideHTTP): first: int | None = None, cache_ttl: int | None = None, ) -> AsyncGenerator[s.ModeratedChannel]: - after = None - - while True: - req = await self.get( - '/moderation/channels', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'user_id': user_id, - 'after': after, - 'first': first, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - moderated_channels = s.ModeratedChannels.model_validate( - json - ) - - for moderated_channel in moderated_channels.data: - yield moderated_channel - - if isinstance(moderated_channels.pagination, s.Pagination): - after = moderated_channels.pagination.cursor - - if after is None: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/moderation/channels', + s.ModeratedChannels, + self._auth(access_token), + { + 'user_id': user_id, + 'first': first, + }, + cache_ttl, + ): + for item in data.data: + yield item async def get_moderators( self, @@ -3038,46 +1883,21 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.Moderator]: - after = None - - while True: - req = await self.get( - '/moderation/moderators', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'broadcaster_id': broadcaster_id, - 'user_id': user_id, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - moderators = s.Moderators.model_validate(json) - - for moderator in moderators.data: - yield moderator - - if isinstance(moderators.pagination, s.Pagination): - after = moderators.pagination.cursor - - if after is None: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/moderation/moderators', + s.Moderators, + self._auth(access_token), + self.clean_dict( + { + 'broadcaster_id': broadcaster_id, + 'user_id': user_id, + 'first': first, + } + ), + cache_ttl, + ): + for item in data.data: + yield item async def add_channel_moderator( self, access_token: str, broadcaster_id: int, user_id: int @@ -3088,22 +1908,7 @@ class TwitchAPIClient(OxideHTTP): params={'broadcaster_id': broadcaster_id, 'user_id': user_id}, ) - match req.status_code: - case st.NO_CONTENT: - return True - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.UNPROCESSABLE_ENTITY - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def remove_channel_moderator( self, access_token: str, broadcaster_id: int, user_id: int @@ -3114,17 +1919,7 @@ class TwitchAPIClient(OxideHTTP): params={'broadcaster_id': broadcaster_id, 'user_id': user_id}, ) - match req.status_code: - case st.NO_CONTENT: - return True - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.TOO_MANY_REQUESTS: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def get_vips( self, @@ -3135,46 +1930,19 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.VIP]: - after = None - - while True: - req = await self.get( - '/channels/vips', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'user_id': user_id, - 'broadcaster_id': broadcaster_id, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - vips = s.VIPs.model_validate(json) - - for vip in vips.data: - yield vip - - if isinstance(vips.pagination, s.Pagination): - after = vips.pagination.cursor - - if after is None: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/channels/vips', + s.VIPs, + self._auth(access_token), + { + 'user_id': user_id, + 'broadcaster_id': broadcaster_id, + 'first': first, + }, + cache_ttl, + ): + for item in data.data: + yield item async def add_channel_vip( self, access_token: str, broadcaster_id: int, user_id: int @@ -3185,25 +1953,7 @@ class TwitchAPIClient(OxideHTTP): params={'broadcaster_id': broadcaster_id, 'user_id': user_id}, ) - match req.status_code: - case st.NO_CONTENT: - return True - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.NOT_FOUND - | st.CONFLICT - | st.UNPROCESSABLE_ENTITY - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def remove_channel_vip( self, access_token: str, broadcaster_id: int, user_id: int @@ -3214,24 +1964,7 @@ class TwitchAPIClient(OxideHTTP): params={'broadcaster_id': broadcaster_id, 'user_id': user_id}, ) - match req.status_code: - case st.NO_CONTENT: - return True - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.NOT_FOUND - | st.UNPROCESSABLE_ENTITY - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def update_shield_mode_status( self, @@ -3251,17 +1984,7 @@ class TwitchAPIClient(OxideHTTP): }, ) - match req.status_code: - case st.OK: - return s.ShieldModeStatus.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ShieldModeStatus) async def get_shield_mode_status( self, @@ -3281,17 +2004,7 @@ class TwitchAPIClient(OxideHTTP): }, ) - match req.status_code: - case st.OK: - return s.ShieldModeStatus.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ShieldModeStatus) async def warn_chat_user( self, @@ -3316,24 +2029,7 @@ class TwitchAPIClient(OxideHTTP): }, ) - match req.status_code: - case st.OK: - return s.UserWarns.model_validate(await req.json()) - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.CONFLICT - | st.TOO_MANY_REQUESTS - | st.INTERNAL_SERVER_ERROR - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.UserWarns) async def get_polls( self, @@ -3344,46 +2040,21 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.Poll]: - after = None - - while True: - req = await self.get( - '/polls', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'broadcaster_id': broadcaster_id, - 'id': poll_id, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - polls = s.Polls.model_validate(json) - - for poll in polls.data: - yield poll - - if isinstance(polls.pagination, s.Pagination): - after = polls.pagination.cursor - - if after is None: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/polls', + s.Polls, + self._auth(access_token), + self.clean_dict( + { + 'broadcaster_id': broadcaster_id, + 'id': poll_id, + 'first': first, + } + ), + cache_ttl, + ): + for item in data.data: + yield item async def create_poll( self, @@ -3414,17 +2085,7 @@ class TwitchAPIClient(OxideHTTP): json=body, ) - match req.status_code: - case st.OK: - return s.Polls.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Polls) async def end_poll( self, @@ -3443,17 +2104,7 @@ class TwitchAPIClient(OxideHTTP): }, ) - match req.status_code: - case st.OK: - return s.Polls.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Polls) async def get_predictions( self, @@ -3464,46 +2115,21 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.Prediction]: - after = None - - while True: - req = await self.get( - '/predictions', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'broadcaster_id': broadcaster_id, - 'id': prediction_id, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - predictions = s.Predictions.model_validate(json) - - for prediction in predictions.data: - yield prediction - - if isinstance(predictions.pagination, s.Pagination): - after = predictions.pagination.cursor - - if after is None: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/predictions', + s.Predictions, + self._auth(access_token), + self.clean_dict( + { + 'broadcaster_id': broadcaster_id, + 'id': prediction_id, + 'first': first, + } + ), + cache_ttl, + ): + for item in data.data: + yield item async def create_prediction( self, @@ -3527,17 +2153,7 @@ class TwitchAPIClient(OxideHTTP): json=body, ) - match req.status_code: - case st.OK: - return s.Predictions.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.TOO_MANY_REQUESTS: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Predictions) async def end_prediction( self, @@ -3561,17 +2177,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.Predictions.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Predictions) async def start_a_raid( self, @@ -3588,23 +2194,7 @@ class TwitchAPIClient(OxideHTTP): }, ) - match req.status_code: - case st.OK: - return s.Raids.model_validate(await req.json()) - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.NOT_FOUND - | st.CONFLICT - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Raids) async def cancel_a_raid( self, access_token: str, broadcaster_id: int @@ -3615,22 +2205,7 @@ class TwitchAPIClient(OxideHTTP): params={'broadcaster_id': broadcaster_id}, ) - match req.status_code: - case st.NO_CONTENT: - return True - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.NOT_FOUND - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def get_channel_stream_schedule( self, @@ -3642,52 +2217,22 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.Schedule]: - after = None - - while True: - req = await self.get( - '/schedule', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'broadcaster_id': broadcaster_id, - 'id': segment_id, - 'start_time': start_time, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - schedules = s.Schedules.model_validate(json) - - for schedule in schedules.data: - yield schedule - - if isinstance(schedules.pagination, s.Pagination): - after = schedules.pagination.cursor - - if after is None: - break - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.NOT_FOUND - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/schedule', + s.Schedules, + self._auth(access_token), + self.clean_dict( + { + 'broadcaster_id': broadcaster_id, + 'id': segment_id, + 'start_time': start_time, + 'first': first, + } + ), + cache_ttl, + ): + for item in data.data: + yield item async def get_channel_icalendar( self, @@ -3740,17 +2285,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.NO_CONTENT: - return True - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def create_channel_stream_schedule_segment( self, @@ -3780,17 +2315,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.Schedules.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Schedules) async def update_channel_stream_schedule_segment( self, @@ -3826,17 +2351,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.Schedules.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Schedules) async def delete_channel_stream_schedule_segment( self, access_token: str, broadcaster_id: int, segment_id: str @@ -3852,17 +2367,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.NO_CONTENT: - return True - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def search_categories( self, @@ -3872,41 +2377,15 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.Category]: - after = None - - while True: - req = await self.get( - '/search/categories', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - {'query': query, 'first': first, 'after': after} - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - categories = s.Categories.model_validate(json) - - for category in categories.data: - yield category - - if isinstance(categories.pagination, s.Pagination): - after = categories.pagination.cursor - - if not after: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/search/categories', + s.Categories, + self._auth(access_token), + {'query': query, 'first': first}, + cache_ttl, + ): + for item in data.data: + yield item async def search_channels( self, @@ -3917,46 +2396,17 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.Channel]: - after = None - - while True: - req = await self.get( - '/search/channels', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'query': query, - 'live_only': live_only, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - channels = s.Channels.model_validate(json) - - for channel in channels.data: - yield channel - - if isinstance(channels.pagination, s.Pagination): - after = channels.pagination.cursor - - if not after: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/search/channels', + s.Channels, + self._auth(access_token), + self.clean_dict( + {'query': query, 'live_only': live_only, 'first': first} + ), + cache_ttl, + ): + for item in data.data: + yield item async def get_stream_key( self, @@ -3972,17 +2422,7 @@ class TwitchAPIClient(OxideHTTP): params={'broadcaster_id': broadcaster_id}, ) - match req.status_code: - case st.OK: - return s.StreamKeys.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.StreamKeys) async def get_streams( self, @@ -3997,50 +2437,25 @@ class TwitchAPIClient(OxideHTTP): before: str | None = None, cache_ttl: int | None = None, ) -> AsyncGenerator[s.Stream]: - after = None - - while True: - req = await self.get( - '/streams', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'user_id': user_id, - 'user_login': user_login, - 'game_id': game_id, - 'type': stream_type, - 'language': language, - 'first': first, - 'before': before, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - streams = s.Streams.model_validate(json) - - for stream in streams.data: - yield stream - - if isinstance(streams.pagination, s.Pagination): - after = streams.pagination.cursor - - if not after: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/streams', + s.Streams, + self._auth(access_token), + self.clean_dict( + { + 'user_id': user_id, + 'user_login': user_login, + 'game_id': game_id, + 'type': stream_type, + 'language': language, + 'first': first, + 'before': before, + } + ), + cache_ttl, + ): + for item in data.data: + yield item async def get_followed_streams( self, @@ -4050,45 +2465,18 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.Stream]: - after = None - - while True: - req = await self.get( - '/streams/followed', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'user_id': user_id, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - streams = s.Streams.model_validate(json) - - for stream in streams.data: - yield stream - - if isinstance(streams.pagination, s.Pagination): - after = streams.pagination.cursor - - if not after: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/streams/followed', + s.Streams, + self._auth(access_token), + { + 'user_id': user_id, + 'first': first, + }, + cache_ttl, + ): + for item in data.data: + yield item async def create_stream_marker( self, @@ -4108,19 +2496,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.CreateStreamMarkers.model_validate(await req.json()) - - case ( - st.BAD_REQUEST | st.UNAUTHORIZED | st.FORBIDDEN | st.NOT_FOUND - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.CreateStreamMarkers) async def get_stream_markers( self, @@ -4132,52 +2508,22 @@ class TwitchAPIClient(OxideHTTP): before: str | None = None, cache_ttl: int | None = None, ) -> AsyncGenerator[s.StreamMarkersData]: - after = None - - while True: - req = await self.get( - '/streams/markers', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'user_id': user_id, - 'video_id': video_id, - 'first': first, - 'before': before, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - markers = s.StreamMarkers.model_validate(json) - - for marker in markers.data: - yield marker - - if isinstance(markers.pagination, s.Pagination): - after = markers.pagination.cursor - - if not after: - break - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.NOT_FOUND - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/streams/markers', + s.StreamMarkers, + self._auth(access_token), + self.clean_dict( + { + 'user_id': user_id, + 'video_id': video_id, + 'first': first, + 'before': before, + } + ), + cache_ttl, + ): + for item in data.data: + yield item async def get_broadcaster_subscriptions( self, @@ -4188,52 +2534,21 @@ class TwitchAPIClient(OxideHTTP): before: str | None = None, cache_ttl: int | None = None, ) -> AsyncGenerator[tuple[int, int, s.BroadcasterSubscription]]: - after = None - - while True: - req = await self.get( - '/subscriptions', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'broadcaster_id': broadcaster_id, - 'first': first, - 'after': after, - 'before': before, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - subscriptions = s.BroadcasterSubscriptions.model_validate( - json - ) - - for subscription in subscriptions.data: - yield ( - subscriptions.total, - subscriptions.points, - subscription, - ) - - if isinstance(subscriptions.pagination, s.Pagination): - after = subscriptions.pagination.cursor - - if not after: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/subscriptions', + s.BroadcasterSubscriptions, + self._auth(access_token), + self.clean_dict( + { + 'broadcaster_id': broadcaster_id, + 'first': first, + 'before': before, + } + ), + cache_ttl, + ): + for item in data.data: + yield data.total, data.points, item async def check_user_subscription( self, @@ -4255,17 +2570,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.OK: - return s.UserSubscription.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.UserSubscription) async def get_channel_teams( self, @@ -4281,17 +2586,7 @@ class TwitchAPIClient(OxideHTTP): params={'broadcaster_id': broadcaster_id}, ) - match req.status_code: - case st.OK: - return s.ChannelTeams.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.ChannelTeams) async def get_teams( self, @@ -4308,17 +2603,7 @@ class TwitchAPIClient(OxideHTTP): params=self.clean_dict({'name': name, 'id': team_id}), ) - match req.status_code: - case st.OK: - return s.Teams.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Teams) async def get_users( self, @@ -4335,17 +2620,7 @@ class TwitchAPIClient(OxideHTTP): params=self.clean_dict({'id': user_id, 'login': login}), ) - match req.status_code: - case st.OK: - return s.Users.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Users) async def update_user( self, access_token: str, *, description: str = '' @@ -4356,17 +2631,7 @@ class TwitchAPIClient(OxideHTTP): json={'description': description}, ) - match req.status_code: - case st.OK: - return s.Users.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.TOO_MANY_REQUESTS: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.Users) async def get_authorization_by_user( self, @@ -4382,22 +2647,7 @@ class TwitchAPIClient(OxideHTTP): params={'user_id': user_id}, ) - match req.status_code: - case st.OK: - return s.AuthorizationsByUser.model_validate(await req.json()) - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.INTERNAL_SERVER_ERROR - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.AuthorizationsByUser) async def get_user_block_list( self, @@ -4407,45 +2657,15 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.UserBlock]: - after = None - - while True: - req = await self.get( - '/users/blocks', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'broadcaster_id': broadcaster_id, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - user_block_list = s.UserBlockList.model_validate(json) - - for user in user_block_list.data: - yield user - - if isinstance(user_block_list.pagination, s.Pagination): - after = user_block_list.pagination.cursor - - if after is None: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/users/blocks', + s.UserBlockList, + self._auth(access_token), + {'broadcaster_id': broadcaster_id, 'first': first}, + cache_ttl, + ): + for item in data.data: + yield item async def block_user( self, @@ -4467,17 +2687,7 @@ class TwitchAPIClient(OxideHTTP): ), ) - match req.status_code: - case st.NO_CONTENT: - return True - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def unblock_user( self, access_token: str, target_user_id: int @@ -4488,17 +2698,7 @@ class TwitchAPIClient(OxideHTTP): params={'target_user_id': target_user_id}, ) - match req.status_code: - case st.NO_CONTENT: - return True - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def get_user_extensions( self, access_token: str, *, cache_ttl: int | None = None @@ -4509,17 +2709,7 @@ class TwitchAPIClient(OxideHTTP): headers=self._auth(access_token), ) - match req.status_code: - case st.OK: - return s.UserExtensions.model_validate(await req.json()) - - case st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.UserExtensions) async def get_user_active_extensions( self, @@ -4535,17 +2725,7 @@ class TwitchAPIClient(OxideHTTP): params={'user_id': user_id}, ) - match req.status_code: - case st.OK: - return s.UserActiveExtensions.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.UserActiveExtensions) async def update_user_extensions( self, access_token: str, extensions: s.UserActiveExtensions @@ -4556,17 +2736,7 @@ class TwitchAPIClient(OxideHTTP): json=extensions.model_dump(), ) - match req.status_code: - case st.NO_CONTENT: - return True - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) async def get_videos( self, @@ -4582,51 +2752,26 @@ class TwitchAPIClient(OxideHTTP): first: int = 20, cache_ttl: int | None = None, ) -> AsyncGenerator[s.Video]: - after = None - - while True: - req = await self.get( - '/videos', - cache_ttl=cache_ttl, - headers=self._auth(access_token), - params=self.clean_dict( - { - 'video_id': video_id, - 'user_id': user_id, - 'game_id': game_id, - 'language': language, - 'period': period, - 'sort': sort, - 'video_type': video_type, - 'first': first, - 'after': after, - } - ), - ) - - match req.status_code: - case st.OK: - json = await req.json() - videos = s.Videos.model_validate(json) - - for video in videos.data: - yield video - - if isinstance(videos.pagination, s.Pagination): - after = videos.pagination.cursor - - if after is None: - break - - case st.BAD_REQUEST | st.UNAUTHORIZED | st.NOT_FOUND: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError( - req.status_code, 'Internal Server Error' - ) + async for data in self._paginate( + '/videos', + s.Videos, + self._auth(access_token), + self.clean_dict( + { + 'video_id': video_id, + 'user_id': user_id, + 'game_id': game_id, + 'language': language, + 'period': period, + 'sort': sort, + 'video_type': video_type, + 'first': first, + } + ), + cache_ttl, + ): + for item in data.data: + yield item async def delete_videos( self, access_token: str, video_id: int | list[int] @@ -4637,17 +2782,7 @@ class TwitchAPIClient(OxideHTTP): params={'id': video_id}, ) - match req.status_code: - case st.OK: - return s.DeleteVideos.model_validate(await req.json()) - - case st.BAD_REQUEST | st.UNAUTHORIZED: - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, s.DeleteVideos) async def send_whisper( self, @@ -4663,20 +2798,4 @@ class TwitchAPIClient(OxideHTTP): json={'message': message}, ) - match req.status_code: - case st.NO_CONTENT: - return True - - case ( - st.BAD_REQUEST - | st.UNAUTHORIZED - | st.FORBIDDEN - | st.NOT_FOUND - | st.TOO_MANY_REQUESTS - ): - raise s.ClientError( - req.status_code, (await req.json())['message'] - ) - - case _: - raise s.InternalError(req.status_code, 'Internal Server Error') + return await self._process(req, None) diff --git a/src/oxidetwitch/schema.py b/src/oxidetwitch/schema.py index 6d627e1..7a9c1f2 100644 --- a/src/oxidetwitch/schema.py +++ b/src/oxidetwitch/schema.py @@ -33,6 +33,14 @@ class Pagination(BaseSchema): cursor: str +class BasePaginated(BaseSchema): + pagination: Pagination | dict[Any, Any] | None = None + + +class PaginatedSchema[T](BasePaginated): + data: list[T] + + class AppAccessToken(BaseSchema): access_token: str expires_in: int @@ -100,9 +108,8 @@ class ExtensionAnalyticsData(BaseSchema): date_range: DateRange -class ExtensionAnalytics(BaseSchema): +class ExtensionAnalytics(PaginatedSchema[ExtensionAnalyticsData]): data: list[ExtensionAnalyticsData] - pagination: Pagination | dict[Any, Any] | None = None class GameAnalyticsData(BaseSchema): @@ -112,9 +119,8 @@ class GameAnalyticsData(BaseSchema): date_range: DateRange -class GameAnalytics(BaseSchema): +class GameAnalytics(PaginatedSchema[GameAnalyticsData]): data: list[GameAnalyticsData] - pagination: Pagination | dict[Any, Any] | None = None class BitsLeaderboardData(BaseSchema): @@ -213,9 +219,8 @@ class ExtensionTransactionsData(BaseSchema): product_data: ExtensionProductData -class ExtensionTransactions(BaseSchema): +class ExtensionTransactions(PaginatedSchema[ExtensionTransactionsData]): data: list[ExtensionTransactionsData] - pagination: Pagination | dict[Any, Any] | None = None class ContentClassificationLabel(TypedDict): @@ -265,9 +270,8 @@ class FollowedChannel(BaseSchema): followed_at: datetime -class FollowedChannels(BaseSchema): +class FollowedChannels(PaginatedSchema[FollowedChannel]): data: list[FollowedChannel] - pagination: Pagination | dict[Any, Any] | None = None total: int @@ -278,9 +282,8 @@ class ChannelFollower(BaseSchema): followed_at: datetime -class ChannelFollowers(BaseSchema): +class ChannelFollowers(PaginatedSchema[ChannelFollower]): data: list[ChannelFollower] - pagination: Pagination | dict[Any, Any] | None = None total: int @@ -353,9 +356,8 @@ class CustomRewardRedemption(BaseSchema): reward: CustomRewardRedemptionReward -class CustomRewardRedemptions(BaseSchema): +class CustomRewardRedemptions(PaginatedSchema[CustomRewardRedemption]): data: list[CustomRewardRedemption] - pagination: Pagination | dict[Any, Any] | None = None class CharityCampaignCurrentAmount(BaseSchema): @@ -402,9 +404,8 @@ class CharityDonation(BaseSchema): amount: CharityDonationAmount -class CharityDonations(BaseSchema): +class CharityDonations(PaginatedSchema[CharityDonation]): data: list[CharityDonation] - pagination: Pagination | dict[Any, Any] | None = None class ChattersData(BaseSchema): @@ -413,9 +414,8 @@ class ChattersData(BaseSchema): user_name: str -class Chatters(BaseSchema): +class Chatters(PaginatedSchema[ChattersData]): data: list[ChattersData] - pagination: Pagination | dict[Any, Any] | None = None total: int @@ -522,8 +522,8 @@ class SharedChatSession(BaseSchema): data: list[SharedChatSessionData] -class UserEmotes(ChannelEmotes): - pagination: Pagination | dict[Any, Any] | None = None +class UserEmotes(PaginatedSchema[ChannelEmote], ChannelEmotes): + pass class MessageDropReason(BaseSchema): @@ -572,9 +572,8 @@ class Clip(BaseSchema): is_featured: bool -class Clips(BaseSchema): +class Clips(PaginatedSchema[Clip]): data: list[Clip] - pagination: Pagination | dict[Any, Any] | None = None class ClipDownload(BaseSchema): @@ -626,9 +625,8 @@ class ConduitShard(BaseSchema): transport: ConduitShardTransportWebhook | ConduitShardTransportWebsocket -class ConduitShards(BaseSchema): +class ConduitShards(PaginatedSchema[ConduitShard]): data: list[ConduitShard] - pagination: Pagination | dict[Any, Any] | None = None class UpdateConduitShardTransportWebhook(TypedDict): @@ -688,9 +686,8 @@ class DropEntitlement(BaseSchema): last_updated: datetime -class DropsEntitlements(BaseSchema): +class DropsEntitlements(PaginatedSchema[DropEntitlement]): data: list[DropEntitlement] - pagination: Pagination | dict[Any, Any] | None = None class UpdateDropsEntitlementsData(BaseSchema): @@ -723,9 +720,8 @@ class ExtensionLiveChannel(BaseSchema): title: str -class ExtensionLiveChannels(BaseSchema): +class ExtensionLiveChannels(PaginatedSchema[ExtensionLiveChannel]): data: list[ExtensionLiveChannel] - paginaiton: Pagination | dict[Any, Any] | None = None class ExtensionSecret(BaseSchema): @@ -838,12 +834,11 @@ class ExtensionBitsProducts(BaseSchema): data: list[ExtensionBitsProduct] -class EventsubBaseSubscriptions(BaseSchema): +class EventsubBaseSubscriptions(PaginatedSchema[sub.Any]): data: list[sub.Any] total: int total_cost: int max_total_cost: int - pagination: Pagination | dict[Any, Any] | None = None class Game(BaseSchema): @@ -853,9 +848,8 @@ class Game(BaseSchema): igdb_id: int | str -class Games(BaseSchema): +class Games(PaginatedSchema[Game]): data: list[Game] - pagination: Pagination | dict[Any, Any] | None = None class CreatorGoal(BaseSchema): @@ -971,9 +965,8 @@ class BannedUser(BaseSchema): moderator_name: str -class BannedUsers(BaseSchema): +class BannedUsers(PaginatedSchema[BannedUser]): data: list[BannedUser] - pagination: Pagination | dict[Any, Any] | None = None class BanUserData(BaseSchema): @@ -1008,9 +1001,8 @@ class UnbanRequest(BaseSchema): resolution_text: str | None -class UnbanRequests(BaseSchema): +class UnbanRequests(PaginatedSchema[UnbanRequest]): data: list[UnbanRequest] - pagination: Pagination | dict[Any, Any] | None = None class BlockedTerm(BaseSchema): @@ -1023,9 +1015,8 @@ class BlockedTerm(BaseSchema): expires_at: datetime | None -class BlockedTerms(BaseSchema): +class BlockedTerms(PaginatedSchema[BlockedTerm]): data: list[BlockedTerm] - pagination: Pagination | dict[Any, Any] | None = None class ModeratedChannel(BaseSchema): @@ -1034,9 +1025,8 @@ class ModeratedChannel(BaseSchema): broadcaster_name: str -class ModeratedChannels(BaseSchema): +class ModeratedChannels(PaginatedSchema[ModeratedChannel]): data: list[ModeratedChannel] - pagination: Pagination | dict[Any, Any] | None = None class Moderator(BaseSchema): @@ -1045,9 +1035,8 @@ class Moderator(BaseSchema): user_name: str -class Moderators(BaseSchema): +class Moderators(PaginatedSchema[Moderator]): data: list[Moderator] - pagination: Pagination | dict[Any, Any] | None = None class VIP(BaseSchema): @@ -1056,9 +1045,8 @@ class VIP(BaseSchema): user_name: str -class VIPs(BaseSchema): +class VIPs(PaginatedSchema[VIP]): data: list[VIP] - pagination: Pagination | dict[Any, Any] | None = None class ShieldModeStatusData(BaseSchema): @@ -1108,9 +1096,8 @@ class Poll(BaseSchema): ended_at: datetime | None -class Polls(BaseSchema): +class Polls(PaginatedSchema[Poll]): data: list[Poll] - pagination: Pagination | dict[Any, Any] | None = None class PredictionTopPredictor(BaseSchema): @@ -1145,9 +1132,8 @@ class Prediction(BaseSchema): locked_at: datetime | None -class Predictions(BaseSchema): +class Predictions(PaginatedSchema[Prediction]): data: list[Prediction] - pagination: Pagination | dict[Any, Any] | None = None class Raid(BaseSchema): @@ -1187,9 +1173,8 @@ class Schedule(BaseSchema): segments: list[ScheduleSegment] -class Schedules(BaseSchema): +class Schedules(PaginatedSchema[Schedule]): data: list[Schedule] - pagination: Pagination | dict[Any, Any] | None = None class Category(BaseSchema): @@ -1198,9 +1183,8 @@ class Category(BaseSchema): box_art_url: str -class Categories(BaseSchema): +class Categories(PaginatedSchema[Category]): data: list[Category] - pagination: Pagination | dict[Any, Any] | None = None class Channel(BaseSchema): @@ -1217,9 +1201,8 @@ class Channel(BaseSchema): started_at: datetime | None -class Channels(BaseSchema): +class Channels(PaginatedSchema[Channel]): data: list[Channel] - pagination: Pagination | dict[Any, Any] | None = None class StreamKey(BaseSchema): @@ -1248,9 +1231,8 @@ class Stream(BaseSchema): is_mature: bool -class Streams(BaseSchema): +class Streams(PaginatedSchema[Stream]): data: list[Stream] - pagination: Pagination | dict[Any, Any] | None = None class BaseStreamMarker(BaseSchema): @@ -1280,9 +1262,8 @@ class StreamMarkersData(BaseSchema): videos: list[StreamMarkerVideo] -class StreamMarkers(BaseSchema): +class StreamMarkers(PaginatedSchema[StreamMarkersData]): data: list[StreamMarkersData] - pagination: Pagination | dict[Any, Any] | None = None class Subscription(BaseSchema): @@ -1303,9 +1284,8 @@ class BroadcasterSubscription(Subscription): user_name: str -class BroadcasterSubscriptions(BaseSchema): +class BroadcasterSubscriptions(PaginatedSchema[BroadcasterSubscription]): data: list[BroadcasterSubscription] - pagination: Pagination | dict[Any, Any] | None = None total: int points: int @@ -1391,9 +1371,8 @@ class UserBlock(BaseSchema): display_name: str -class UserBlockList(BaseSchema): +class UserBlockList(PaginatedSchema[UserBlock]): data: list[UserBlock] - pagination: Pagination | dict[Any, Any] | None = None class UserExtension(BaseSchema): @@ -1480,9 +1459,8 @@ class Video(BaseSchema): muted_segments: list[VideoMutedSegment] -class Videos(BaseSchema): +class Videos(PaginatedSchema[Video]): data: list[Video] - pagination: Pagination | dict[Any, Any] | None class DeleteVideos(BaseSchema): -- 2.47.2