aioqzone 1.9.2.dev2__tar.gz → 1.9.3.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.
Files changed (54) hide show
  1. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/PKG-INFO +2 -2
  2. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/pyproject.toml +2 -2
  3. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/api/h5/model.py +21 -7
  4. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/model/api/__init__.py +10 -1
  5. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/model/api/request.py +6 -0
  6. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/model/api/response.py +13 -0
  7. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/LICENSE +0 -0
  8. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/README.md +0 -0
  9. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/__init__.py +0 -0
  10. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/api/__init__.py +0 -0
  11. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/api/h5/__init__.py +0 -0
  12. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/api/login/__init__.py +0 -0
  13. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/api/login/_base.py +0 -0
  14. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/exception.py +0 -0
  15. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/message.py +0 -0
  16. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/model/__init__.py +0 -0
  17. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/model/api/feed.py +0 -0
  18. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/model/api/profile.py +0 -0
  19. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/model/protocol/__init__.py +0 -0
  20. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/model/protocol/config.py +0 -0
  21. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/model/protocol/entity.py +0 -0
  22. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/utils/__init__.py +0 -0
  23. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/utils/entity.py +0 -0
  24. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/utils/regex.py +0 -0
  25. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/utils/retry.py +0 -0
  26. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/aioqzone/utils/time.py +0 -0
  27. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/__init__.py +0 -0
  28. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/base.py +0 -0
  29. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/constant.py +0 -0
  30. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/exception.py +0 -0
  31. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/message.py +0 -0
  32. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/py.typed +0 -0
  33. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/qr/__init__.py +0 -0
  34. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/qr/type.py +0 -0
  35. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/type.py +0 -0
  36. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/up/__init__.py +0 -0
  37. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/up/_model.py +0 -0
  38. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/up/captcha/__init__.py +0 -0
  39. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/up/captcha/_model.py +0 -0
  40. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/up/captcha/capsess.py +0 -0
  41. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/up/captcha/click/__init__.py +0 -0
  42. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/up/captcha/click/_types.py +0 -0
  43. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/up/captcha/pil_utils.py +0 -0
  44. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/up/captcha/select/__init__.py +0 -0
  45. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/up/captcha/select/_types.py +0 -0
  46. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/up/captcha/slide/__init__.py +0 -0
  47. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/up/captcha/slide/_types.py +0 -0
  48. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/up/encrypt.py +0 -0
  49. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/up/h5.py +0 -0
  50. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/up/web.py +0 -0
  51. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/utils/encrypt.py +0 -0
  52. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/utils/iter.py +0 -0
  53. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/utils/jsjson.py +0 -0
  54. {aioqzone-1.9.2.dev2 → aioqzone-1.9.3.dev1}/src/qqqr/utils/net.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: aioqzone
3
- Version: 1.9.2.dev2
3
+ Version: 1.9.3.dev1
4
4
  Summary: A Python wrapper for Qzone login and H5 APIs.
5
5
  License: AGPL-3.0
6
6
  Keywords: qzone-api,autologin,asyncio-spider
@@ -22,7 +22,7 @@ Provides-Extra: slide-captcha
22
22
  Requires-Dist: aiohttp (>=3.9.0,<4.0.0)
23
23
  Requires-Dist: exceptiongroup (>=1.1.1,<2.0.0)
24
24
  Requires-Dist: pillow (>=10.1.0,<11.0.0)
25
- Requires-Dist: pychaosvm (>=0.4.1,<0.5.0)
25
+ Requires-Dist: pychaosvm (>=0.4.3,<0.5.0)
26
26
  Requires-Dist: pydantic (>=2.5.0,<3.0.0)
27
27
  Requires-Dist: pydantic-settings (>=2.2.0,<3.0.0)
28
28
  Requires-Dist: rsa (>=4.8,<5.0)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "aioqzone"
3
- version = "1.9.2.dev2"
3
+ version = "1.9.3.dev1"
4
4
  description = "A Python wrapper for Qzone login and H5 APIs."
5
5
  authors = ["aioqzone <22952836+JamzumSum@users.noreply.github.com>"]
6
6
  license = "AGPL-3.0"
@@ -36,7 +36,7 @@ tenacity = ">=8.2.3,<10.0.0"
36
36
  exceptiongroup = "^1.1.1"
37
37
  tylisten = "^2.1.4"
38
38
  pillow = "^10.1.0"
39
- pychaosvm = { version = "~0.4.1", source = "aioqzone-index" }
39
+ pychaosvm = { version = "~0.4.3", source = "aioqzone-index" }
40
40
  slide-tc = {version = "~0.1.1", optional = true, source = "aioqzone-index" }
41
41
 
42
42
  [tool.poetry.extras]
@@ -61,13 +61,15 @@ class QzoneH5API:
61
61
 
62
62
  async for attempt in self._relogin_retry:
63
63
  with attempt:
64
- if (gtk := self.login.gtk) == 0:
65
- raise TryAgain("no login state")
66
- if api.attach_token:
67
- params["g_tk"] = gtk
68
- hostuin: int = getattr(api.params, "hostuin", self.login.uin)
69
- if qzonetoken := self.qzone_tokens.get(hostuin):
70
- params["qzonetoken"] = qzonetoken
64
+ if api.login_required:
65
+ if (gtk := self.login.gtk) == 0:
66
+ raise TryAgain("no login state")
67
+
68
+ if api.attach_token:
69
+ params["g_tk"] = gtk
70
+ hostuin: int = getattr(api.params, "hostuin", self.login.uin)
71
+ if qzonetoken := self.qzone_tokens.get(hostuin):
72
+ params["qzonetoken"] = qzonetoken
71
73
 
72
74
  async with self.client.request(
73
75
  api.http_method,
@@ -252,3 +254,15 @@ class QzoneH5API:
252
254
  response=DeleteUgcResp,
253
255
  )
254
256
  )
257
+
258
+ async def avatar(self, hostuin: int, size: t.Literal[100, 640]) -> AvatarResponse:
259
+ """Get avatar from uin. Do not require login.
260
+
261
+ :param hostuin: the uin whose avatar to be get
262
+ :param size: availible sizes: `100`|`640`
263
+ """
264
+ return await self.call(
265
+ AvatarApi(
266
+ params=AvatarParams.model_validate(locals()),
267
+ ),
268
+ )
@@ -23,12 +23,13 @@ class QzoneApi(BaseModel, t.Generic[TyRequest, TyResponse]):
23
23
  referer: str = "https://h5.qzone.qq.com/"
24
24
 
25
25
  attach_token: t.ClassVar[bool] = True
26
+ login_required: t.ClassVar[bool] = True
26
27
  params: TyRequest = Field(default_factory=QzoneRequestParams)
27
28
  response: t.ClassVar[t.Type[TyResponse]] # type: ignore
28
29
 
29
30
  @property
30
31
  def url(self) -> URL:
31
- return URL(str(self.host)).with_path(self.path)
32
+ return URL(str(self.host)).with_path(self.path.format(**self.params.model_dump()))
32
33
 
33
34
 
34
35
  class IndexPageApi(QzoneApi[QzoneRequestParams, IndexPageResp]):
@@ -124,3 +125,11 @@ class PhotosPreuploadApi(QzoneApi[PhotosPreuploadParams, PhotosPreuploadResponse
124
125
  http_method: t.ClassVar[TyHttpMethod] = "POST"
125
126
  host: t.ClassVar[str] = "https://mobile.qzone.qq.com"
126
127
  path: t.ClassVar[str] = "/up/cgi-bin/upload/cgi_upload_pic_v2"
128
+
129
+
130
+ class AvatarApi(QzoneApi[AvatarParams, AvatarResponse]):
131
+ response: t.ClassVar = AvatarResponse
132
+ login_required: t.ClassVar[bool] = False
133
+ http_method: t.ClassVar[TyHttpMethod] = "GET"
134
+ host: t.ClassVar[str] = "https://qlogo2.store.qq.com"
135
+ path: t.ClassVar[str] = "/qzone/{hostuin}/{hostuin}/{size}"
@@ -16,6 +16,7 @@ from .response import PicInfo, UploadPicResponse
16
16
  __all__ = [
17
17
  "QzoneRequestParams",
18
18
  "ActiveFeedsParams",
19
+ "AvatarParams",
19
20
  "GetFeedsParams",
20
21
  "ProfileParams",
21
22
  "ShuoshuoParams",
@@ -241,3 +242,8 @@ class PhotosPreuploadParams(QzoneRequestParams):
241
242
 
242
243
  params.update(md5="|".join(md5), filelen="|".join(size))
243
244
  return params
245
+
246
+
247
+ class AvatarParams(QzoneRequestParams):
248
+ hostuin: int
249
+ size: t.Literal[100, 640] = 100
@@ -44,6 +44,7 @@ __all__ = [
44
44
  "FeedData",
45
45
  "PicInfo",
46
46
  "ProfileFeedData",
47
+ "AvatarResponse",
47
48
  ]
48
49
 
49
50
  validate_strdict = TypeAdapter(StrDict).validate_python
@@ -288,3 +289,15 @@ class PhotosPreuploadResponse(QzoneResponse):
288
289
  picinfos = json_loads(m.group(1))
289
290
  assert isinstance(picinfos, list)
290
291
  return dict(photos=[PicInfo.from_response_object(info["picinfo"]) for info in picinfos]) # type: ignore
292
+
293
+
294
+ class AvatarResponse(QzoneResponse):
295
+ _errno_key = None
296
+ _msg_key = None
297
+ _data_key = None
298
+
299
+ avatar: bytes
300
+
301
+ @classmethod
302
+ async def response_to_object(cls, response: ClientResponse) -> "StrDict":
303
+ return {"avatar": await response.content.read()}
File without changes
File without changes