Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Deploy] Deploy v0.0.4 #6

Merged
merged 5 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions chzzkpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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
)
22 changes: 7 additions & 15 deletions chzzkpy/chat/http.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
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,
)
from ..http import ChzzkSession, 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
4 changes: 2 additions & 2 deletions chzzkpy/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
115 changes: 28 additions & 87 deletions chzzkpy/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -98,36 +54,28 @@ 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()
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 await func(self, *args, **kwargs)
return decorator

return wrapper
async def before_request(
self, request: RequestCore, path: str
) -> tuple[RequestCore, str]:
_log.debug(f"Path({path}) was called.")

@staticmethod
def login_able(func):
@functools.wraps(func)
async def wrapper(self: "ChzzkSession", *args, **kwargs):
# 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:
Expand All @@ -138,19 +86,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]:
Expand All @@ -161,11 +105,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
2 changes: 1 addition & 1 deletion chzzkpy/live.py
Original file line number Diff line number Diff line change
Expand Up @@ -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: ???
Expand Down
10 changes: 5 additions & 5 deletions requirements.in
Original file line number Diff line number Diff line change
@@ -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
32 changes: 16 additions & 16 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,64 +4,64 @@
#
# pip-compile --output-file=requirements.txt requirements.in
#
aiohttp==3.9.1
ahttp-client==1.0.1
# 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.3
# via -r requirements.in
pydantic-core==2.16.2
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
# pydantic-core
yarl==1.9.4
# via
# -r requirements.in
# ahttp-client
# aiohttp
# async-client-decorator
Loading