| import hashlib |
| from enum import Enum |
| from random import randint |
| from typing import Any, Optional |
|
|
| from hibiapi.utils.config import APIConfig |
| from hibiapi.utils.net import catch_network_error |
| from hibiapi.utils.routing import BaseEndpoint, dont_route |
|
|
| Config = APIConfig("tieba") |
|
|
|
|
| class TiebaSignUtils: |
| salt = b"tiebaclient!!!" |
|
|
| @staticmethod |
| def random_digit(length: int) -> str: |
| return "".join(map(str, [randint(0, 9) for _ in range(length)])) |
|
|
| @staticmethod |
| def construct_content(params: dict[str, Any]) -> bytes: |
| |
| |
| return b"&".join( |
| map( |
| lambda k, v: ( |
| k.encode() |
| + b"=" |
| + str(v.value if isinstance(v, Enum) else v).encode() |
| ), |
| params.keys(), |
| params.values(), |
| ) |
| ) |
|
|
| @classmethod |
| def sign(cls, params: dict[str, Any]) -> bytes: |
| params.update( |
| { |
| "_client_id": ( |
| "wappc_" + cls.random_digit(13) + "_" + cls.random_digit(3) |
| ), |
| "_client_type": 2, |
| "_client_version": "9.9.8.32", |
| **{ |
| k.upper(): str(v).strip() |
| for k, v in Config["net"]["params"].as_dict().items() |
| if v |
| }, |
| } |
| ) |
| params = {k: params[k] for k in sorted(params.keys())} |
| params["sign"] = ( |
| hashlib.md5(cls.construct_content(params).replace(b"&", b"") + cls.salt) |
| .hexdigest() |
| .upper() |
| ) |
| return cls.construct_content(params) |
|
|
|
|
| class TiebaEndpoint(BaseEndpoint): |
| base = "http://c.tieba.baidu.com" |
|
|
| @dont_route |
| @catch_network_error |
| async def request( |
| self, endpoint: str, *, params: Optional[dict[str, Any]] = None |
| ) -> dict[str, Any]: |
| response = await self.client.post( |
| url=self._join(self.base, endpoint, {}), |
| content=TiebaSignUtils.sign(params or {}), |
| ) |
| response.raise_for_status() |
| return response.json() |
|
|
| async def post_list(self, *, name: str, page: int = 1, size: int = 50): |
| return await self.request( |
| "c/f/frs/page", |
| params={ |
| "kw": name, |
| "pn": page, |
| "rn": size, |
| }, |
| ) |
|
|
| async def post_detail( |
| self, |
| *, |
| tid: int, |
| page: int = 1, |
| size: int = 50, |
| reversed: bool = False, |
| ): |
| return await self.request( |
| "c/f/pb/page", |
| params={ |
| **({"last": 1, "r": 1} if reversed else {}), |
| "kz": tid, |
| "pn": page, |
| "rn": size, |
| }, |
| ) |
|
|
| async def subpost_detail( |
| self, |
| *, |
| tid: int, |
| pid: int, |
| page: int = 1, |
| size: int = 50, |
| ): |
| return await self.request( |
| "c/f/pb/floor", |
| params={ |
| "kz": tid, |
| "pid": pid, |
| "pn": page, |
| "rn": size, |
| }, |
| ) |
|
|
| async def user_profile(self, *, uid: int): |
| return await self.request( |
| "c/u/user/profile", |
| params={ |
| "uid": uid, |
| "need_post_count": 1, |
| "has_plist": 1, |
| }, |
| ) |
|
|
| async def user_subscribed( |
| self, *, uid: int, page: int = 1 |
| ): |
| return await self.request( |
| "c/f/forum/like", |
| params={ |
| "is_guest": 0, |
| "uid": uid, |
| "page_no": page, |
| }, |
| ) |
|
|