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.
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/PKG-INFO +6 -6
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/README.md +2 -2
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/pyproject.toml +5 -5
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/api/feed.py +3 -12
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/api/profile.py +3 -4
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/type.py +1 -1
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/captcha/__init__.py +4 -1
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/captcha/_model.py +19 -30
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/captcha/capsess.py +22 -6
- aioqzone-1.9.1.dev3/src/qqqr/up/captcha/click/__init__.py +3 -0
- aioqzone-1.9.1.dev3/src/qqqr/up/captcha/click/_types.py +8 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/captcha/select/_types.py +7 -9
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/captcha/slide/_types.py +26 -7
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/LICENSE +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/__init__.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/api/__init__.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/api/h5/__init__.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/api/h5/model.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/api/login/__init__.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/api/login/_base.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/exception.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/message.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/__init__.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/api/__init__.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/api/request.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/api/response.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/protocol/__init__.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/protocol/config.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/model/protocol/entity.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/utils/__init__.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/utils/entity.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/utils/regex.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/utils/retry.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/aioqzone/utils/time.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/__init__.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/base.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/constant.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/exception.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/message.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/py.typed +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/qr/__init__.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/qr/type.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/__init__.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/_model.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/captcha/pil_utils.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/captcha/select/__init__.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/captcha/slide/__init__.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/encrypt.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/h5.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/up/web.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/utils/encrypt.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/utils/iter.py +0 -0
- {aioqzone-1.8.6.dev1 → aioqzone-1.9.1.dev3}/src/qqqr/utils/jsjson.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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
|
[][home]
|
|
44
44
|
[][pypi]
|
|
45
45
|
[](https://github.com/psf/black)
|
|
46
|
-
[](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-
|
|
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
|
[][home]
|
|
6
6
|
[][pypi]
|
|
7
7
|
[](https://github.com/psf/black)
|
|
8
|
-
[](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-
|
|
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.
|
|
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.
|
|
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.
|
|
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 = ['
|
|
97
|
+
target-version = ['py39']
|
|
98
98
|
|
|
99
99
|
[tool.pyright]
|
|
100
|
-
pythonVersion = "3.
|
|
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(
|
|
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 =
|
|
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(
|
|
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 =
|
|
97
|
+
v.summary = v.summary.removeprefix(":")
|
|
99
98
|
return v
|
|
100
99
|
|
|
101
100
|
|
|
@@ -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 .
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
|
120
|
+
from .slide import SlideCaptchaSession as cls
|
|
105
121
|
|
|
106
122
|
return cls(session=session, prehandle=prehandle)
|
|
@@ -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
|
|
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
|
|
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 =
|
|
66
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|