diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 23ca1dab..1265f2b6 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -37,7 +37,7 @@ jobs: ref: develop - name: Setup Rye - uses: eifinger/setup-rye@v2 + uses: eifinger/setup-rye@v3 - name: Pin Py${{ matrix.python-version }} run: | diff --git a/.github/workflows/Pages.yml b/.github/workflows/Pages.yml index f81f3aea..75f32337 100644 --- a/.github/workflows/Pages.yml +++ b/.github/workflows/Pages.yml @@ -25,7 +25,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Rye - uses: eifinger/setup-rye@v2 + uses: eifinger/setup-rye@v3 - name: Install dependencies run: rye sync -q diff --git a/.github/workflows/Publish.yml b/.github/workflows/Publish.yml index bad6d873..e9119bac 100644 --- a/.github/workflows/Publish.yml +++ b/.github/workflows/Publish.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/checkout@v4 - name: Build wheels - uses: pypa/cibuildwheel@v2.17.0 + uses: pypa/cibuildwheel@v2.18.1 - uses: actions/upload-artifact@v4 with: diff --git a/.gitignore b/.gitignore index 2d85a1ab..579de58e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *.py[cd] __pycache__ +log/ dist/ build/ diff --git a/aiotieba/api/_classdef/user.py b/aiotieba/api/_classdef/user.py index 6add3cfb..76a534f5 100644 --- a/aiotieba/api/_classdef/user.py +++ b/aiotieba/api/_classdef/user.py @@ -80,7 +80,7 @@ def __hash__(self) -> int: def __bool__(self) -> bool: return bool(self.user_id) - def __ior__(self, obj: "UserInfo") -> "UserInfo": + def __ior__(self, obj) -> "UserInfo": for field in dcs.fields(obj): if hasattr(self, field.name): val = getattr(obj, field.name) diff --git a/aiotieba/api/get_bawu_postlogs/_api.py b/aiotieba/api/get_bawu_postlogs/_api.py index f20cf797..6c3cddde 100644 --- a/aiotieba/api/get_bawu_postlogs/_api.py +++ b/aiotieba/api/get_bawu_postlogs/_api.py @@ -1,4 +1,5 @@ import datetime +import time from typing import Optional from urllib.parse import quote @@ -38,17 +39,28 @@ async def request( params.append(('op_type', op_type)) if search_value: - search_value = quote(search_value) - extend_params = [ - ('svalue', search_value), - ('stype', 'post_uname' if search_type == BawuSearchType.USER else 'op_uname'), - ] + if search_type == BawuSearchType.USER: + search_value = quote(search_value) + extend_params = [ + ('svalue', search_value), + ('stype', 'post_uname'), + ] + else: + extend_params = [ + ('svalue', search_value), + ('stype', 'op_uname'), + ] params += extend_params - if start_dt or end_dt: + if start_dt: + begin = int(start_dt.timestamp()) + if end_dt is None: + end = int(time.time()) + else: + end = int(end_dt.timestamp()) extend_params = [ - ('end', int(end_dt.timestamp())), - ('begin', int(start_dt.timestamp())), + ('end', end), + ('begin', begin), ] params += extend_params diff --git a/aiotieba/api/get_bawu_userlogs/_api.py b/aiotieba/api/get_bawu_userlogs/_api.py index 5a85fd1c..96cb64b0 100644 --- a/aiotieba/api/get_bawu_userlogs/_api.py +++ b/aiotieba/api/get_bawu_userlogs/_api.py @@ -1,4 +1,5 @@ import datetime +import time from typing import Optional from urllib.parse import quote @@ -38,17 +39,28 @@ async def request( params.append(('op_type', op_type)) if search_value: - search_value = quote(search_value) - extend_params = [ - ('svalue', search_value), - ('stype', 'post_uname' if search_type == BawuSearchType.USER else 'op_uname'), - ] + if search_type == BawuSearchType.USER: + search_value = quote(search_value) + extend_params = [ + ('svalue', search_value), + ('stype', 'post_uname'), + ] + else: + extend_params = [ + ('svalue', search_value), + ('stype', 'op_uname'), + ] params += extend_params - if start_dt or end_dt: + if start_dt: + begin = int(start_dt.timestamp()) + if end_dt is None: + end = int(time.time()) + else: + end = int(end_dt.timestamp()) extend_params = [ - ('begin', int(start_dt.timestamp())), - ('end', int(end_dt.timestamp())), + ('end', end), + ('begin', begin), ] params += extend_params diff --git a/aiotieba/api/get_cid/_api.py b/aiotieba/api/get_cid/_api.py index 688b5649..b452f4bc 100644 --- a/aiotieba/api/get_cid/_api.py +++ b/aiotieba/api/get_cid/_api.py @@ -1,4 +1,4 @@ -from typing import Dict +from typing import Dict, List, Union import yarl @@ -7,8 +7,10 @@ from ...exception import TiebaServerError from ...helper import parse_json +TypeCates = List[Dict[str, Union[str, int]]] -def parse_body(body: bytes) -> Dict[str, str]: + +def parse_body(body: bytes) -> TypeCates: res_json = parse_json(body) if code := int(res_json['error_code']): raise TiebaServerError(code, res_json['error_msg']) @@ -18,7 +20,7 @@ def parse_body(body: bytes) -> Dict[str, str]: return cates -async def request(http_core: HttpCore, fname: str) -> Dict[str, str]: +async def request(http_core: HttpCore, fname: str) -> TypeCates: data = [ ('BDUSS', http_core.account.BDUSS), ('word', fname), diff --git a/aiotieba/api/get_recovers/_classdef.py b/aiotieba/api/get_recovers/_classdef.py index 6dd9bbab..014cfd7d 100644 --- a/aiotieba/api/get_recovers/_classdef.py +++ b/aiotieba/api/get_recovers/_classdef.py @@ -56,7 +56,7 @@ def show_name(self) -> str: @cached_property def log_name(self) -> str: - return self.user_name if self.user_name else f"{self.nick_name_new}/{self.portrait}" + return self.user_name or f"{self.nick_name_new}/{self.portrait}" @dcs.dataclass diff --git a/aiotieba/api/get_replys/_classdef.py b/aiotieba/api/get_replys/_classdef.py index d54a24b8..cb80f3e8 100644 --- a/aiotieba/api/get_replys/_classdef.py +++ b/aiotieba/api/get_replys/_classdef.py @@ -123,7 +123,7 @@ def show_name(self) -> str: @cached_property def log_name(self) -> str: - return self.user_name if self.user_name else f"{self.nick_name_new}/{self.user_id}" + return self.user_name or f"{self.nick_name_new}/{self.user_id}" @dcs.dataclass diff --git a/aiotieba/api/get_selfinfo_initNickname/_classdef.py b/aiotieba/api/get_selfinfo_initNickname/_classdef.py index 2fcb4661..ece72f55 100644 --- a/aiotieba/api/get_selfinfo_initNickname/_classdef.py +++ b/aiotieba/api/get_selfinfo_initNickname/_classdef.py @@ -47,4 +47,4 @@ def nick_name(self) -> str: @cached_property def log_name(self) -> str: - return self.user_name if self.user_name else f"{self.nick_name_old}/{self.tieba_uid}" + return self.user_name or f"{self.nick_name_old}/{self.tieba_uid}" diff --git a/aiotieba/api/get_tab_map/_api.py b/aiotieba/api/get_tab_map/_api.py index 54301752..774be638 100644 --- a/aiotieba/api/get_tab_map/_api.py +++ b/aiotieba/api/get_tab_map/_api.py @@ -1,5 +1,3 @@ -from typing import Dict - import yarl from ...const import APP_BASE_HOST, APP_SECURE_SCHEME, MAIN_VERSION @@ -20,7 +18,7 @@ def pack_proto(account: Account, fname: str) -> bytes: return req_proto.SerializeToString() -def parse_body(body: bytes) -> Dict[str, int]: +def parse_body(body: bytes) -> TabMap: res_proto = SearchPostForumResIdl_pb2.SearchPostForumResIdl() res_proto.ParseFromString(body) @@ -33,7 +31,7 @@ def parse_body(body: bytes) -> Dict[str, int]: return tab_map -async def request_http(http_core: HttpCore, fname: str) -> Dict[str, int]: +async def request_http(http_core: HttpCore, fname: str) -> TabMap: data = pack_proto(http_core.account, fname) request = http_core.pack_proto_request( @@ -47,7 +45,7 @@ async def request_http(http_core: HttpCore, fname: str) -> Dict[str, int]: return parse_body(body) -async def request_ws(ws_core: WsCore, fname: str) -> Dict[str, int]: +async def request_ws(ws_core: WsCore, fname: str) -> TabMap: data = pack_proto(ws_core.account, fname) response = await ws_core.send(data, CMD) diff --git a/aiotieba/api/get_uinfo_panel/_classdef.py b/aiotieba/api/get_uinfo_panel/_classdef.py index fe8aa0d9..4466b0c8 100644 --- a/aiotieba/api/get_uinfo_panel/_classdef.py +++ b/aiotieba/api/get_uinfo_panel/_classdef.py @@ -103,4 +103,4 @@ def show_name(self) -> str: @cached_property def log_name(self) -> str: - return self.user_name if self.user_name else f"{self.nick_name_new}/{self.portrait}" + return self.user_name or f"{self.nick_name_new}/{self.portrait}" diff --git a/aiotieba/client.py b/aiotieba/client.py index 9c0a3212..892f209a 100644 --- a/aiotieba/client.py +++ b/aiotieba/client.py @@ -115,7 +115,6 @@ from .helper.cache import ForumInfoCache from .helper.utils import handle_exception, is_portrait, is_user_name from .logging import get_logger as LOG -from .typing import TypeUserInfo if TYPE_CHECKING: import datetime @@ -152,7 +151,7 @@ class Client: account (Account, optional): Account实例 该字段会覆盖前两个参数. Defaults to None. try_ws (bool, optional): 尝试使用websocket接口. Defaults to False. proxy (bool | ProxyConfig, optional): True则使用环境变量代理 False则禁用代理 输入ProxyConfig实例以手动配置代理. Defaults to False. - timeout (TimeoutConfig, optional): 超时配置. Defaults to TimeoutConfig(). + timeout (TimeoutConfig, optional): 超时配置. Defaults to None. loop (asyncio.AbstractEventLoop, optional): 事件循环. Defaults to None. """ @@ -172,7 +171,7 @@ def __init__( account: Optional[Account] = None, try_ws: bool = False, proxy: Union[bool, ProxyConfig] = False, - timeout: TimeoutConfig = TimeoutConfig, + timeout: Optional[TimeoutConfig] = None, loop: Optional[asyncio.AbstractEventLoop] = None, ) -> None: if loop is None: @@ -633,7 +632,7 @@ async def _get_uinfo_panel(self, name_or_portrait: str) -> get_uinfo_panel.UserI return await get_uinfo_panel.request(self._http_core, name_or_portrait) - async def get_user_info(self, id_: Union[str, int], /, require: ReqUInfo = ReqUInfo.ALL) -> TypeUserInfo: + async def get_user_info(self, id_: Union[str, int], /, require: ReqUInfo = ReqUInfo.ALL) -> UserInfo: """ 获取用户信息 @@ -642,7 +641,7 @@ async def get_user_info(self, id_: Union[str, int], /, require: ReqUInfo = ReqUI require (ReqUInfo): 指示需要获取的字段 Returns: - TypeUserInfo: 用户信息 + UserInfo: 用户信息 """ if not id_: @@ -1834,7 +1833,7 @@ async def __get_cid(self, fname_or_fid: Union[str, int], /, cname: str = '') -> cid = 0 for item in cates: if cname == item['class_name']: - cid = int(item['class_id']) + cid = item['class_id'] break return cid diff --git a/aiotieba/config.py b/aiotieba/config.py index a1ff4326..0ab31086 100644 --- a/aiotieba/config.py +++ b/aiotieba/config.py @@ -48,7 +48,7 @@ class TimeoutConfig: ws_read (float, optional): 从发送websocket数据到结束等待响应的超时时间. Defaults to 8.0. ws_keepalive (float, optional): websocket在长达ws_keepalive的时间内未发生IO则发送close信号关闭连接. Defaults to 300.0. ws_heartbeat (float, optional): websocket心跳间隔. 为None则不发送心跳. Defaults to None. - dns_ttl (float, optional): dns的本地缓存超时时间. Defaults to 600.0. + dns_ttl (int, optional): dns的本地缓存超时时间. Defaults to 600. Note: 所有时间均以秒为单位 @@ -60,7 +60,7 @@ class TimeoutConfig: ws_read: float = 8.0 ws_keepalive: float = 300.0 ws_heartbeat: Optional[float] = None - dns_ttl: float = 600.0 + dns_ttl: int = 600 def __init__( self, @@ -72,7 +72,7 @@ def __init__( ws_read: float = 8.0, ws_keepalive: float = 300.0, ws_heartbeat: Optional[float] = None, - dns_ttl: float = 600.0, + dns_ttl: int = 600, ) -> None: self.http = aiohttp.ClientTimeout(connect=http_acquire_conn, sock_read=http_read, sock_connect=http_connect) self.http_keepalive = http_keepalive diff --git a/aiotieba/const.py b/aiotieba/const.py index d0b3f2ec..1327e33d 100644 --- a/aiotieba/const.py +++ b/aiotieba/const.py @@ -1,4 +1,4 @@ -MAIN_VERSION = "12.59.1.0" +MAIN_VERSION = "12.62.1.0" POST_VERSION = "12.35.1.0" APP_SECURE_SCHEME = "https" diff --git a/aiotieba/core/http.py b/aiotieba/core/http.py index bf6dc30d..e90532cd 100644 --- a/aiotieba/core/http.py +++ b/aiotieba/core/http.py @@ -2,6 +2,7 @@ import dataclasses as dcs import random import urllib.parse +from http.cookies import Morsel from typing import Dict, List, Optional, Tuple import aiohttp @@ -77,11 +78,11 @@ def __init__(self, account: Account, net_core: NetCore, loop: Optional[asyncio.A def set_account(self, new_account: Account) -> None: self.account = new_account - BDUSS_morsel = aiohttp.cookiejar.Morsel() + BDUSS_morsel = Morsel() BDUSS_morsel.set('BDUSS', new_account.BDUSS, new_account.BDUSS) BDUSS_morsel['domain'] = "baidu.com" self.web.cookie_jar._cookies[("baidu.com", "/")]['BDUSS'] = BDUSS_morsel - STOKEN_morsel = aiohttp.cookiejar.Morsel() + STOKEN_morsel = Morsel() STOKEN_morsel.set('STOKEN', new_account.STOKEN, new_account.STOKEN) STOKEN_morsel['domain'] = "tieba.baidu.com" self.web.cookie_jar._cookies[("tieba.baidu.com", "/")]['STOKEN'] = STOKEN_morsel @@ -129,7 +130,7 @@ def pack_proto_request(self, url: yarl.URL, data: bytes) -> aiohttp.ClientReques aiohttp.ClientRequest """ - writer = aiohttp.MultipartWriter('form-data', boundary=f"*-reverse1999-{random.randint(0,9)}") + writer = aiohttp.MultipartWriter('form-data', boundary=f"*-reverse1999-{random.randint(0, 9)}") payload_headers = { aiohttp.hdrs.CONTENT_DISPOSITION: aiohttp.helpers.content_disposition_header( 'form-data', name='data', filename='file' diff --git a/aiotieba/core/net.py b/aiotieba/core/net.py index 6b67c9c6..0a344c1b 100644 --- a/aiotieba/core/net.py +++ b/aiotieba/core/net.py @@ -1,6 +1,6 @@ import asyncio import dataclasses as dcs -from typing import Callable +from typing import Callable, Optional import aiohttp @@ -24,8 +24,8 @@ class NetCore: Args: connector (aiohttp.TCPConnector): 用于生成TCP连接的连接器 - proxy (ProxyConfig, optional): 代理配置. Defaults to ProxyConfig(). - timeout (TimeoutConfig, optional): 超时配置. Defaults to TimeoutConfig(). + proxy (ProxyConfig, optional): 代理配置. Defaults to None. + timeout (TimeoutConfig, optional): 超时配置. Defaults to None. """ connector: aiohttp.TCPConnector @@ -35,8 +35,8 @@ class NetCore: def __init__( self, connector: aiohttp.TCPConnector, - proxy: ProxyConfig = ProxyConfig, - timeout: TimeoutConfig = TimeoutConfig, + proxy: Optional[ProxyConfig] = None, + timeout: Optional[TimeoutConfig] = None, ) -> None: self.connector = connector diff --git a/aiotieba/exception.py b/aiotieba/exception.py index b79a9ccb..db5cdffa 100644 --- a/aiotieba/exception.py +++ b/aiotieba/exception.py @@ -27,7 +27,7 @@ def __bool__(self) -> bool: def __int__(self) -> int: return int(bool(self)) - def __repr__(self) -> int: + def __repr__(self) -> str: return str(bool(self)) def __hash__(self) -> int: diff --git a/aiotieba/helper/crypto/__init__.py b/aiotieba/helper/crypto/__init__.py index f6db35d9..fb2c37e2 100644 --- a/aiotieba/helper/crypto/__init__.py +++ b/aiotieba/helper/crypto/__init__.py @@ -4,7 +4,7 @@ from .crypto import sign as _sign -def sign(data: List[Tuple[str, Union[str, int]]]) -> List[Tuple[str, str]]: +def sign(data: List[Tuple[str, Union[str, int]]]) -> List[Tuple[str, Union[str, int]]]: """ 为参数元组列表添加贴吧客户端签名 @@ -12,7 +12,7 @@ def sign(data: List[Tuple[str, Union[str, int]]]) -> List[Tuple[str, str]]: data (list[tuple[str, str | int]]): 参数元组列表 Returns: - list[tuple[str, str]]: 签名后的form参数元组列表 + list[tuple[str, str | int]]: 签名后的form参数元组列表 """ data.append(('sign', _sign(data))) diff --git a/aiotieba/helper/crypto/crypto.pyi b/aiotieba/helper/crypto/crypto.pyi index 54ae5992..0a277221 100644 --- a/aiotieba/helper/crypto/crypto.pyi +++ b/aiotieba/helper/crypto/crypto.pyi @@ -1,5 +1,3 @@ -from typing import List, Tuple, Union - def cuid_galaxy2(android_id: str) -> str: """ 使用给定的android_id生成cuid_galaxy2 @@ -47,7 +45,7 @@ def rc4_42(xyus_md5_str: str, aes_cbc_sec_key: bytes) -> bytes: bytes """ -def sign(data: List[Tuple[str, Union[str, int]]]) -> str: +def sign(data: list[tuple[str, str | int]]) -> str: """ 为参数元组列表计算贴吧客户端签名 diff --git a/aiotieba/helper/utils.py b/aiotieba/helper/utils.py index 4f6330e4..72873140 100644 --- a/aiotieba/helper/utils.py +++ b/aiotieba/helper/utils.py @@ -110,7 +110,7 @@ def removesuffix(s: str, suffix: str) -> str: return s -def is_portrait(portrait: str) -> bool: +def is_portrait(portrait: Any) -> bool: """ 简单判断输入是否符合portrait格式 """ @@ -118,7 +118,7 @@ def is_portrait(portrait: str) -> bool: return isinstance(portrait, str) and portrait.startswith('tb.') -def is_user_name(user_name: str) -> bool: +def is_user_name(user_name: Any) -> bool: """ 简单判断输入是否符合user_name格式 """ diff --git a/aiotieba/logging.py b/aiotieba/logging.py index 0a1f5623..a7112cfe 100644 --- a/aiotieba/logging.py +++ b/aiotieba/logging.py @@ -32,7 +32,7 @@ def __init__(self, name: str = '', stream_log_level: int = logging.DEBUG) -> Non self.addHandler(stream_hd) -LOGGER = TiebaLogger() +LOGGER = None def get_logger() -> TiebaLogger: @@ -100,11 +100,13 @@ def enable_filelog(log_level: int = logging.INFO, log_dir: Path = Path('log'), b log_dir = Path(log_dir) log_dir.mkdir(0o755, parents=True, exist_ok=True) + logger = get_logger() + file_hd = logging.handlers.TimedRotatingFileHandler( - log_dir / f"{LOGGER.name}.log", when='MIDNIGHT', backupCount=backup_count, encoding='utf-8' + log_dir / f"{logger.name}.log", when='MIDNIGHT', backupCount=backup_count, encoding='utf-8' ) file_hd.setLevel(log_level) file_hd.setFormatter(_FORMATTER) - LOGGER.addHandler(file_hd) + logger.addHandler(file_hd) _FILELOG_ENABLED = True diff --git a/aiotieba/typing.py b/aiotieba/typing.py index 5b928422..f3f6d11f 100644 --- a/aiotieba/typing.py +++ b/aiotieba/typing.py @@ -2,6 +2,5 @@ from .api.get_comments import Comment, Comments from .api.get_posts import Post, Posts from .api.get_threads import Thread, Threads -from .api.profile import UserInfo_pf -TypeUserInfo = UserInfo_pf +TypeUserInfo = UserInfo diff --git a/pyproject.toml b/pyproject.toml index d89eda8e..548ecac5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "aiotieba" -version = "4.4.4" +version = "4.4.5" description = "Asynchronous I/O Client for Baidu Tieba" authors = [{ name = "Starry-OvO", email = "starry.qvq@gmail.com" }] urls = { Repository = "https://github.com/Starry-OvO/aiotieba/", Documentation = "https://aiotieba.cc/" } @@ -32,7 +32,7 @@ dependencies = [ "lxml>=4.6.4,<6;python_version=='3.10'", "lxml>=4.9.2,<6;python_version=='3.11'", "lxml>=4.9.3,<6;python_version>='3.12'", - "protobuf>=4.21.0,<5", + "protobuf>=4.21.0,<6", "cryptography>=35.0.0,<43", "async-timeout>=4.0,<5;python_version<'3.11'", "StrEnum>=0.4.0,<0.5;python_version<'3.11'", @@ -65,8 +65,8 @@ build-backend = "scikit_build_core.build" managed = true dev-dependencies = [ "aiotieba[speedup]", - "pytest==8.1.1", - "pytest-asyncio==0.23.6", + "pytest==8.2.1", + "pytest-asyncio==0.23.7", "pytest-rerunfailures==14.0", "mkdocs-material", "mkdocstrings[python]", @@ -87,13 +87,14 @@ skip = "*-win32 *_i686 *_s390x *_ppc64le" [tool.ruff] line-length = 120 target-version = "py38" +preview = true [tool.ruff.format] quote-style = "preserve" [tool.ruff.lint] select = ["W", "E", "F", "I", "UP", "YTT", "A", "B", "C4", "PIE", "PT", "PERF", "FURB"] -ignore = ["E402", "E501"] +ignore = ["E402", "E501", "E266"] [tool.ruff.lint.per-file-ignores] "__init__.py" = ["F401"]