aioqzone 1.9.8.dev1__tar.gz → 1.10.2.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/PKG-INFO +3 -3
- aioqzone-1.10.2.dev1/pyproject.toml +102 -0
- aioqzone-1.9.8.dev1/pyproject.toml → aioqzone-1.10.2.dev1/pyproject.toml.orig +5 -6
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/api/h5/model.py +26 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/api/login/__init__.py +23 -2
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/model/api/__init__.py +13 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/model/api/request.py +17 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/model/api/response.py +116 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/utils/jsjson.py +4 -3
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/README.md +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/__init__.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/api/__init__.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/api/h5/__init__.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/api/login/_base.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/exception.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/message.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/model/__init__.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/model/api/feed.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/model/api/profile.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/model/protocol/__init__.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/model/protocol/config.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/model/protocol/entity.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/utils/__init__.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/utils/entity.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/utils/regex.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/utils/retry.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/aioqzone/utils/time.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/__init__.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/base.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/constant.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/exception.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/message.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/py.typed +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/qr/__init__.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/qr/type.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/type.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/up/__init__.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/up/_model.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/up/captcha/__init__.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/up/captcha/_model.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/up/captcha/capsess.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/up/captcha/click/__init__.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/up/captcha/click/_types.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/up/captcha/pil_utils.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/up/captcha/select/__init__.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/up/captcha/select/_types.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/up/captcha/slide/__init__.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/up/captcha/slide/_types.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/up/encrypt.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/up/h5.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/up/web.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/utils/encrypt.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/utils/iter.py +0 -0
- {aioqzone-1.9.8.dev1 → aioqzone-1.10.2.dev1}/src/qqqr/utils/net.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aioqzone
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.10.2.dev1
|
|
4
4
|
Summary: A Python wrapper for Qzone login and H5 APIs.
|
|
5
5
|
Keywords: qzone-api,autologin,asyncio-spider
|
|
6
6
|
Author: aioqzone
|
|
@@ -15,12 +15,12 @@ Requires-Dist: pydantic-settings>=2.9.0
|
|
|
15
15
|
Requires-Dist: rsa>=4.9.1
|
|
16
16
|
Requires-Dist: tenacity>=8.2.3,<10.0.0
|
|
17
17
|
Requires-Dist: tylisten>=2.2.1
|
|
18
|
-
Requires-Dist: pillow>=
|
|
18
|
+
Requires-Dist: pillow>=13.0.0
|
|
19
19
|
Requires-Dist: pychaosvm~=0.5.4
|
|
20
20
|
Requires-Dist: slide-tc~=0.1.4 ; extra == 'slide-captcha'
|
|
21
21
|
Maintainer: JamzumSum
|
|
22
22
|
Maintainer-email: JamzumSum <22952836+JamzumSum@users.noreply.github.com>
|
|
23
|
-
Requires-Python: >=3.
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
24
|
Project-URL: Homepage, https://github.com/aioqzone/aioqzone
|
|
25
25
|
Project-URL: Repository, https://github.com/aioqzone/aioqzone
|
|
26
26
|
Project-URL: Documentation, https://aioqzone.github.io/aioqzone
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "aioqzone"
|
|
3
|
+
version = "1.10.2.dev1"
|
|
4
|
+
description = "A Python wrapper for Qzone login and H5 APIs."
|
|
5
|
+
license = "AGPL-3.0-or-later"
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
keywords = [
|
|
9
|
+
"qzone-api",
|
|
10
|
+
"autologin",
|
|
11
|
+
"asyncio-spider",
|
|
12
|
+
]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
17
|
+
"Typing :: Typed",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"aiohttp~=3.14",
|
|
21
|
+
"pydantic~=2.12",
|
|
22
|
+
"pydantic-settings>=2.9.0",
|
|
23
|
+
"rsa>=4.9.1",
|
|
24
|
+
"tenacity>=8.2.3,<10.0.0",
|
|
25
|
+
"tylisten>=2.2.1",
|
|
26
|
+
"pillow>=13.0.0",
|
|
27
|
+
"pychaosvm~=0.5.4",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[[project.authors]]
|
|
31
|
+
name = "aioqzone"
|
|
32
|
+
|
|
33
|
+
[[project.maintainers]]
|
|
34
|
+
name = "JamzumSum"
|
|
35
|
+
email = "22952836+JamzumSum@users.noreply.github.com"
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://github.com/aioqzone/aioqzone"
|
|
39
|
+
Repository = "https://github.com/aioqzone/aioqzone"
|
|
40
|
+
Documentation = "https://aioqzone.github.io/aioqzone"
|
|
41
|
+
"Bug Tracker" = "https://github.com/aioqzone/aioqzone/issues"
|
|
42
|
+
Discussion = "https://t.me/aioqzone_chatroom"
|
|
43
|
+
|
|
44
|
+
[project.optional-dependencies]
|
|
45
|
+
slide-captcha = ["slide-tc~=0.1.4"]
|
|
46
|
+
|
|
47
|
+
[dependency-groups]
|
|
48
|
+
test = [
|
|
49
|
+
"pytest>=9.0.3",
|
|
50
|
+
"pytest-asyncio~=1.1",
|
|
51
|
+
]
|
|
52
|
+
dev = [
|
|
53
|
+
"pre-commit",
|
|
54
|
+
"ruff",
|
|
55
|
+
"ty",
|
|
56
|
+
]
|
|
57
|
+
doc = [
|
|
58
|
+
"Sphinx~=9.0",
|
|
59
|
+
"autodoc-pydantic>=2.2.0",
|
|
60
|
+
"sphinx-autodoc-typehints~=3.0",
|
|
61
|
+
"furo",
|
|
62
|
+
"sphinx-intl",
|
|
63
|
+
"sphinx-inline-tabs",
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
[tool.uv]
|
|
67
|
+
prerelease = "if-necessary-or-explicit"
|
|
68
|
+
|
|
69
|
+
[tool.uv.sources.pychaosvm]
|
|
70
|
+
index = "aioqzone-index"
|
|
71
|
+
|
|
72
|
+
[tool.uv.sources.slide-tc]
|
|
73
|
+
index = "aioqzone-index"
|
|
74
|
+
|
|
75
|
+
[[tool.uv.index]]
|
|
76
|
+
name = "aioqzone-index"
|
|
77
|
+
url = "https://aioqzone.github.io/aioqzone-index/simple/"
|
|
78
|
+
|
|
79
|
+
[tool.uv.build-backend]
|
|
80
|
+
module-name = [
|
|
81
|
+
"aioqzone",
|
|
82
|
+
"qqqr",
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
[tool.pytest.ini_options]
|
|
86
|
+
pythonpath = "src"
|
|
87
|
+
log_cli = true
|
|
88
|
+
log_cli_level = "WARNING"
|
|
89
|
+
asyncio_default_fixture_loop_scope = "module"
|
|
90
|
+
|
|
91
|
+
[tool.ruff]
|
|
92
|
+
line-length = 99
|
|
93
|
+
|
|
94
|
+
[tool.ruff.lint]
|
|
95
|
+
ignore = [
|
|
96
|
+
"F403",
|
|
97
|
+
"F405",
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
[build-system]
|
|
101
|
+
requires = ["uv_build"]
|
|
102
|
+
build-backend = "uv_build"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "aioqzone"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.10.2.dev1"
|
|
4
4
|
description = "A Python wrapper for Qzone login and H5 APIs."
|
|
5
5
|
authors = [{ name = "aioqzone" }]
|
|
6
6
|
maintainers = [
|
|
@@ -8,7 +8,7 @@ maintainers = [
|
|
|
8
8
|
]
|
|
9
9
|
license = "AGPL-3.0-or-later"
|
|
10
10
|
readme = "README.md"
|
|
11
|
-
requires-python = ">=3.
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
12
|
keywords = ["qzone-api", "autologin", "asyncio-spider"]
|
|
13
13
|
classifiers = [
|
|
14
14
|
"Development Status :: 4 - Beta",
|
|
@@ -25,7 +25,7 @@ dependencies = [
|
|
|
25
25
|
"tenacity>=8.2.3,<10.0.0",
|
|
26
26
|
# "exceptiongroup>=1.1.1",
|
|
27
27
|
"tylisten>=2.2.1",
|
|
28
|
-
"pillow>=
|
|
28
|
+
"pillow>=13.0.0",
|
|
29
29
|
"pychaosvm~=0.5.4",
|
|
30
30
|
]
|
|
31
31
|
|
|
@@ -37,15 +37,14 @@ Documentation = "https://aioqzone.github.io/aioqzone"
|
|
|
37
37
|
"Discussion" = "https://t.me/aioqzone_chatroom"
|
|
38
38
|
|
|
39
39
|
[dependency-groups]
|
|
40
|
-
test = ["pytest>=9.0.3", "pytest-asyncio
|
|
40
|
+
test = ["pytest>=9.0.3", "pytest-asyncio~=1.1"]
|
|
41
41
|
dev = [
|
|
42
42
|
"pre-commit",
|
|
43
43
|
"ruff",
|
|
44
44
|
"ty",
|
|
45
45
|
]
|
|
46
46
|
doc = [
|
|
47
|
-
"Sphinx
|
|
48
|
-
"Sphinx~=9.0; python_version>='3.11'",
|
|
47
|
+
"Sphinx~=9.0",
|
|
49
48
|
"autodoc-pydantic>=2.2.0",
|
|
50
49
|
"sphinx-autodoc-typehints~=3.0",
|
|
51
50
|
"furo",
|
|
@@ -410,3 +410,29 @@ class QzoneH5API:
|
|
|
410
410
|
params=SetTopParams.model_validate(locals()),
|
|
411
411
|
)
|
|
412
412
|
)
|
|
413
|
+
|
|
414
|
+
async def get_visitor(self, page: int = 1) -> VisitorResp:
|
|
415
|
+
"""Get today / total visitor count.
|
|
416
|
+
|
|
417
|
+
:param page: page number
|
|
418
|
+
|
|
419
|
+
.. note::
|
|
420
|
+
|
|
421
|
+
Contributed by hexadecimal233.
|
|
422
|
+
"""
|
|
423
|
+
return await self.call(VisitorApi(params=VisitorParams.model_validate(locals())))
|
|
424
|
+
|
|
425
|
+
async def get_message_board(
|
|
426
|
+
self, hostUin: int, num: int = 10, start: int = 0
|
|
427
|
+
) -> MessageBoardResp:
|
|
428
|
+
"""Get message board messages.
|
|
429
|
+
|
|
430
|
+
:param hostUin: QQ of the profile owner
|
|
431
|
+
:param num: number of messages to fetch
|
|
432
|
+
:param start: start offset
|
|
433
|
+
|
|
434
|
+
.. note::
|
|
435
|
+
|
|
436
|
+
Contributed by hexadecimal233.
|
|
437
|
+
"""
|
|
438
|
+
return await self.call(MessageBoardApi(params=MessageBoardParams.model_validate(locals())))
|
|
@@ -8,7 +8,14 @@ import logging
|
|
|
8
8
|
import typing as t
|
|
9
9
|
|
|
10
10
|
from aiohttp import ClientError
|
|
11
|
-
from tenacity import
|
|
11
|
+
from tenacity import (
|
|
12
|
+
RetryCallState,
|
|
13
|
+
TryAgain,
|
|
14
|
+
retry,
|
|
15
|
+
retry_if_exception_type,
|
|
16
|
+
stop_after_attempt,
|
|
17
|
+
wait_exponential,
|
|
18
|
+
)
|
|
12
19
|
from tylisten import FutureStore
|
|
13
20
|
from typing_extensions import Self
|
|
14
21
|
|
|
@@ -31,6 +38,20 @@ __all__ = [
|
|
|
31
38
|
]
|
|
32
39
|
|
|
33
40
|
|
|
41
|
+
def _reraise_last_attempt(retry_state: RetryCallState) -> t.NoReturn:
|
|
42
|
+
"""Re-raise the last attempt exception instead of tenacity's ``RetryError``.
|
|
43
|
+
|
|
44
|
+
tenacity >= 9.2.1 unwraps ``TryAgain`` to its cause when ``reraise=True``,
|
|
45
|
+
which would surface the underlying ``ClientError`` (or ``GeneratorExit``)
|
|
46
|
+
rather than the documented :exc:`TryAgain`. Re-raising the attempt's own
|
|
47
|
+
exception keeps the public contract stable across tenacity versions.
|
|
48
|
+
"""
|
|
49
|
+
outcome = retry_state.outcome
|
|
50
|
+
if outcome is None:
|
|
51
|
+
raise RuntimeError("retry outcome is unexpectedly None")
|
|
52
|
+
raise outcome.result()
|
|
53
|
+
|
|
54
|
+
|
|
34
55
|
@t.final
|
|
35
56
|
class ConstLoginMan(Loginable):
|
|
36
57
|
"""A basic login manager which uses external provided cookie."""
|
|
@@ -99,7 +120,7 @@ class UpLoginManager(Loginable):
|
|
|
99
120
|
stop=stop_after_attempt(2),
|
|
100
121
|
wait=wait_exponential(min=1, max=6),
|
|
101
122
|
retry=retry_if_exception_type(TryAgain),
|
|
102
|
-
|
|
123
|
+
retry_error_callback=_reraise_last_attempt,
|
|
103
124
|
)
|
|
104
125
|
async def _new_cookie(self) -> t.Dict[str, str]:
|
|
105
126
|
"""
|
|
@@ -197,3 +197,16 @@ class SetTopApi(QzoneApi[SetTopParams, SingleReturnResp]):
|
|
|
197
197
|
http_method: t.ClassVar[TyHttpMethod] = "POST"
|
|
198
198
|
host: t.ClassVar[str] = "user.qzone.qq.com"
|
|
199
199
|
path: t.ClassVar[str] = "proxy/domain/ic2.qzone.qq.com/cgi-bin/feeds/cgi_settopfeed"
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class VisitorApi(QzoneApi[VisitorParams, VisitorResp]):
|
|
203
|
+
response: t.ClassVar = VisitorResp
|
|
204
|
+
http_method: t.ClassVar[TyHttpMethod] = "GET"
|
|
205
|
+
path: t.ClassVar[str] = "/proxy/domain/g.qzone.qq.com/cgi-bin/friendshow/cgi_get_visitor_more"
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class MessageBoardApi(QzoneApi[MessageBoardParams, MessageBoardResp]):
|
|
209
|
+
response: t.ClassVar = MessageBoardResp
|
|
210
|
+
http_method: t.ClassVar[TyHttpMethod] = "GET"
|
|
211
|
+
host: t.ClassVar[str] = "https://user.qzone.qq.com"
|
|
212
|
+
path: t.ClassVar[str] = "/proxy/domain/m.qzone.qq.com/cgi-bin/new/get_msgb"
|
|
@@ -39,6 +39,8 @@ __all__ = [
|
|
|
39
39
|
"UgcRight",
|
|
40
40
|
"PhotoData",
|
|
41
41
|
"SetTopParams",
|
|
42
|
+
"VisitorParams",
|
|
43
|
+
"MessageBoardParams",
|
|
42
44
|
]
|
|
43
45
|
|
|
44
46
|
|
|
@@ -320,3 +322,18 @@ class SetTopParams(QzoneRequestParams):
|
|
|
320
322
|
@field_serializer("set_top", return_type=str)
|
|
321
323
|
def serialize_set_top(self, set_top: bool) -> str:
|
|
322
324
|
return "set" if set_top else "cancel"
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
class VisitorParams(QzoneRequestParams):
|
|
328
|
+
uin_fields = ("uin",)
|
|
329
|
+
mask: int = 7
|
|
330
|
+
page: int = 1
|
|
331
|
+
fupdate: int = 1
|
|
332
|
+
clear: int = 1
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
class MessageBoardParams(QzoneRequestParams):
|
|
336
|
+
uin_fields = ("uin",)
|
|
337
|
+
hostUin: int
|
|
338
|
+
num: int = 10
|
|
339
|
+
start: int = 0
|
|
@@ -15,6 +15,7 @@ from pydantic import (
|
|
|
15
15
|
AliasChoices,
|
|
16
16
|
AliasPath,
|
|
17
17
|
BaseModel,
|
|
18
|
+
ConfigDict,
|
|
18
19
|
Field,
|
|
19
20
|
HttpUrl,
|
|
20
21
|
TypeAdapter,
|
|
@@ -55,6 +56,14 @@ __all__ = [
|
|
|
55
56
|
"PicInfo",
|
|
56
57
|
"ProfileFeedData",
|
|
57
58
|
"AvatarResponse",
|
|
59
|
+
"VisitorResp",
|
|
60
|
+
"VisitorUser",
|
|
61
|
+
"VisitorItem",
|
|
62
|
+
"ShuoshuoSnapshot",
|
|
63
|
+
"ReplyItem",
|
|
64
|
+
"CommentListItem",
|
|
65
|
+
"AuthorInfo",
|
|
66
|
+
"MessageBoardResp",
|
|
58
67
|
]
|
|
59
68
|
|
|
60
69
|
validate_strdict = TypeAdapter(StrDict).validate_python
|
|
@@ -361,3 +370,110 @@ class AvatarResponse(QzoneResponse):
|
|
|
361
370
|
@classmethod
|
|
362
371
|
async def response_to_object(cls, response: ClientResponse) -> "StrDict":
|
|
363
372
|
return {"avatar": await response.content.read()}
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
class VisitorUser(BaseModel):
|
|
376
|
+
uin: int
|
|
377
|
+
src: int
|
|
378
|
+
platform_src: int
|
|
379
|
+
service_src: int
|
|
380
|
+
time: int
|
|
381
|
+
hide_from: int
|
|
382
|
+
is_hide_visit: int
|
|
383
|
+
name: str
|
|
384
|
+
yellow: int = -1
|
|
385
|
+
supervip: int = 0
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
class ShuoshuoSnapshot(BaseModel):
|
|
389
|
+
model_config = ConfigDict(union_mode="left_to_right")
|
|
390
|
+
|
|
391
|
+
id: str
|
|
392
|
+
src: int
|
|
393
|
+
platform_src: int
|
|
394
|
+
service_src: int
|
|
395
|
+
vtime: int
|
|
396
|
+
url: t.Union[HttpUrl, str]
|
|
397
|
+
imgsrc: t.Union[HttpUrl, str]
|
|
398
|
+
name: str
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
class VisitorItem(BaseModel):
|
|
402
|
+
uin: int
|
|
403
|
+
src: int
|
|
404
|
+
platform_src: int
|
|
405
|
+
service_src: int
|
|
406
|
+
time: int
|
|
407
|
+
hide_from: int
|
|
408
|
+
is_hide_visit: int
|
|
409
|
+
name: str
|
|
410
|
+
yellow: int = -1
|
|
411
|
+
supervip: int = 0
|
|
412
|
+
uins: t.List[VisitorUser] = []
|
|
413
|
+
shuoshuoes: t.List[ShuoshuoSnapshot] = []
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
class VisitorResp(QzoneResponse):
|
|
417
|
+
Ishost: int = 0
|
|
418
|
+
page: int = 1
|
|
419
|
+
totalpage: int = 1
|
|
420
|
+
todaycount: int = 0
|
|
421
|
+
totalcount: int = 0
|
|
422
|
+
noright_todaycount: int = 0
|
|
423
|
+
noright_totalcount: int = 0
|
|
424
|
+
items: t.List[VisitorItem] = []
|
|
425
|
+
|
|
426
|
+
@classmethod
|
|
427
|
+
async def response_to_object(cls, response: ClientResponse) -> StrDict:
|
|
428
|
+
m = response_callback.search(await response.text())
|
|
429
|
+
if not m:
|
|
430
|
+
raise TryAgain("VisitorResp: callback not found")
|
|
431
|
+
return validate_strdict(json_loads(m.group(1)))
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
class ReplyItem(BaseModel):
|
|
435
|
+
content: str
|
|
436
|
+
uin: int
|
|
437
|
+
time: int
|
|
438
|
+
nick: str
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
class CommentListItem(BaseModel):
|
|
442
|
+
id: str
|
|
443
|
+
secret: int
|
|
444
|
+
pasterid: str
|
|
445
|
+
bmp: str
|
|
446
|
+
pubtime: str
|
|
447
|
+
modifytime: int
|
|
448
|
+
effect: int
|
|
449
|
+
type: int
|
|
450
|
+
|
|
451
|
+
uin: t.Optional[int] = None
|
|
452
|
+
nickname: t.Optional[str] = None
|
|
453
|
+
capacity: t.Optional[int] = None
|
|
454
|
+
htmlContent: t.Optional[str] = None
|
|
455
|
+
ubbContent: t.Optional[str] = None
|
|
456
|
+
signature: t.Optional[str] = None
|
|
457
|
+
replyList: t.Optional[t.List[ReplyItem]] = Field(default_factory=list)
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
class AuthorInfo(BaseModel):
|
|
461
|
+
msg: str = ""
|
|
462
|
+
htmlMsg: str = ""
|
|
463
|
+
sign: str = ""
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
class MessageBoardResp(QzoneResponse):
|
|
467
|
+
auditNum: t.Optional[int] = None
|
|
468
|
+
auditON: t.Optional[int] = None
|
|
469
|
+
|
|
470
|
+
total: int
|
|
471
|
+
authorInfo: AuthorInfo = Field(default_factory=AuthorInfo)
|
|
472
|
+
commentList: t.List[CommentListItem] = Field(default_factory=list)
|
|
473
|
+
|
|
474
|
+
@classmethod
|
|
475
|
+
async def response_to_object(cls, response: ClientResponse) -> StrDict:
|
|
476
|
+
m = response_callback.search(await response.text())
|
|
477
|
+
if not m:
|
|
478
|
+
raise TryAgain("MessageBoardResp: callback not found")
|
|
479
|
+
return validate_strdict(json_loads(m.group(1)))
|
|
@@ -41,10 +41,11 @@ class AstLoader:
|
|
|
41
41
|
:return: A :obj:`JsonValue` object.
|
|
42
42
|
"""
|
|
43
43
|
js = dedent(js).replace(r"\/", "/")
|
|
44
|
-
node = ast.parse(js, mode="eval")
|
|
44
|
+
node = ast.parse(js, filename=filename, mode="eval")
|
|
45
45
|
node = ast.fix_missing_locations(cls.RewriteUndef().visit(node))
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
# Parse as a data literal only. Never compile/eval the input: it comes from
|
|
47
|
+
# remote responses and must not be executed as Python code.
|
|
48
|
+
return ast.literal_eval(node)
|
|
48
49
|
|
|
49
50
|
|
|
50
51
|
def json_loads(js: str) -> JsonValue:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|