From 33028e18f71c4871d6ae10965494af02ec762ac5 Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Wed, 28 Feb 2024 22:53:27 +0900 Subject: [PATCH 1/5] [Fix] Fixed channel type issue --- chzzkpy/client.py | 4 ++-- chzzkpy/live.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/chzzkpy/client.py b/chzzkpy/client.py index 648bb43..2a205a8 100644 --- a/chzzkpy/client.py +++ b/chzzkpy/client.py @@ -82,11 +82,11 @@ def login(self, authorization_key: str, session_key: str): self._api_session.login(authorization_key, session_key) self._game_session.login(authorization_key, session_key) - async def live_status(self, channel_id: str) -> LiveStatus: + async def live_status(self, channel_id: str) -> Optional[LiveStatus]: res = await self._api_session.live_status(channel_id=channel_id) return res.content - async def live_detail(self, channel_id: str) -> LiveDetail: + async def live_detail(self, channel_id: str) -> Optional[LiveDetail]: res = await self._api_session.live_detail(channel_id=channel_id) return res.content diff --git a/chzzkpy/live.py b/chzzkpy/live.py index 0ee57a4..145d1fe 100644 --- a/chzzkpy/live.py +++ b/chzzkpy/live.py @@ -44,7 +44,7 @@ class LiveStatus(ChzzkModel): adult: bool chat_channel_id: str category_type: Optional[str] - live_category: str + live_category: Optional[str] live_category_value: str live_polling_status: Json[LivePollingStatus] = Field(alias="livePollingStatusJson") fault_status: Any # typing: ??? From a3f601a20579783e7d191be412ed359ff3b0a1d1 Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Tue, 5 Mar 2024 21:30:33 +0900 Subject: [PATCH 2/5] [Feat] Update requirements --- requirements.in | 10 +++++----- requirements.txt | 30 +++++++++++++++--------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/requirements.in b/requirements.in index d4646a3..5ad4a8b 100644 --- a/requirements.in +++ b/requirements.in @@ -1,14 +1,14 @@ -aiohttp==3.9.1 +aiohttp==3.9.3 aiosignal==1.3.1 annotated-types==0.6.0 async-timeout==4.0.3 -async_client_decorator==0.2.0 +ahttp_client==1.0.0 attrs==23.2.0 charset-normalizer==3.3.2 frozenlist==1.4.1 idna==3.6 -multidict==6.0.4 -pydantic==2.6.1 -pydantic_core==2.16.2 +multidict==6.0.5 +pydantic==2.6.2 +pydantic_core==2.16.3 typing_extensions==4.9.0 yarl==1.9.4 diff --git a/requirements.txt b/requirements.txt index d60cd34..e1182c9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,54 +4,54 @@ # # pip-compile --output-file=requirements.txt requirements.in # -aiohttp==3.9.1 +ahttp-client==1.0.0 + # via -r requirements.in +aiohttp==3.9.3 # via # -r requirements.in - # async-client-decorator + # ahttp-client aiosignal==1.3.1 # via # -r requirements.in + # ahttp-client # aiohttp - # async-client-decorator annotated-types==0.6.0 # via # -r requirements.in # pydantic -async-client-decorator==0.2.0 - # via -r requirements.in async-timeout==4.0.3 # via # -r requirements.in - # async-client-decorator + # ahttp-client attrs==23.2.0 # via # -r requirements.in + # ahttp-client # aiohttp - # async-client-decorator charset-normalizer==3.3.2 # via # -r requirements.in - # async-client-decorator + # ahttp-client frozenlist==1.4.1 # via # -r requirements.in + # ahttp-client # aiohttp # aiosignal - # async-client-decorator idna==3.6 # via # -r requirements.in - # async-client-decorator + # ahttp-client # yarl -multidict==6.0.4 +multidict==6.0.5 # via # -r requirements.in + # ahttp-client # aiohttp - # async-client-decorator # yarl -pydantic==2.6.1 +pydantic==2.6.2 # via -r requirements.in -pydantic-core==2.16.2 +pydantic-core==2.16.3 # via # -r requirements.in # pydantic @@ -63,5 +63,5 @@ typing-extensions==4.9.0 yarl==1.9.4 # via # -r requirements.in + # ahttp-client # aiohttp - # async-client-decorator From 96c2a5fa2c3b7eabc056a655b0ece8eadd8d4526 Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Tue, 5 Mar 2024 22:40:05 +0900 Subject: [PATCH 3/5] [Feat] Update package ahttp-client (async-client-decorator) --- chzzkpy/chat/http.py | 19 +++---- chzzkpy/http.py | 118 ++++++++++--------------------------------- requirements.txt | 6 +-- 3 files changed, 38 insertions(+), 105 deletions(-) diff --git a/chzzkpy/chat/http.py b/chzzkpy/chat/http.py index b4f3c26..b54e7c8 100644 --- a/chzzkpy/chat/http.py +++ b/chzzkpy/chat/http.py @@ -1,26 +1,21 @@ -from async_client_decorator import get, Query +from ahttp_client import get, Query +from ahttp_client.extension import get_pydantic_response_model from typing import Annotated from .access_token import AccessToken from ..base_model import Content from ..http import ( ChzzkSession, - NaverGameAPISession, - _response_pydantic_model_validation_able, - _custom_query_name, - _response_pydantic_model_validation, + NaverGameAPISession ) class ChzzkChatSession(NaverGameAPISession): - @_response_pydantic_model_validation_able - @ChzzkSession.logging - @_custom_query_name("channel_id", "channelId") # Will moved. (Temporary Decorator) - @ChzzkSession.login_able - @get("/nng_main/v1/chats/access-token") + @get_pydantic_response_model() + @get("/nng_main/v1/chats/access-token", directly_response=True) + @ChzzkSession.configuration(login_able=True) @Query.default_query("chatType", "STREAMING") - @_response_pydantic_model_validation async def chat_access_token( - self, channel_id: Annotated[str, Query] + self, channel_id: Annotated[str, Query.to_camel()] ) -> Content[AccessToken]: pass diff --git a/chzzkpy/http.py b/chzzkpy/http.py index c6d75e5..18cd84b 100644 --- a/chzzkpy/http.py +++ b/chzzkpy/http.py @@ -23,12 +23,12 @@ import asyncio import functools -import inspect import logging from typing import Annotated, Optional -import aiohttp -from async_client_decorator import Session, get, Path +from ahttp_client import Session, get, Path +from ahttp_client.extension import get_pydantic_response_model +from ahttp_client.request import RequestCore from .base_model import ChzzkModel, Content from .error import LoginRequired @@ -38,50 +38,6 @@ _log = logging.getLogger(__name__) -# This decorator will remove at https://github.com/gunyu1019/async-client-decorator/issues/8 -def _response_pydantic_model_validation(func): - signature = inspect.signature(func) - if not issubclass(signature.return_annotation, ChzzkModel): - return func - - signature = inspect.signature(func) - - @functools.wraps(func) - async def wrapper(self: Session, response: aiohttp.ClientResponse, *_1, **_2): - data = await response.json() - validated_data = signature.return_annotation.model_validate(data) - return validated_data - - return wrapper - - -# This decorator will remove at https://github.com/gunyu1019/async-client-decorator/issues/8 -def _response_pydantic_model_validation_able(func): - signature = inspect.signature(func) - if not issubclass(signature.return_annotation, ChzzkModel): - return func - - func.__component_parameter__.response.append("response") - return func - - -# This decorator will remove at https://github.com/gunyu1019/async-client-decorator/issues/9 -def _custom_query_name(oldest_name, replace_name): - def decorator(func): - func.__component_parameter__.query[replace_name] = ( - func.__component_parameter__.query.pop(oldest_name) - ) - - @functools.wraps(func) - def wrapper(self, *args, **kwargs): - kwargs[replace_name] = kwargs.pop(oldest_name) - return func(self, *args, **kwargs) - - return wrapper - - return decorator - - class ChzzkSession(Session): def __init__(self, base_url: str, loop: Optional[asyncio.AbstractEventLoop] = None): super().__init__(base_url=base_url, loop=loop) @@ -98,36 +54,25 @@ def has_login(self) -> bool: return self._authorization_key is not None and self._session_key is not None @staticmethod - def login_required(func): - @functools.wraps(func) - async def wrapper(self: "ChzzkSession", *args, **kwargs): - if not self.has_login: - raise LoginRequired() - - return await func(self, *args, **kwargs) - - return wrapper - - @staticmethod - def login_able(func): - @functools.wraps(func) - async def wrapper(self: "ChzzkSession", *args, **kwargs): + def configuration(login_able: bool = False, login_required: bool = False): + def decorator(func): + func.__login_able__ = login_able + func.__login_required__ = login_required + return func + return decorator + + async def before_request(self, request: RequestCore, path: str) -> tuple[RequestCore, str]: + _log.debug(f"Path({path}) was called.") + + # Authorization + if getattr(request.func, "__login_able__", False): if self.has_login: - if "Cookie" not in func.__component_parameter__.header.keys(): - func.__component_parameter__.header["Cookie"] = "" - func.__component_parameter__.header["Cookie"] += self._token - return await func(self, *args, **kwargs) - - return wrapper - - @staticmethod - def logging(func): - @functools.wraps(func) - def wrapper(self: "ChzzkSession", *args, **kwargs): - _log.debug(f"Path({func.__request_path__}) was called.") - return func(self, *args, **kwargs) - - return wrapper + if "Cookie" not in request.headers.keys(): + request.headers["Cookie"] = "" + request.headers["Cookie"] += self._token + elif getattr(request.func, "__login_required__", False): + raise LoginRequired() + return request, path @property def _token(self) -> str: @@ -138,19 +83,15 @@ class ChzzkAPISession(ChzzkSession): def __init__(self, loop: Optional[asyncio.AbstractEventLoop] = None): super().__init__(base_url="https://api.chzzk.naver.com", loop=loop) - @_response_pydantic_model_validation_able - @ChzzkSession.logging - @get("/polling/v2/channels/{channel_id}/live-status") - @_response_pydantic_model_validation + @get_pydantic_response_model() + @get("/polling/v2/channels/{channel_id}/live-status", directly_response=True) async def live_status( self, channel_id: Annotated[str, Path] ) -> Content[LiveStatus]: pass - @_response_pydantic_model_validation_able - @ChzzkSession.logging - @get("/service/v2/channels/{channel_id}/live-detail") - @_response_pydantic_model_validation + @get_pydantic_response_model() + @get("/service/v2/channels/{channel_id}/live-detail", directly_response=True) async def live_detail( self, channel_id: Annotated[str, Path] ) -> Content[LiveDetail]: @@ -161,11 +102,8 @@ class NaverGameAPISession(ChzzkSession): def __init__(self, loop: Optional[asyncio.AbstractEventLoop] = None): super().__init__(base_url="https://comm-api.game.naver.com", loop=loop) - @_response_pydantic_model_validation_able - @ChzzkSession.logging - @ChzzkSession.login_required - @ChzzkSession.login_able - @get("/nng_main/v1/user/getUserStatus") - @_response_pydantic_model_validation + @get_pydantic_response_model() + @get("/nng_main/v1/user/getUserStatus", directly_response=True) + @ChzzkSession.configuration(login_able=True, login_required=True) async def user(self) -> Content[User]: pass diff --git a/requirements.txt b/requirements.txt index e1182c9..5cb3235 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ # # pip-compile --output-file=requirements.txt requirements.in # -ahttp-client==1.0.0 +ahttp-client==1.0.1 # via -r requirements.in aiohttp==3.9.3 # via @@ -49,13 +49,13 @@ multidict==6.0.5 # ahttp-client # aiohttp # yarl -pydantic==2.6.2 +pydantic==2.6.3 # via -r requirements.in pydantic-core==2.16.3 # via # -r requirements.in # pydantic -typing-extensions==4.9.0 +typing-extensions==4.10.0 # via # -r requirements.in # pydantic From 1aec4cc7ff1299223db55cb495bd1b5d3c719bb6 Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Tue, 5 Mar 2024 22:43:30 +0900 Subject: [PATCH 4/5] [Deploy] Deploy v0.0.4 --- chzzkpy/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chzzkpy/__init__.py b/chzzkpy/__init__.py index dad07ed..d55671a 100644 --- a/chzzkpy/__init__.py +++ b/chzzkpy/__init__.py @@ -39,7 +39,7 @@ __author__ = "gunyu1019" __license__ = "MIT" __copyright__ = "Copyright 2024-present gunyu1019" -__version__ = "0.0.3-alpha1" # version_info.to_string() +__version__ = "0.0.4-alpha1" # version_info.to_string() class VersionInfo(NamedTuple): @@ -57,5 +57,5 @@ def to_string(self) -> str: version_info: VersionInfo = VersionInfo( - major=0, minor=0, micro=3, release_level="alpha", serial=1 + major=0, minor=0, micro=4, release_level="alpha", serial=1 ) From a81dec6f8de30ca602755b885f0192d2115d9daa Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Tue, 5 Mar 2024 22:45:28 +0900 Subject: [PATCH 5/5] [Formatting] Formatting with black --- chzzkpy/chat/http.py | 5 +---- chzzkpy/http.py | 5 ++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/chzzkpy/chat/http.py b/chzzkpy/chat/http.py index b54e7c8..d55684c 100644 --- a/chzzkpy/chat/http.py +++ b/chzzkpy/chat/http.py @@ -4,10 +4,7 @@ from .access_token import AccessToken from ..base_model import Content -from ..http import ( - ChzzkSession, - NaverGameAPISession -) +from ..http import ChzzkSession, NaverGameAPISession class ChzzkChatSession(NaverGameAPISession): diff --git a/chzzkpy/http.py b/chzzkpy/http.py index 18cd84b..357fbb3 100644 --- a/chzzkpy/http.py +++ b/chzzkpy/http.py @@ -59,9 +59,12 @@ def decorator(func): func.__login_able__ = login_able func.__login_required__ = login_required return func + return decorator - async def before_request(self, request: RequestCore, path: str) -> tuple[RequestCore, str]: + async def before_request( + self, request: RequestCore, path: str + ) -> tuple[RequestCore, str]: _log.debug(f"Path({path}) was called.") # Authorization