aioqzone 1.8.6.dev1__tar.gz → 1.9.1.dev3__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.8.6.dev1 → aioqzone-1.9.1.dev3}/PKG-INFO +6 -6
  2. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/README.md +2 -2
  3. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/pyproject.toml +5 -5
  4. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/api/feed.py +3 -12
  5. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/api/profile.py +3 -4
  6. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/type.py +1 -1
  7. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/captcha/__init__.py +4 -1
  8. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/captcha/_model.py +19 -30
  9. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/captcha/capsess.py +22 -6
  10. aioqzone-1.9.1.dev3/src/qqqr/up/captcha/click/__init__.py +3 -0
  11. aioqzone-1.9.1.dev3/src/qqqr/up/captcha/click/_types.py +8 -0
  12. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/captcha/select/_types.py +7 -9
  13. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/captcha/slide/_types.py +26 -7
  14. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/LICENSE +0 -0
  15. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/__init__.py +0 -0
  16. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/api/__init__.py +0 -0
  17. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/api/h5/__init__.py +0 -0
  18. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/api/h5/model.py +0 -0
  19. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/api/login/__init__.py +0 -0
  20. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/api/login/_base.py +0 -0
  21. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/exception.py +0 -0
  22. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/message.py +0 -0
  23. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/__init__.py +0 -0
  24. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/api/__init__.py +0 -0
  25. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/api/request.py +0 -0
  26. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/api/response.py +0 -0
  27. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/protocol/__init__.py +0 -0
  28. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/protocol/config.py +0 -0
  29. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/protocol/entity.py +0 -0
  30. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/utils/__init__.py +0 -0
  31. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/utils/entity.py +0 -0
  32. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/utils/regex.py +0 -0
  33. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/utils/retry.py +0 -0
  34. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/utils/time.py +0 -0
  35. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/__init__.py +0 -0
  36. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/base.py +0 -0
  37. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/constant.py +0 -0
  38. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/exception.py +0 -0
  39. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/message.py +0 -0
  40. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/py.typed +0 -0
  41. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/qr/__init__.py +0 -0
  42. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/qr/type.py +0 -0
  43. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/__init__.py +0 -0
  44. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/_model.py +0 -0
  45. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/captcha/pil_utils.py +0 -0
  46. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/captcha/select/__init__.py +0 -0
  47. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/captcha/slide/__init__.py +0 -0
  48. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/encrypt.py +0 -0
  49. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/h5.py +0 -0
  50. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/web.py +0 -0
  51. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/utils/encrypt.py +0 -0
  52. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/utils/iter.py +0 -0
  53. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/utils/jsjson.py +0 -0
  54. {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/utils/net.py +0 -0
@@ -1,29 +1,29 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aioqzone
3
- Version: 1.8.6.dev1
3
+ Version: 1.9.1.dev3
4
4
  Summary: A Python wrapper for Qzone login and H5 APIs.
5
5
  Home-page: https://github.com/aioqzone/aioqzone
6
6
  License: AGPL-3.0
7
7
  Keywords: qzone-api,autologin,asyncio-spider
8
8
  Author: aioqzone
9
9
  Author-email: zzzzss990315@gmail.com
10
- Requires-Python: >=3.8,<4.0
10
+ Requires-Python: >=3.9,<4.0
11
11
  Classifier: Development Status :: 4 - Beta
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: License :: OSI Approved :: GNU Affero General Public License v3
14
14
  Classifier: Programming Language :: Python :: 3
15
- Classifier: Programming Language :: Python :: 3.8
16
15
  Classifier: Programming Language :: Python :: 3.9
17
16
  Classifier: Programming Language :: Python :: 3.10
18
17
  Classifier: Programming Language :: Python :: 3.11
19
18
  Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
20
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
21
  Classifier: Typing :: Typed
22
22
  Provides-Extra: slide-captcha
23
23
  Requires-Dist: aiohttp (>=3.9.0,<4.0.0)
24
24
  Requires-Dist: exceptiongroup (>=1.1.1,<2.0.0)
25
25
  Requires-Dist: pillow (>=10.1.0,<11.0.0)
26
- Requires-Dist: pychaosvm (>=0.4.0,<0.5.0)
26
+ Requires-Dist: pychaosvm (>=0.4.1,<0.5.0)
27
27
  Requires-Dist: pydantic (>=2.5.0,<3.0.0)
28
28
  Requires-Dist: pydantic-settings (>=2.2.0,<3.0.0)
29
29
  Requires-Dist: rsa (>=4.8,<5.0)
@@ -43,7 +43,7 @@ aioqzone封装了一些Qzone接口。
43
43
  [![python](https://img.shields.io/pypi/pyversions/aioqzone?logo=python&logoColor=white)][home]
44
44
  [![version](https://img.shields.io/pypi/v/aioqzone?logo=python)][pypi]
45
45
  [![style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
46
- [![discuss](https://img.shields.io/badge/dynamic/xml?style=social&logo=telegram&label=Discuss&query=%2F%2Fdiv%5B%40class%3D%22tgme_page_extra%22%5D&url=https%3A%2F%2Ft.me%2Faioqzone_chatroom)](https://t.me/aioqzone_chatroom)
46
+ [![discuss](https://img.shields.io/endpoint?label=Discuss&style=social&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Faioqzone_chatroom)](https://t.me/aioqzone_chatroom)
47
47
 
48
48
  [English](README_en.md) | 简体中文
49
49
 
@@ -98,7 +98,7 @@ __在做了:__
98
98
  ## 许可证
99
99
 
100
100
  ```
101
- Copyright (C) 2022-2023 aioqzone.
101
+ Copyright (C) 2022-2024 aioqzone.
102
102
 
103
103
  This program is free software: you can redistribute it and/or modify
104
104
  it under the terms of the GNU Affero General Public License as published
@@ -5,7 +5,7 @@ aioqzone封装了一些Qzone接口。
5
5
  [![python](https://img.shields.io/pypi/pyversions/aioqzone?logo=python&logoColor=white)][home]
6
6
  [![version](https://img.shields.io/pypi/v/aioqzone?logo=python)][pypi]
7
7
  [![style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
8
- [![discuss](https://img.shields.io/badge/dynamic/xml?style=social&logo=telegram&label=Discuss&query=%2F%2Fdiv%5B%40class%3D%22tgme_page_extra%22%5D&url=https%3A%2F%2Ft.me%2Faioqzone_chatroom)](https://t.me/aioqzone_chatroom)
8
+ [![discuss](https://img.shields.io/endpoint?label=Discuss&style=social&url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Faioqzone_chatroom)](https://t.me/aioqzone_chatroom)
9
9
 
10
10
  [English](README_en.md) | 简体中文
11
11
 
@@ -60,7 +60,7 @@ __在做了:__
60
60
  ## 许可证
61
61
 
62
62
  ```
63
- Copyright (C) 2022-2023 aioqzone.
63
+ Copyright (C) 2022-2024 aioqzone.
64
64
 
65
65
  This program is free software: you can redistribute it and/or modify
66
66
  it under the terms of the GNU Affero General Public License as published
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "aioqzone"
3
- version = "1.8.6.dev1"
3
+ version = "1.9.1.dev3"
4
4
  description = "A Python wrapper for Qzone login and H5 APIs."
5
5
  authors = ["aioqzone <zzzzss990315@gmail.com>"]
6
6
  license = "AGPL-3.0"
@@ -27,7 +27,7 @@ exclude = ["*.js"]
27
27
  "Discussion" = "https://t.me/aioqzone_chatroom"
28
28
 
29
29
  [tool.poetry.dependencies]
30
- python = "^3.8"
30
+ python = "^3.9"
31
31
  aiohttp = "^3.9.0"
32
32
  pydantic = "^2.5.0"
33
33
  pydantic-settings = "^2.2.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.0", source = "aioqzone-index" }
39
+ pychaosvm = { version = "~0.4.1", source = "aioqzone-index" }
40
40
  slide-tc = {version = "~0.1.1", optional = true, source = "aioqzone-index" }
41
41
 
42
42
  [tool.poetry.extras]
@@ -94,8 +94,8 @@ line_length = 99
94
94
 
95
95
  [tool.black]
96
96
  line-length = 99
97
- target-version = ['py38']
97
+ target-version = ['py39']
98
98
 
99
99
  [tool.pyright]
100
- pythonVersion = "3.8"
100
+ pythonVersion = "3.9"
101
101
  pythonPlatform = "All"
@@ -15,15 +15,6 @@ from pydantic import (
15
15
 
16
16
  __all__ = ["FeedData"]
17
17
 
18
- if sys.version_info >= (3, 9):
19
- removeprefix = str.removeprefix
20
- else:
21
-
22
- def removeprefix(self: str, prefix: str, /):
23
- if self.startswith(prefix):
24
- return self[len(prefix) :]
25
- return self
26
-
27
18
 
28
19
  class UgcRight(IntEnum):
29
20
  unknown = 0
@@ -225,13 +216,13 @@ class Share(HasCommon):
225
216
 
226
217
  class FeedOriginal(HasFid, HasCommon, HasUserInfo, HasSummary, HasMedia):
227
218
  @model_validator(mode="before")
228
- def remove_prefix(cls, v: dict):
229
- return {removeprefix(k, "cell_"): i for k, i in v.items()}
219
+ def remove_prefix(cls, v: dict[str, t.Any]):
220
+ return {k.removeprefix("cell_"): i for k, i in v.items()}
230
221
 
231
222
  @field_validator("summary")
232
223
  @classmethod
233
224
  def remove_colon(cls, v: FeedSummary):
234
- v.summary = removeprefix(v.summary, ":")
225
+ v.summary = v.summary.removeprefix(":")
235
226
  return v
236
227
 
237
228
 
@@ -21,7 +21,6 @@ from .feed import (
21
21
  Share,
22
22
  ShareInfo,
23
23
  UserInfo,
24
- removeprefix,
25
24
  )
26
25
 
27
26
 
@@ -89,13 +88,13 @@ class ProfileComment(FeedComment):
89
88
 
90
89
  class ProfileFeedOriginal(HasFid, HasCommon, HasUserInfo, HasSummary, HasMedia):
91
90
  @model_validator(mode="before")
92
- def remove_prefix(cls, v: dict):
93
- return {removeprefix(k, "cell_"): i for k, i in v.items()}
91
+ def remove_prefix(cls, v: dict[str, t.Any]):
92
+ return {k.removeprefix("cell_"): i for k, i in v.items()}
94
93
 
95
94
  @field_validator("summary")
96
95
  @classmethod
97
96
  def remove_colon(cls, v: FeedSummary):
98
- v.summary = removeprefix(v.summary, ":")
97
+ v.summary = v.summary.removeprefix(":")
99
98
  return v
100
99
 
101
100
 
@@ -3,7 +3,7 @@ from dataclasses import dataclass
3
3
 
4
4
  from pydantic import BaseModel
5
5
 
6
- if sys.version_info >= (3, 9):
6
+ if sys.version_info >= (3, 10):
7
7
  frozen_args = dict(frozen=True, slots=True)
8
8
  else:
9
9
  frozen_args = dict(frozen=True)
@@ -18,7 +18,8 @@ from ...utils.net import ClientAdapter
18
18
  from .._model import VerifyResp
19
19
  from ._model import PrehandleResp
20
20
  from .capsess import BaseTcaptchaSession as TcaptchaSession
21
- from .select._types import SelectCaptchaSession
21
+ from .click import ClickCaptchaSession
22
+ from .select import SelectCaptchaSession
22
23
 
23
24
  PREHANDLE_URL = "https://t.captcha.qq.com/cap_union_prehandle"
24
25
  SHOW_NEW_URL = "https://t.captcha.qq.com/cap_union_new_show"
@@ -132,6 +133,8 @@ class Captcha(_CaptchaHookMixin):
132
133
  sess = TcaptchaSession.factory(sid, await retry_closure())
133
134
  if isinstance(sess, SelectCaptchaSession):
134
135
  sess.solve_captcha_hook = self.solve_select_captcha
136
+ elif isinstance(sess, ClickCaptchaSession):
137
+ raise NotImplementedError("“依次点击”类验证码正在施工")
135
138
  else:
136
139
  sess.solve_captcha_hook = self.solve_slide_captcha
137
140
  return sess
@@ -1,6 +1,6 @@
1
- from typing import List, Optional
1
+ import typing as t
2
2
 
3
- from pydantic import BaseModel, Field
3
+ from pydantic import AliasPath, BaseModel, Field
4
4
 
5
5
 
6
6
  class PowCfg(BaseModel):
@@ -15,12 +15,25 @@ class CommonCaptchaConf(BaseModel):
15
15
  """relative path to get tdc.js"""
16
16
 
17
17
 
18
+ class CommonClickConf(BaseModel):
19
+ data_type: str = Field(validation_alias=AliasPath("data_type", 0))
20
+ mark_style: str
21
+
22
+
23
+ class CommonBgElmConf(BaseModel):
24
+ cfg: CommonClickConf = Field(validation_alias="click_cfg")
25
+
26
+
27
+ class CommonRender(BaseModel):
28
+ bg: CommonBgElmConf = Field(validation_alias="bg_elem_cfg")
29
+
30
+
18
31
  class Sprite(BaseModel):
19
32
  """Represents a sprite from a source material."""
20
33
 
21
- size_2d: List[int]
34
+ size_2d: t.List[int]
22
35
  """sprite size (w, h)"""
23
- sprite_pos: List[int]
36
+ sprite_pos: t.List[int]
24
37
  """sprite position on material (x, y)"""
25
38
 
26
39
  @property
@@ -37,37 +50,13 @@ class Sprite(BaseModel):
37
50
  return (l, t, l + self.width, l + self.height)
38
51
 
39
52
 
40
- class ClickCfg(BaseModel):
41
- mark_style: str
42
- data_type: List[str]
43
-
44
-
45
- class MoveCfg(BaseModel):
46
- track_limit: str
47
- move_factor: List[int]
48
- data_type: Optional[List[str]] = None
49
-
50
-
51
- class FgElemCfg(Sprite):
52
- id: int
53
- init_pos: List[int]
54
- move_cfg: Optional[MoveCfg] = None
55
-
56
-
57
- class FgBindingCfg(BaseModel):
58
- master: int
59
- slave: int
60
- bind_type: str
61
- bind_factor: int
62
-
63
-
64
53
  class CaptchaData(BaseModel):
65
54
  common: CommonCaptchaConf = Field(alias="comm_captcha_cfg")
66
- render: dict = Field(alias="dyn_show_info")
55
+ render: dict[str, t.Any] = Field(alias="dyn_show_info")
67
56
 
68
57
 
69
58
  class PrehandleResp(BaseModel):
70
- captcha: CaptchaData = Field(alias="data", default=None)
59
+ captcha: t.Optional[CaptchaData] = Field(alias="data", default=None)
71
60
  sess: str
72
61
 
73
62
  capclass: int = 0
@@ -1,19 +1,23 @@
1
1
  import asyncio
2
+ import logging
2
3
  import typing as t
3
4
  from abc import ABC, abstractmethod
4
5
  from hashlib import md5
5
6
  from time import time
6
7
 
8
+ from pydantic import ValidationError
7
9
  from tylisten import HookSpec
8
10
  from yarl import URL
9
11
 
10
12
  from qqqr.utils.net import ClientAdapter
11
13
 
12
- from ._model import PrehandleResp
14
+ from ._model import CommonRender, PrehandleResp
15
+
16
+ log = logging.getLogger(__name__)
13
17
 
14
18
 
15
19
  class BaseTcaptchaSession(ABC):
16
- data_type: str = "DynAnswerType_UC"
20
+ data_type: str
17
21
  mouse_track: "asyncio.Future[t.Optional[t.List[t.Tuple[int, int]]]]"
18
22
  solve_captcha_hook: HookSpec
19
23
 
@@ -32,6 +36,7 @@ class BaseTcaptchaSession(ABC):
32
36
  self.mouse_track = asyncio.get_event_loop().create_future()
33
37
 
34
38
  def parse_captcha_data(self):
39
+ assert self.prehandle.captcha
35
40
  self.conf = self.prehandle.captcha
36
41
 
37
42
  def solve_workload(self, *, timeout: float = 30.0):
@@ -97,10 +102,21 @@ class BaseTcaptchaSession(ABC):
97
102
 
98
103
  @classmethod
99
104
  def factory(cls, session: str, prehandle: PrehandleResp):
100
- render = prehandle.captcha.render
101
- if "json_payload" in render:
102
- from .select._types import SelectCaptchaSession as cls
105
+ assert prehandle.captcha
106
+
107
+ try:
108
+ render = CommonRender.model_validate(prehandle.captcha.render)
109
+ except ValidationError:
110
+ log.error(prehandle.captcha.render)
111
+ raise
112
+
113
+ if render.bg.cfg.data_type == "DynAnswerType_UC":
114
+ from .select import SelectCaptchaSession as cls
115
+ elif render.bg.cfg.data_type == "DynAnswerType_POS":
116
+ log.error(prehandle.captcha.render)
117
+ raise NotImplementedError("“依次点击”类验证码正在施工")
118
+ from .click import ClickCaptchaSession as cls
103
119
  else:
104
- from .slide._types import SlideCaptchaSession as cls
120
+ from .slide import SlideCaptchaSession as cls
105
121
 
106
122
  return cls(session=session, prehandle=prehandle)
@@ -0,0 +1,3 @@
1
+ from ._types import ClickCaptchaSession
2
+
3
+ __all__ = ["ClickCaptchaSession"]
@@ -0,0 +1,8 @@
1
+ from .._model import PrehandleResp
2
+ from ..capsess import BaseTcaptchaSession
3
+
4
+
5
+ class ClickCaptchaSession(BaseTcaptchaSession):
6
+ def __init__(self, session: str, prehandle: PrehandleResp) -> None:
7
+ super().__init__(session, prehandle)
8
+ self.mouse_track.set_result(None)
@@ -3,22 +3,21 @@ import logging
3
3
  import typing as t
4
4
  from contextlib import suppress
5
5
 
6
- from pydantic import AliasPath, BaseModel, Field, model_validator
6
+ from pydantic import AliasPath, BaseModel, Field, ValidationError, model_validator
7
7
 
8
8
  from qqqr.message import solve_select_captcha
9
9
  from qqqr.utils.iter import first
10
10
  from qqqr.utils.jsjson import json_loads
11
11
  from qqqr.utils.net import ClientAdapter
12
12
 
13
- from .._model import ClickCfg, PrehandleResp, Sprite
13
+ from .._model import CommonBgElmConf, CommonRender, PrehandleResp, Sprite
14
14
  from ..capsess import BaseTcaptchaSession
15
15
  from ..pil_utils import *
16
16
 
17
17
  log = logging.getLogger(__name__)
18
18
 
19
19
 
20
- class SelectBgElemCfg(Sprite):
21
- click_cfg: ClickCfg
20
+ class SelectBgElemCfg(CommonBgElmConf, Sprite):
22
21
  img_url: str
23
22
 
24
23
 
@@ -40,7 +39,7 @@ class SelectJsonPayload(BaseModel):
40
39
  return len(self.picture_ids)
41
40
 
42
41
 
43
- class SelectCaptchaDisplay(BaseModel):
42
+ class SelectRender(CommonRender):
44
43
  instruction: str
45
44
  bg: SelectBgElemCfg = Field(alias="bg_elem_cfg")
46
45
  verify_trigger_cfg: dict
@@ -56,15 +55,14 @@ class SelectCaptchaDisplay(BaseModel):
56
55
  class SelectCaptchaSession(BaseTcaptchaSession):
57
56
  solve_captcha_hook: solve_select_captcha.TyInst
58
57
 
59
- def __init__(self, session: str, prehandle: PrehandleResp) -> None:
58
+ def __init__(self, session: str, prehandle: "PrehandleResp") -> None:
60
59
  super().__init__(session, prehandle)
61
60
  self.mouse_track.set_result(None)
62
61
 
63
62
  def parse_captcha_data(self):
64
63
  super().parse_captcha_data()
65
- self.render = SelectCaptchaDisplay.model_validate(self.conf.render)
66
- if self.render.bg.click_cfg.data_type:
67
- self.data_type = self.render.bg.click_cfg.data_type[0]
64
+ self.render = SelectRender.model_validate(self.conf.render)
65
+ self.data_type = self.render.bg.cfg.data_type
68
66
 
69
67
  async def get_captcha_problem(self, client: ClientAdapter):
70
68
  async with client.get(self._cdn_join(self.render.bg.img_url)) as r:
@@ -4,13 +4,13 @@ import typing as t
4
4
  from contextlib import suppress
5
5
  from random import choices, randint
6
6
 
7
- from pydantic import BaseModel, Field
7
+ from pydantic import AliasPath, BaseModel, Field
8
8
 
9
9
  from qqqr.message import solve_slide_captcha
10
10
  from qqqr.utils.iter import first, firstn
11
11
  from qqqr.utils.net import ClientAdapter
12
12
 
13
- from .._model import FgBindingCfg, FgElemCfg, Sprite
13
+ from .._model import CommonBgElmConf, CommonRender, Sprite
14
14
  from ..capsess import BaseTcaptchaSession
15
15
  from ..pil_utils import *
16
16
 
@@ -58,14 +58,33 @@ except ImportError:
58
58
  return noise_x, noise_y
59
59
 
60
60
 
61
- class SlideBgElemCfg(Sprite):
61
+ class MoveCfg(BaseModel):
62
+ track_limit: str
63
+ move_factor: t.List[int]
64
+ data_type: str = Field(validation_alias=AliasPath("data_type", 0))
65
+
66
+
67
+ class FgElemCfg(Sprite):
68
+ id: int
69
+ init_pos: t.List[int]
70
+ move_cfg: t.Optional[MoveCfg] = None
71
+
72
+
73
+ class FgBindingCfg(BaseModel):
74
+ master: int
75
+ slave: int
76
+ bind_type: str
77
+ bind_factor: int
78
+
79
+
80
+ class SlideBgElemCfg(CommonBgElmConf, Sprite):
62
81
  img_url: str
63
82
  """relative url to get jigsaw puzzle image (background with dimmed piece shape)."""
64
83
  init_pos: t.List[int] = Field(default=[0, 0])
65
84
  """sprite init position on captcha (x, y)"""
66
85
 
67
86
 
68
- class SlideCaptchaDisplay(BaseModel):
87
+ class SlideRender(CommonRender):
69
88
  bg: SlideBgElemCfg = Field(alias="bg_elem_cfg")
70
89
  """Background (puzzle)"""
71
90
  fg_binding_list: t.List[FgBindingCfg] = Field(default=[])
@@ -80,7 +99,8 @@ class SlideCaptchaSession(BaseTcaptchaSession):
80
99
 
81
100
  def parse_captcha_data(self):
82
101
  super().parse_captcha_data()
83
- self.render = SlideCaptchaDisplay.model_validate(self.conf.render)
102
+ self.render = SlideRender.model_validate(self.conf.render)
103
+
84
104
  self.cdn_urls = (
85
105
  self._cdn_join(self.render.bg.img_url),
86
106
  self._cdn_join(self.render.sprite_url),
@@ -89,8 +109,7 @@ class SlideCaptchaSession(BaseTcaptchaSession):
89
109
 
90
110
  self.piece_sprite = first(self.render.sprites, lambda s: s.move_cfg)
91
111
  assert self.piece_sprite.move_cfg
92
- if self.piece_sprite.move_cfg.data_type:
93
- self.data_type = self.piece_sprite.move_cfg.data_type[0]
112
+ self.data_type = self.piece_sprite.move_cfg.data_type
94
113
 
95
114
  async def get_captcha_problem(self, client: ClientAdapter):
96
115
  """
File without changes