aioqzone 0.13.0.dev1__tar.gz → 0.13.0.dev2__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-0.13.0.dev1 → aioqzone-0.13.0.dev2}/PKG-INFO +1 -1
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/pyproject.toml +8 -9
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/api/h5/raw.py +6 -4
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/api/web/raw.py +29 -17
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/utils/html.py +8 -7
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/qr/__init__.py +1 -2
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/up/captcha/__init__.py +3 -5
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/up/encrypt.py +2 -3
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/up/h5.py +2 -2
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/up/web.py +2 -2
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/utils/iter.py +5 -1
- aioqzone-0.13.0.dev1/src/qqqr/utils/daug.py +0 -25
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/LICENSE +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/README.md +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/__init__.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/api/__init__.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/api/h5/__init__.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/api/h5/model.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/api/loginman/__init__.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/api/loginman/_base.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/api/web/__init__.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/api/web/constant.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/api/web/model.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/event/__init__.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/event/login.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/exception.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/py.typed +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/type/__init__.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/type/entity.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/type/internal.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/type/resp/__init__.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/type/resp/h5.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/type/resp/web.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/utils/__init__.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/utils/catch.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/utils/entity.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/utils/regex.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/aioqzone/utils/time.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/__init__.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/base.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/constant.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/event/__init__.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/event/evt.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/event/evtmgr.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/event/login.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/exception.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/py.typed +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/qr/type.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/ssl.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/type.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/up/__init__.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/up/captcha/jigsaw.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/up/type.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/utils/encrypt.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/utils/jsjson.py +0 -0
- {aioqzone-0.13.0.dev1 → aioqzone-0.13.0.dev2}/src/qqqr/utils/net.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "aioqzone"
|
|
3
|
-
version = "0.13.0.
|
|
3
|
+
version = "0.13.0.dev2"
|
|
4
4
|
description = "Python wrapper for Qzone login and Qzone HTTP APIs."
|
|
5
5
|
authors = ["aioqzone <zzzzss990315@gmail.com>"]
|
|
6
6
|
license = "AGPL-3.0"
|
|
@@ -33,10 +33,10 @@ pydantic = "^1.10.4"
|
|
|
33
33
|
rsa = "^4.8"
|
|
34
34
|
lxml = "^4.9.1"
|
|
35
35
|
cssselect = "^1.1.0"
|
|
36
|
-
exceptiongroup = { version = "^1.1.1", python = "<3.11"
|
|
36
|
+
exceptiongroup = { version = "^1.1.1", python = "<3.11"}
|
|
37
37
|
|
|
38
|
-
numpy = { version = "^1.22.3", optional = true
|
|
39
|
-
pillow = { version = "^9.4.0", optional = true
|
|
38
|
+
numpy = { version = "^1.22.3", optional = true}
|
|
39
|
+
pillow = { version = "^9.4.0", optional = true}
|
|
40
40
|
pychaosvm = { version = "^0.2.3", optional = true, source = "aioqzone-index" }
|
|
41
41
|
|
|
42
42
|
[tool.poetry.extras]
|
|
@@ -47,7 +47,7 @@ captcha = ["numpy", "pillow", "pychaosvm"]
|
|
|
47
47
|
optional = false
|
|
48
48
|
|
|
49
49
|
[tool.poetry.group.test.dependencies]
|
|
50
|
-
pytest = "^7.
|
|
50
|
+
pytest = "^7.4.0"
|
|
51
51
|
pytest-asyncio = "~0.21.0"
|
|
52
52
|
|
|
53
53
|
[tool.poetry.group.dev]
|
|
@@ -62,17 +62,16 @@ isort = "*"
|
|
|
62
62
|
optional = true
|
|
63
63
|
|
|
64
64
|
[tool.poetry.group.docs.dependencies]
|
|
65
|
-
Sphinx = "^
|
|
65
|
+
Sphinx = "^7.0.1"
|
|
66
66
|
autodoc-pydantic = "*"
|
|
67
67
|
sphinx-autodoc-typehints = "^1.19.5"
|
|
68
|
-
|
|
68
|
+
furo = "*"
|
|
69
69
|
sphinx-intl = "*"
|
|
70
70
|
|
|
71
71
|
[[tool.poetry.source]]
|
|
72
72
|
name = "aioqzone-index"
|
|
73
73
|
url = "https://aioqzone.github.io/aioqzone-index/simple"
|
|
74
|
-
priority = "
|
|
75
|
-
|
|
74
|
+
priority = "supplemental"
|
|
76
75
|
|
|
77
76
|
[[tool.poetry.source]]
|
|
78
77
|
name = "PyPI"
|
|
@@ -9,6 +9,7 @@ from aioqzone.api.loginman import Loginable
|
|
|
9
9
|
from aioqzone.exception import QzoneError
|
|
10
10
|
from aioqzone.utils.catch import HTTPStatusErrorDispatch, QzoneErrorDispatch
|
|
11
11
|
from aioqzone.utils.regex import entire_closing, response_callback
|
|
12
|
+
from qqqr.utils.iter import firstn
|
|
12
13
|
from qqqr.utils.jsjson import JsonValue, json_loads
|
|
13
14
|
from qqqr.utils.net import ClientAdapter
|
|
14
15
|
|
|
@@ -102,7 +103,8 @@ class QzoneH5RawAPI:
|
|
|
102
103
|
"""
|
|
103
104
|
|
|
104
105
|
with QzoneErrorDispatch() as qze, HTTPStatusErrorDispatch() as hse:
|
|
105
|
-
|
|
106
|
+
# NOTE: 尽管只有“-3000: 请先登录”明确要求重新登录,但似乎任何原因的-3000错误值都意味着cookie过期。因此移除了对message的校验
|
|
107
|
+
qze.dispatch(-3000)
|
|
106
108
|
qze.dispatch(-10000)
|
|
107
109
|
hse.dispatch(302, 403)
|
|
108
110
|
return await func(*args, **kwds)
|
|
@@ -150,13 +152,13 @@ class QzoneH5RawAPI:
|
|
|
150
152
|
|
|
151
153
|
assert isinstance(r, dict)
|
|
152
154
|
|
|
153
|
-
err =
|
|
155
|
+
err = firstn((r.get(i) for i in errno_key), lambda i: i is not None)
|
|
154
156
|
assert err is not None, f"no {errno_key} in {r.keys()}"
|
|
155
157
|
assert isinstance(err, (int, str))
|
|
156
158
|
err = int(err)
|
|
157
159
|
|
|
158
160
|
if err != 0:
|
|
159
|
-
msg =
|
|
161
|
+
msg = firstn((r.get(i) for i in msg_key), lambda i: i is not None)
|
|
160
162
|
if msg:
|
|
161
163
|
raise QzoneError(err, msg, rdict=r)
|
|
162
164
|
else:
|
|
@@ -183,7 +185,7 @@ class QzoneH5RawAPI:
|
|
|
183
185
|
raise RuntimeError("script tag not found")
|
|
184
186
|
|
|
185
187
|
texts: List[str] = [s.text for s in scripts]
|
|
186
|
-
script =
|
|
188
|
+
script = firstn(texts, lambda s: "shine0callback" in s)
|
|
187
189
|
if not script:
|
|
188
190
|
raise RuntimeError("data script not found")
|
|
189
191
|
|
|
@@ -17,7 +17,7 @@ from aioqzone.type.internal import AlbumData, LikeData
|
|
|
17
17
|
from aioqzone.utils.catch import HTTPStatusErrorDispatch, QzoneErrorDispatch
|
|
18
18
|
from aioqzone.utils.regex import response_callback
|
|
19
19
|
from aioqzone.utils.time import time_ms
|
|
20
|
-
from qqqr.utils.
|
|
20
|
+
from qqqr.utils.iter import firstn
|
|
21
21
|
from qqqr.utils.jsjson import JsonValue, json_loads
|
|
22
22
|
from qqqr.utils.net import ClientAdapter, raise_for_status
|
|
23
23
|
|
|
@@ -156,13 +156,13 @@ class QzoneWebRawAPI:
|
|
|
156
156
|
r = json_loads(rtext)
|
|
157
157
|
assert isinstance(r, dict)
|
|
158
158
|
|
|
159
|
-
err =
|
|
159
|
+
err = firstn((r.get(i) for i in errno_key), lambda i: i is not None)
|
|
160
160
|
assert err is not None, f"no {errno_key} in {r.keys()}"
|
|
161
161
|
assert isinstance(err, (int, str))
|
|
162
162
|
err = int(err)
|
|
163
163
|
|
|
164
164
|
if err != 0:
|
|
165
|
-
msg =
|
|
165
|
+
msg = firstn((r.get(i) for i in msg_key), lambda i: i is not None)
|
|
166
166
|
if msg:
|
|
167
167
|
raise QzoneError(err, msg, rdict=r)
|
|
168
168
|
else:
|
|
@@ -238,10 +238,11 @@ class QzoneWebRawAPI:
|
|
|
238
238
|
"usertime": time_ms(),
|
|
239
239
|
"externparam": external,
|
|
240
240
|
}
|
|
241
|
+
query.update(default)
|
|
241
242
|
|
|
242
243
|
@self._relogin_retry
|
|
243
244
|
async def retry_closure():
|
|
244
|
-
async with self.host_get(const.feeds3_html_more,
|
|
245
|
+
async with self.host_get(const.feeds3_html_more, query) as r:
|
|
245
246
|
r.raise_for_status()
|
|
246
247
|
rtext = r.text
|
|
247
248
|
|
|
@@ -288,11 +289,12 @@ class QzoneWebRawAPI:
|
|
|
288
289
|
"tid": tid,
|
|
289
290
|
"feedsType": feedstype,
|
|
290
291
|
}
|
|
291
|
-
logger.debug("emotion_getcomments post data:
|
|
292
|
+
logger.debug(f"emotion_getcomments post data: {body}")
|
|
293
|
+
body.update(default)
|
|
292
294
|
|
|
293
295
|
@self._relogin_retry
|
|
294
296
|
async def retry_closure():
|
|
295
|
-
async with self.host_post(const.emotion_getcomments, data=
|
|
297
|
+
async with self.host_post(const.emotion_getcomments, data=body) as r:
|
|
296
298
|
r.raise_for_status()
|
|
297
299
|
rtext = r.text
|
|
298
300
|
|
|
@@ -326,10 +328,11 @@ class QzoneWebRawAPI:
|
|
|
326
328
|
"need_private_comment": 1,
|
|
327
329
|
}
|
|
328
330
|
query = {"uin": owner, "tid": fid}
|
|
331
|
+
query.update(default)
|
|
329
332
|
|
|
330
333
|
@self._relogin_retry
|
|
331
334
|
async def retry_closure():
|
|
332
|
-
async with self.host_get(const.emotion_msgdetail, params=
|
|
335
|
+
async with self.host_get(const.emotion_msgdetail, params=query) as r:
|
|
333
336
|
r.raise_for_status()
|
|
334
337
|
return self._rtext_handler(r.text)
|
|
335
338
|
|
|
@@ -387,12 +390,14 @@ class QzoneWebRawAPI:
|
|
|
387
390
|
"abstime": likedata.abstime,
|
|
388
391
|
"fid": likedata.fid,
|
|
389
392
|
}
|
|
390
|
-
logger.debug("like_app post data:
|
|
393
|
+
logger.debug(f"like_app post data: {body}")
|
|
394
|
+
|
|
395
|
+
body.update(default)
|
|
391
396
|
url = const.internal_dolike_app if like else const.internal_unlike_app
|
|
392
397
|
|
|
393
398
|
@self._relogin_retry
|
|
394
399
|
async def retry_closure():
|
|
395
|
-
async with self.host_post(url, data=
|
|
400
|
+
async with self.host_post(url, data=body) as r:
|
|
396
401
|
r.raise_for_status()
|
|
397
402
|
return self._rtext_handler(r.text, errno_key=("code", "ret"))
|
|
398
403
|
|
|
@@ -454,10 +459,11 @@ class QzoneWebRawAPI:
|
|
|
454
459
|
"t": randint(int(1e8), int(1e9 - 1))
|
|
455
460
|
# The distribution is not consistent with photo.js; but the format is.
|
|
456
461
|
}
|
|
462
|
+
query.update(default)
|
|
457
463
|
|
|
458
464
|
@self._relogin_retry
|
|
459
465
|
async def retry_closure():
|
|
460
|
-
async with self.host_get(const.floatview_photo_list,
|
|
466
|
+
async with self.host_get(const.floatview_photo_list, query) as r:
|
|
461
467
|
r.raise_for_status()
|
|
462
468
|
return self._rtext_handler(r.text)
|
|
463
469
|
|
|
@@ -506,9 +512,12 @@ class QzoneWebRawAPI:
|
|
|
506
512
|
"need_private_comment": 1,
|
|
507
513
|
}
|
|
508
514
|
|
|
515
|
+
if pos:
|
|
516
|
+
param.update(add)
|
|
517
|
+
|
|
509
518
|
@self._relogin_retry
|
|
510
519
|
async def retry_closure():
|
|
511
|
-
async with self.host_get(const.emotion_msglist,
|
|
520
|
+
async with self.host_get(const.emotion_msglist, param) as r:
|
|
512
521
|
r.raise_for_status()
|
|
513
522
|
rtext = r.text
|
|
514
523
|
return self._rtext_handler(rtext)
|
|
@@ -551,11 +560,12 @@ class QzoneWebRawAPI:
|
|
|
551
560
|
"feedversion": 1,
|
|
552
561
|
"hostuin": self.login.uin,
|
|
553
562
|
}
|
|
554
|
-
logger.debug("emotion_publish post data:
|
|
563
|
+
logger.debug(f"emotion_publish post data: {body}")
|
|
564
|
+
body.update(default)
|
|
555
565
|
|
|
556
566
|
@self._relogin_retry
|
|
557
567
|
async def retry_closure():
|
|
558
|
-
async with self.host_post(const.emotion_publish, data=
|
|
568
|
+
async with self.host_post(const.emotion_publish, data=body) as r:
|
|
559
569
|
r.raise_for_status()
|
|
560
570
|
return self._rtext_handler(r.text)
|
|
561
571
|
|
|
@@ -649,11 +659,12 @@ class QzoneWebRawAPI:
|
|
|
649
659
|
"hostuin": uin or self.login.uin,
|
|
650
660
|
# 'pic_bo': ''
|
|
651
661
|
}
|
|
652
|
-
logger.debug("emotion_update post data:
|
|
662
|
+
logger.debug(f"emotion_update post data: {body}")
|
|
663
|
+
body.update(default)
|
|
653
664
|
|
|
654
665
|
@self._relogin_retry
|
|
655
666
|
async def retry_closure():
|
|
656
|
-
async with self.host_post(const.emotion_update, data=
|
|
667
|
+
async with self.host_post(const.emotion_update, data=body) as r:
|
|
657
668
|
r.raise_for_status()
|
|
658
669
|
return self._rtext_handler(r.text)
|
|
659
670
|
|
|
@@ -706,11 +717,12 @@ class QzoneWebRawAPI:
|
|
|
706
717
|
private=int(is_private),
|
|
707
718
|
paramstr=1,
|
|
708
719
|
)
|
|
709
|
-
logger.debug("emotion_re_feeds post data:
|
|
720
|
+
logger.debug(f"emotion_re_feeds post data: {data}")
|
|
721
|
+
data.update(default)
|
|
710
722
|
|
|
711
723
|
@self._relogin_retry
|
|
712
724
|
async def retry_closure():
|
|
713
|
-
async with self.host_post(const.emotion_re_feeds, data=
|
|
725
|
+
async with self.host_post(const.emotion_re_feeds, data=data) as r:
|
|
714
726
|
r.raise_for_status()
|
|
715
727
|
return self._rtext_handler(r.text)
|
|
716
728
|
|
|
@@ -7,12 +7,13 @@ Use this module to get some data from Qzone html feed.
|
|
|
7
7
|
"""
|
|
8
8
|
import logging
|
|
9
9
|
import re
|
|
10
|
+
from contextlib import suppress
|
|
10
11
|
from typing import Iterable, List, Optional, Union, cast
|
|
11
12
|
|
|
12
13
|
from lxml.html import HtmlElement, fromstring
|
|
13
|
-
from pydantic import BaseModel, HttpUrl
|
|
14
|
+
from pydantic import BaseModel, HttpUrl, ValidationError
|
|
14
15
|
|
|
15
|
-
from qqqr.utils.
|
|
16
|
+
from qqqr.utils.iter import firstn
|
|
16
17
|
|
|
17
18
|
from ..type.entity import ConEntity
|
|
18
19
|
from ..type.internal import AlbumData
|
|
@@ -81,7 +82,7 @@ class HtmlContent(BaseModel):
|
|
|
81
82
|
img_data = lambda a: {k[5:]: v for k, v in a.attrib.items() if k.startswith("data-")}
|
|
82
83
|
|
|
83
84
|
def load_src(a: Iterable[HtmlElement]) -> Optional[HttpUrl]:
|
|
84
|
-
o
|
|
85
|
+
o = firstn(a, lambda i: i.tag == "img")
|
|
85
86
|
if o is None:
|
|
86
87
|
return
|
|
87
88
|
src: str = o.get("src", "")
|
|
@@ -101,11 +102,11 @@ class HtmlContent(BaseModel):
|
|
|
101
102
|
|
|
102
103
|
finfo: HtmlElement = mxsafe(root.cssselect("div.f-info"))
|
|
103
104
|
lia: List[HtmlElement] = root.cssselect("div.f-ct a.img-item")
|
|
105
|
+
(d := img_data(lia[0]))["hostuin"] = hostuin
|
|
104
106
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
album = None
|
|
107
|
+
album = None
|
|
108
|
+
with suppress(ValidationError):
|
|
109
|
+
album = AlbumData.parse_obj(d)
|
|
109
110
|
|
|
110
111
|
pic = [
|
|
111
112
|
PicRep(
|
|
@@ -11,7 +11,6 @@ from ..constant import StatusCode
|
|
|
11
11
|
from ..event import Emittable, hook_guard
|
|
12
12
|
from ..event.login import QrEvent
|
|
13
13
|
from ..exception import UserBreak
|
|
14
|
-
from ..utils.daug import du
|
|
15
14
|
from ..utils.encrypt import hash33
|
|
16
15
|
|
|
17
16
|
log = logging.getLogger(__name__)
|
|
@@ -86,7 +85,7 @@ class QrLogin(LoginBase[QrSession], Emittable[QrEvent]):
|
|
|
86
85
|
"daid": self.app.daid,
|
|
87
86
|
}
|
|
88
87
|
|
|
89
|
-
async with self.client.get(POLL_QR, params=
|
|
88
|
+
async with self.client.get(POLL_QR, params=data.update(const) or data) as r:
|
|
90
89
|
r.raise_for_status()
|
|
91
90
|
rl = re.findall(r"'(.*?)'[,\)]", r.text)
|
|
92
91
|
|
|
@@ -3,6 +3,7 @@ import base64
|
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
5
|
import re
|
|
6
|
+
from contextlib import suppress
|
|
6
7
|
from hashlib import md5
|
|
7
8
|
from ipaddress import IPv4Address
|
|
8
9
|
from random import random
|
|
@@ -13,7 +14,6 @@ from chaosvm import prepare
|
|
|
13
14
|
from chaosvm.proxy.dom import TDC
|
|
14
15
|
from httpx import URL
|
|
15
16
|
|
|
16
|
-
from ...utils.daug import du
|
|
17
17
|
from ...utils.iter import first
|
|
18
18
|
from ...utils.net import ClientAdapter
|
|
19
19
|
from ..type import PrehandleResp, VerifyResp
|
|
@@ -165,7 +165,7 @@ class Captcha:
|
|
|
165
165
|
"subsid": 1,
|
|
166
166
|
"callback": CALLBACK,
|
|
167
167
|
}
|
|
168
|
-
async with self.client.get(PREHANDLE_URL, params=
|
|
168
|
+
async with self.client.get(PREHANDLE_URL, params=data.update(const) or data) as r:
|
|
169
169
|
r.raise_for_status()
|
|
170
170
|
m = re.search(CALLBACK + r"\((\{.*\})\)", r.text)
|
|
171
171
|
|
|
@@ -189,11 +189,9 @@ class Captcha:
|
|
|
189
189
|
# BUG: should always bypass client's proxy settings
|
|
190
190
|
async with self.client.get("https://" + api) as r:
|
|
191
191
|
cand = r.text.strip()
|
|
192
|
-
|
|
192
|
+
with suppress(ValueError):
|
|
193
193
|
IPv4Address(cand)
|
|
194
194
|
return cand
|
|
195
|
-
except ValueError:
|
|
196
|
-
continue
|
|
197
195
|
return ""
|
|
198
196
|
|
|
199
197
|
async def get_captcha_problem(self, sess: TcaptchaSession):
|
|
@@ -4,6 +4,7 @@ import base64
|
|
|
4
4
|
import struct
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
6
|
from binascii import hexlify
|
|
7
|
+
from contextlib import suppress
|
|
7
8
|
from hashlib import md5
|
|
8
9
|
from random import randint
|
|
9
10
|
from typing import Union
|
|
@@ -121,11 +122,9 @@ class TeaEncoder(PasswdEncoder):
|
|
|
121
122
|
"""
|
|
122
123
|
e = []
|
|
123
124
|
for i in range(0, len(s), 2):
|
|
124
|
-
|
|
125
|
+
with suppress(ValueError):
|
|
125
126
|
e.append(int(s[i : i + 2], 16))
|
|
126
127
|
continue
|
|
127
|
-
except ValueError:
|
|
128
|
-
pass
|
|
129
128
|
try:
|
|
130
129
|
e.append(int(s[i : i + 1], 16))
|
|
131
130
|
except ValueError:
|
|
@@ -4,7 +4,6 @@ import re
|
|
|
4
4
|
from httpx import URL
|
|
5
5
|
|
|
6
6
|
from qqqr.constant import StatusCode
|
|
7
|
-
from qqqr.utils.daug import du
|
|
8
7
|
|
|
9
8
|
from .type import CheckResp, LoginResp
|
|
10
9
|
from .web import UpWebLogin, UpWebSession
|
|
@@ -93,7 +92,8 @@ class UpH5Login(UpWebLogin):
|
|
|
93
92
|
data["pt_sms_code"] = sess.sms_code
|
|
94
93
|
self.referer = "https://xui.ptlogin2.qq.com/"
|
|
95
94
|
|
|
96
|
-
|
|
95
|
+
data.update(const)
|
|
96
|
+
async with self.client.get(LOGIN_URL, params=data) as response:
|
|
97
97
|
response.raise_for_status()
|
|
98
98
|
|
|
99
99
|
rl = re.findall(r"'(.*?)'[,\)]", response.text)
|
|
@@ -13,7 +13,6 @@ from qqqr.event import Emittable
|
|
|
13
13
|
from qqqr.event.login import UpEvent
|
|
14
14
|
from qqqr.exception import TencentLoginError
|
|
15
15
|
from qqqr.type import APPID, PT_QR_APP, Proxy
|
|
16
|
-
from qqqr.utils.daug import du
|
|
17
16
|
from qqqr.utils.net import ClientAdapter
|
|
18
17
|
|
|
19
18
|
from .encrypt import PasswdEncoder, TeaEncoder
|
|
@@ -230,7 +229,8 @@ class UpWebLogin(LoginBase[UpWebSession], Emittable[UpEvent]):
|
|
|
230
229
|
data["pt_sms_code"] = sess.sms_code
|
|
231
230
|
self.referer = sess.login_referer
|
|
232
231
|
|
|
233
|
-
|
|
232
|
+
data.update(const)
|
|
233
|
+
async with self.client.get(LOGIN_URL, params=data) as response:
|
|
234
234
|
response.raise_for_status()
|
|
235
235
|
|
|
236
236
|
rl = re.findall(r"'(.*?)'[,\)]", response.text)
|
|
@@ -20,6 +20,10 @@ def first(
|
|
|
20
20
|
it: Iterable[T], pred: Optional[Callable[[T], Union[object, None]]] = None, *, default: D = ...
|
|
21
21
|
) -> Union[T, D]:
|
|
22
22
|
f = filter(pred, it)
|
|
23
|
-
if default
|
|
23
|
+
if default is ...:
|
|
24
24
|
return next(f)
|
|
25
25
|
return next(f, default)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def firstn(it: Iterable[T], pred: Optional[Callable[[T], Union[object, None]]] = None):
|
|
29
|
+
return first(it, pred, default=None)
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"""Augment python `dict` since we cannot use py37+ features...
|
|
2
|
-
|
|
3
|
-
Maybe these are user-defined syntax-sugars?
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from itertools import chain
|
|
7
|
-
from typing import Dict, Hashable, Iterable, Mapping, MutableMapping, Tuple, TypeVar
|
|
8
|
-
|
|
9
|
-
K = TypeVar("K", bound=Hashable)
|
|
10
|
-
V = TypeVar("V")
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def u_(*dicts: Mapping[K, V]) -> Iterable[Tuple[K, V]]:
|
|
14
|
-
return chain(*(i.items() for i in dicts))
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def du(*dicts: Mapping[K, V]) -> Dict[K, V]:
|
|
18
|
-
"""returns union of these dict."""
|
|
19
|
-
return dict(u_(*dicts))
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def di(d: MutableMapping[K, V], **kw) -> MutableMapping[K, V]:
|
|
23
|
-
"""insert kw into this dict."""
|
|
24
|
-
d.update(**kw)
|
|
25
|
-
return d
|
|
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
|