pixelarraythirdparty 1.1.7__py3-none-any.whl → 1.2.0__py3-none-any.whl
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.
- pixelarraythirdparty/__init__.py +1 -1
- pixelarraythirdparty/client.py +28 -0
- pixelarraythirdparty/feedback/feedback.py +8 -1
- pixelarraythirdparty/order/order.py +33 -0
- pixelarraythirdparty/unified_login/__init__.py +18 -2
- pixelarraythirdparty/unified_login/unified_login.py +332 -34
- pixelarraythirdparty/user/user.py +0 -3
- {pixelarraythirdparty-1.1.7.dist-info → pixelarraythirdparty-1.2.0.dist-info}/METADATA +1 -1
- pixelarraythirdparty-1.2.0.dist-info/RECORD +21 -0
- pixelarraythirdparty-1.1.7.dist-info/RECORD +0 -21
- {pixelarraythirdparty-1.1.7.dist-info → pixelarraythirdparty-1.2.0.dist-info}/WHEEL +0 -0
- {pixelarraythirdparty-1.1.7.dist-info → pixelarraythirdparty-1.2.0.dist-info}/licenses/LICENSE +0 -0
- {pixelarraythirdparty-1.1.7.dist-info → pixelarraythirdparty-1.2.0.dist-info}/top_level.txt +0 -0
pixelarraythirdparty/__init__.py
CHANGED
pixelarraythirdparty/client.py
CHANGED
|
@@ -4,6 +4,12 @@ from typing import Dict, Any, Tuple
|
|
|
4
4
|
|
|
5
5
|
class AsyncClient:
|
|
6
6
|
def __init__(self, api_key: str):
|
|
7
|
+
"""
|
|
8
|
+
description:
|
|
9
|
+
初始化异步客户端
|
|
10
|
+
parameters:
|
|
11
|
+
api_key(str): API密钥,用于身份验证
|
|
12
|
+
"""
|
|
7
13
|
self.base_url = "https://thirdparty.pixelarrayai.com"
|
|
8
14
|
self.api_key = api_key
|
|
9
15
|
self.headers = {
|
|
@@ -14,6 +20,17 @@ class AsyncClient:
|
|
|
14
20
|
async def _request(
|
|
15
21
|
self, method: str, url: str, **kwargs
|
|
16
22
|
) -> Tuple[Dict[str, Any], bool]:
|
|
23
|
+
"""
|
|
24
|
+
description:
|
|
25
|
+
发送异步HTTP请求的通用方法
|
|
26
|
+
parameters:
|
|
27
|
+
method(str): HTTP方法,如"GET"、"POST"、"PUT"、"DELETE"等
|
|
28
|
+
url(str): 请求URL路径(相对于base_url)
|
|
29
|
+
**kwargs: 其他请求参数,如json、params、headers等
|
|
30
|
+
return:
|
|
31
|
+
data(Dict[str, Any]): 响应数据字典,如果请求失败则返回空字典
|
|
32
|
+
success(bool): 请求是否成功
|
|
33
|
+
"""
|
|
17
34
|
# 如果kwargs中有headers,则合并headers
|
|
18
35
|
headers = self.headers.copy()
|
|
19
36
|
if "headers" in kwargs:
|
|
@@ -38,6 +55,17 @@ class AsyncClient:
|
|
|
38
55
|
async def _request_raw(
|
|
39
56
|
self, method: str, url: str, **kwargs
|
|
40
57
|
) -> Tuple[int, Dict[str, Any]]:
|
|
58
|
+
"""
|
|
59
|
+
description:
|
|
60
|
+
发送异步HTTP请求的原始方法,返回完整的HTTP状态码和响应数据
|
|
61
|
+
parameters:
|
|
62
|
+
method(str): HTTP方法,如"GET"、"POST"、"PUT"、"DELETE"等
|
|
63
|
+
url(str): 请求URL路径(相对于base_url)
|
|
64
|
+
**kwargs: 其他请求参数,如json、params、headers等
|
|
65
|
+
return:
|
|
66
|
+
status_code(int): HTTP状态码
|
|
67
|
+
data(Dict[str, Any]): 响应数据字典,如果解析失败则返回空字典
|
|
68
|
+
"""
|
|
41
69
|
headers = self.headers.copy()
|
|
42
70
|
if "headers" in kwargs:
|
|
43
71
|
headers.update(kwargs["headers"])
|
|
@@ -68,7 +68,14 @@ class FeedbackManagerAsync(AsyncClient):
|
|
|
68
68
|
file_type: str,
|
|
69
69
|
):
|
|
70
70
|
"""
|
|
71
|
-
|
|
71
|
+
description:
|
|
72
|
+
内部方法:上传文件到OSS
|
|
73
|
+
parameters:
|
|
74
|
+
feedback_id(int): 反馈ID
|
|
75
|
+
file_data(Union[str, BinaryIO, bytes]): 文件数据,可以是文件路径、文件对象或字节数据
|
|
76
|
+
file_type(str): 文件类型,可选值:"image"(图片)、"video"(视频)
|
|
77
|
+
return:
|
|
78
|
+
success(bool): 上传是否成功
|
|
72
79
|
"""
|
|
73
80
|
# 读取文件数据
|
|
74
81
|
file_content = None
|
|
@@ -43,6 +43,39 @@ class OrderManagerAsync(AsyncClient):
|
|
|
43
43
|
return {}, False
|
|
44
44
|
return data, True
|
|
45
45
|
|
|
46
|
+
async def create_order_v2(
|
|
47
|
+
self,
|
|
48
|
+
product_id: str,
|
|
49
|
+
body: str = None,
|
|
50
|
+
remark: str = None,
|
|
51
|
+
payment_channel: str = "WECHAT",
|
|
52
|
+
):
|
|
53
|
+
"""
|
|
54
|
+
description:
|
|
55
|
+
创建订单并直接返回支付二维码(V2,推荐)。
|
|
56
|
+
相比旧流程(create_order + generate_qr_code),该接口一次请求完成下单和二维码链接生成。
|
|
57
|
+
parameters:
|
|
58
|
+
product_id(str): 产品ID
|
|
59
|
+
body(str): 商品描述
|
|
60
|
+
remark(str): 订单备注
|
|
61
|
+
payment_channel(str): 支付渠道,可选值:"WECHAT"、"ALIPAY"、"PAYPAL"
|
|
62
|
+
return:
|
|
63
|
+
data(dict): 返回数据
|
|
64
|
+
- order(dict): 订单信息(字段同 create_order 返回)
|
|
65
|
+
- qr_code_url(str): 支付二维码图片URL
|
|
66
|
+
success(bool): 操作是否成功
|
|
67
|
+
"""
|
|
68
|
+
data = {
|
|
69
|
+
"product_id": product_id,
|
|
70
|
+
"body": body,
|
|
71
|
+
"remark": remark,
|
|
72
|
+
"payment_channel": payment_channel,
|
|
73
|
+
}
|
|
74
|
+
data, success = await self._request("POST", "/api/orders/create_v2", json=data)
|
|
75
|
+
if not success:
|
|
76
|
+
return {}, False
|
|
77
|
+
return data, True
|
|
78
|
+
|
|
46
79
|
async def list_order(
|
|
47
80
|
self,
|
|
48
81
|
page: int = 1,
|
|
@@ -1,4 +1,20 @@
|
|
|
1
|
-
from .unified_login import
|
|
1
|
+
from .unified_login import (
|
|
2
|
+
GoogleLogin,
|
|
3
|
+
WechatLogin,
|
|
4
|
+
GitHubLogin,
|
|
5
|
+
DouyinLogin,
|
|
6
|
+
GitLabLogin,
|
|
7
|
+
SMSLogin,
|
|
8
|
+
EmailLogin,
|
|
9
|
+
)
|
|
2
10
|
|
|
3
|
-
__all__ = [
|
|
11
|
+
__all__ = [
|
|
12
|
+
"GoogleLogin",
|
|
13
|
+
"WechatLogin",
|
|
14
|
+
"GitHubLogin",
|
|
15
|
+
"DouyinLogin",
|
|
16
|
+
"GitLabLogin",
|
|
17
|
+
"SMSLogin",
|
|
18
|
+
"EmailLogin",
|
|
19
|
+
]
|
|
4
20
|
|
|
@@ -14,6 +14,15 @@ class GoogleLogin(AsyncClient):
|
|
|
14
14
|
```
|
|
15
15
|
google = GoogleLogin(api_key="your_api_key")
|
|
16
16
|
user_info, success = await google.login()
|
|
17
|
+
if success:
|
|
18
|
+
access_token = user_info.get("access_token")
|
|
19
|
+
refresh_token = user_info.get("refresh_token")
|
|
20
|
+
|
|
21
|
+
# 使用refresh_token刷新access_token
|
|
22
|
+
if refresh_token:
|
|
23
|
+
token_data, success = await google.refresh_access_token(refresh_token)
|
|
24
|
+
if success:
|
|
25
|
+
new_access_token = token_data.get("access_token")
|
|
17
26
|
```
|
|
18
27
|
"""
|
|
19
28
|
|
|
@@ -21,6 +30,13 @@ class GoogleLogin(AsyncClient):
|
|
|
21
30
|
super().__init__(api_key)
|
|
22
31
|
|
|
23
32
|
async def _get_auth_url(self) -> Tuple[Optional[Dict[str, str]], bool]:
|
|
33
|
+
"""
|
|
34
|
+
description:
|
|
35
|
+
获取Google OAuth授权URL
|
|
36
|
+
return:
|
|
37
|
+
auth_data(dict, optional): 授权数据字典,包含auth_url和state
|
|
38
|
+
success(bool): 是否成功
|
|
39
|
+
"""
|
|
24
40
|
data, success = await self._request(
|
|
25
41
|
"POST", "/api/unified-login/google/auth-url"
|
|
26
42
|
)
|
|
@@ -33,10 +49,13 @@ class GoogleLogin(AsyncClient):
|
|
|
33
49
|
|
|
34
50
|
async def login(self, timeout: int = 180) -> Tuple[Dict, bool]:
|
|
35
51
|
"""
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
52
|
+
description:
|
|
53
|
+
仿 Supabase CLI 的一键登录流程:打开浏览器完成授权,终端端轮询等待登录结果
|
|
54
|
+
parameters:
|
|
55
|
+
timeout(int, optional): 等待用户完成授权的超时时间(秒),默认为180
|
|
56
|
+
return:
|
|
57
|
+
user_info(dict): 用户信息字典
|
|
58
|
+
success(bool): 是否成功
|
|
40
59
|
"""
|
|
41
60
|
auth_data, success = await self._get_auth_url()
|
|
42
61
|
if not success or not auth_data:
|
|
@@ -68,6 +87,16 @@ class GoogleLogin(AsyncClient):
|
|
|
68
87
|
async def _wait_for_google_login(
|
|
69
88
|
self, state: str, timeout: int
|
|
70
89
|
) -> Tuple[Dict, bool]:
|
|
90
|
+
"""
|
|
91
|
+
description:
|
|
92
|
+
等待Google登录结果,轮询检查登录状态
|
|
93
|
+
parameters:
|
|
94
|
+
state(str): 登录状态标识
|
|
95
|
+
timeout(int): 超时时间(秒)
|
|
96
|
+
return:
|
|
97
|
+
user_info(dict): 用户信息字典
|
|
98
|
+
success(bool): 是否成功
|
|
99
|
+
"""
|
|
71
100
|
interval = 2
|
|
72
101
|
total_checks = max(1, timeout // interval) if timeout > 0 else 1
|
|
73
102
|
|
|
@@ -88,6 +117,25 @@ class GoogleLogin(AsyncClient):
|
|
|
88
117
|
|
|
89
118
|
return {}, False
|
|
90
119
|
|
|
120
|
+
async def refresh_access_token(self, refresh_token: str) -> Tuple[Dict, bool]:
|
|
121
|
+
"""
|
|
122
|
+
description:
|
|
123
|
+
使用refresh_token刷新access_token
|
|
124
|
+
parameters:
|
|
125
|
+
refresh_token(str): Google OAuth refresh_token
|
|
126
|
+
return:
|
|
127
|
+
token_data(dict): 包含新的access_token和可能的refresh_token的字典
|
|
128
|
+
success(bool): 是否成功
|
|
129
|
+
"""
|
|
130
|
+
data, success = await self._request(
|
|
131
|
+
"POST",
|
|
132
|
+
"/api/unified-login/google/refresh-token",
|
|
133
|
+
json={"refresh_token": refresh_token},
|
|
134
|
+
)
|
|
135
|
+
if not success:
|
|
136
|
+
return {}, False
|
|
137
|
+
return data, True
|
|
138
|
+
|
|
91
139
|
|
|
92
140
|
class WechatLogin(AsyncClient):
|
|
93
141
|
"""
|
|
@@ -112,9 +160,13 @@ class WechatLogin(AsyncClient):
|
|
|
112
160
|
|
|
113
161
|
async def _get_auth_url(self, login_type: str = "desktop") -> Tuple[Optional[Dict[str, str]], bool]:
|
|
114
162
|
"""
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
:
|
|
163
|
+
description:
|
|
164
|
+
获取微信授权URL
|
|
165
|
+
parameters:
|
|
166
|
+
login_type(str, optional): 登录类型,desktop表示PC端扫码登录,mobile表示微信公众号登录,默认为desktop
|
|
167
|
+
return:
|
|
168
|
+
auth_data(dict, optional): 授权数据字典,包含auth_url和state
|
|
169
|
+
success(bool): 是否成功
|
|
118
170
|
"""
|
|
119
171
|
if login_type == "mobile":
|
|
120
172
|
# 微信公众号OAuth登录(手机端)
|
|
@@ -133,20 +185,14 @@ class WechatLogin(AsyncClient):
|
|
|
133
185
|
|
|
134
186
|
async def login(self, login_type: str = "desktop", timeout: int = 180) -> Tuple[Dict, bool]:
|
|
135
187
|
"""
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
wechat = WechatLogin(api_key="your_api_key")
|
|
145
|
-
# PC端扫码登录
|
|
146
|
-
user_info, success = await wechat.login(login_type="desktop")
|
|
147
|
-
# 微信公众号登录(手机端)
|
|
148
|
-
user_info, success = await wechat.login(login_type="mobile")
|
|
149
|
-
```
|
|
188
|
+
description:
|
|
189
|
+
仿 Supabase CLI 的一键登录流程:打开浏览器完成授权,终端端轮询等待登录结果
|
|
190
|
+
parameters:
|
|
191
|
+
login_type(str, optional): 登录类型,desktop表示PC端扫码登录,mobile表示微信公众号登录,默认为desktop
|
|
192
|
+
timeout(int, optional): 等待用户完成授权的超时时间(秒),默认为180
|
|
193
|
+
return:
|
|
194
|
+
user_info(dict): 用户信息字典
|
|
195
|
+
success(bool): 是否成功
|
|
150
196
|
"""
|
|
151
197
|
auth_data, success = await self._get_auth_url(login_type)
|
|
152
198
|
if not success or not auth_data:
|
|
@@ -163,6 +209,14 @@ class WechatLogin(AsyncClient):
|
|
|
163
209
|
return await self._wait_for_wechat_login(state, timeout, login_type)
|
|
164
210
|
|
|
165
211
|
def _extract_state(self, auth_url: Optional[str]) -> Optional[str]:
|
|
212
|
+
"""
|
|
213
|
+
description:
|
|
214
|
+
从授权URL中提取state参数
|
|
215
|
+
parameters:
|
|
216
|
+
auth_url(str, optional): 授权URL
|
|
217
|
+
return:
|
|
218
|
+
state(str, optional): state参数值,如果提取失败则返回None
|
|
219
|
+
"""
|
|
166
220
|
if not auth_url:
|
|
167
221
|
return None
|
|
168
222
|
try:
|
|
@@ -179,11 +233,15 @@ class WechatLogin(AsyncClient):
|
|
|
179
233
|
self, state: str, timeout: int, login_type: str = "desktop"
|
|
180
234
|
) -> Tuple[Dict, bool]:
|
|
181
235
|
"""
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
:
|
|
185
|
-
|
|
186
|
-
|
|
236
|
+
description:
|
|
237
|
+
等待微信登录结果,轮询检查登录状态
|
|
238
|
+
parameters:
|
|
239
|
+
state(str): 登录状态标识
|
|
240
|
+
timeout(int): 超时时间(秒)
|
|
241
|
+
login_type(str, optional): 登录类型,desktop表示PC端扫码登录,mobile表示微信公众号登录,默认为desktop
|
|
242
|
+
return:
|
|
243
|
+
user_info(dict): 用户信息字典
|
|
244
|
+
success(bool): 是否成功
|
|
187
245
|
"""
|
|
188
246
|
interval = 2
|
|
189
247
|
total_checks = max(1, timeout // interval) if timeout > 0 else 1
|
|
@@ -227,6 +285,13 @@ class GitHubLogin(AsyncClient):
|
|
|
227
285
|
super().__init__(api_key)
|
|
228
286
|
|
|
229
287
|
async def _get_auth_url(self) -> Tuple[Optional[Dict[str, str]], bool]:
|
|
288
|
+
"""
|
|
289
|
+
description:
|
|
290
|
+
获取GitHub OAuth授权URL
|
|
291
|
+
return:
|
|
292
|
+
auth_data(dict, optional): 授权数据字典,包含auth_url和state
|
|
293
|
+
success(bool): 是否成功
|
|
294
|
+
"""
|
|
230
295
|
data, success = await self._request(
|
|
231
296
|
"POST", "/api/unified-login/github/auth-url"
|
|
232
297
|
)
|
|
@@ -239,10 +304,13 @@ class GitHubLogin(AsyncClient):
|
|
|
239
304
|
|
|
240
305
|
async def login(self, timeout: int = 180) -> Tuple[Dict, bool]:
|
|
241
306
|
"""
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
307
|
+
description:
|
|
308
|
+
仿 Supabase CLI 的一键登录流程:打开浏览器完成授权,终端端轮询等待登录结果
|
|
309
|
+
parameters:
|
|
310
|
+
timeout(int, optional): 等待用户完成授权的超时时间(秒),默认为180
|
|
311
|
+
return:
|
|
312
|
+
user_info(dict): 用户信息字典
|
|
313
|
+
success(bool): 是否成功
|
|
246
314
|
"""
|
|
247
315
|
auth_data, success = await self._get_auth_url()
|
|
248
316
|
if not success or not auth_data:
|
|
@@ -259,6 +327,14 @@ class GitHubLogin(AsyncClient):
|
|
|
259
327
|
return await self._wait_for_github_login(state, timeout)
|
|
260
328
|
|
|
261
329
|
def _extract_state(self, auth_url: Optional[str]) -> Optional[str]:
|
|
330
|
+
"""
|
|
331
|
+
description:
|
|
332
|
+
从授权URL中提取state参数
|
|
333
|
+
parameters:
|
|
334
|
+
auth_url(str, optional): 授权URL
|
|
335
|
+
return:
|
|
336
|
+
state(str, optional): state参数值,如果提取失败则返回None
|
|
337
|
+
"""
|
|
262
338
|
if not auth_url:
|
|
263
339
|
return None
|
|
264
340
|
try:
|
|
@@ -274,6 +350,16 @@ class GitHubLogin(AsyncClient):
|
|
|
274
350
|
async def _wait_for_github_login(
|
|
275
351
|
self, state: str, timeout: int
|
|
276
352
|
) -> Tuple[Dict, bool]:
|
|
353
|
+
"""
|
|
354
|
+
description:
|
|
355
|
+
等待GitHub登录结果,轮询检查登录状态
|
|
356
|
+
parameters:
|
|
357
|
+
state(str): 登录状态标识
|
|
358
|
+
timeout(int): 超时时间(秒)
|
|
359
|
+
return:
|
|
360
|
+
user_info(dict): 用户信息字典
|
|
361
|
+
success(bool): 是否成功
|
|
362
|
+
"""
|
|
277
363
|
interval = 2
|
|
278
364
|
total_checks = max(1, timeout // interval) if timeout > 0 else 1
|
|
279
365
|
|
|
@@ -310,6 +396,13 @@ class DouyinLogin(AsyncClient):
|
|
|
310
396
|
super().__init__(api_key)
|
|
311
397
|
|
|
312
398
|
async def _get_auth_url(self) -> Tuple[Optional[Dict[str, str]], bool]:
|
|
399
|
+
"""
|
|
400
|
+
description:
|
|
401
|
+
获取抖音OAuth授权URL
|
|
402
|
+
return:
|
|
403
|
+
auth_data(dict, optional): 授权数据字典,包含auth_url和state
|
|
404
|
+
success(bool): 是否成功
|
|
405
|
+
"""
|
|
313
406
|
data, success = await self._request(
|
|
314
407
|
"POST", "/api/unified-login/douyin/auth-url"
|
|
315
408
|
)
|
|
@@ -322,10 +415,13 @@ class DouyinLogin(AsyncClient):
|
|
|
322
415
|
|
|
323
416
|
async def login(self, timeout: int = 180) -> Tuple[Dict, bool]:
|
|
324
417
|
"""
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
418
|
+
description:
|
|
419
|
+
仿 Supabase CLI 的一键登录流程:打开浏览器完成授权,终端端轮询等待登录结果
|
|
420
|
+
parameters:
|
|
421
|
+
timeout(int, optional): 等待用户完成授权的超时时间(秒),默认为180
|
|
422
|
+
return:
|
|
423
|
+
user_info(dict): 用户信息字典
|
|
424
|
+
success(bool): 是否成功
|
|
329
425
|
"""
|
|
330
426
|
auth_data, success = await self._get_auth_url()
|
|
331
427
|
if not success or not auth_data:
|
|
@@ -342,6 +438,14 @@ class DouyinLogin(AsyncClient):
|
|
|
342
438
|
return await self._wait_for_douyin_login(state, timeout)
|
|
343
439
|
|
|
344
440
|
def _extract_state(self, auth_url: Optional[str]) -> Optional[str]:
|
|
441
|
+
"""
|
|
442
|
+
description:
|
|
443
|
+
从授权URL中提取state参数
|
|
444
|
+
parameters:
|
|
445
|
+
auth_url(str, optional): 授权URL
|
|
446
|
+
return:
|
|
447
|
+
state(str, optional): state参数值,如果提取失败则返回None
|
|
448
|
+
"""
|
|
345
449
|
if not auth_url:
|
|
346
450
|
return None
|
|
347
451
|
try:
|
|
@@ -357,6 +461,16 @@ class DouyinLogin(AsyncClient):
|
|
|
357
461
|
async def _wait_for_douyin_login(
|
|
358
462
|
self, state: str, timeout: int
|
|
359
463
|
) -> Tuple[Dict, bool]:
|
|
464
|
+
"""
|
|
465
|
+
description:
|
|
466
|
+
等待抖音登录结果,轮询检查登录状态
|
|
467
|
+
parameters:
|
|
468
|
+
state(str): 登录状态标识
|
|
469
|
+
timeout(int): 超时时间(秒)
|
|
470
|
+
return:
|
|
471
|
+
user_info(dict): 用户信息字典
|
|
472
|
+
success(bool): 是否成功
|
|
473
|
+
"""
|
|
360
474
|
interval = 2
|
|
361
475
|
total_checks = max(1, timeout // interval) if timeout > 0 else 1
|
|
362
476
|
|
|
@@ -459,3 +573,187 @@ class GitLabLogin(AsyncClient):
|
|
|
459
573
|
await asyncio.sleep(interval)
|
|
460
574
|
|
|
461
575
|
return {}, False
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
class SMSLogin(AsyncClient):
|
|
579
|
+
"""
|
|
580
|
+
短信验证码登录客户端
|
|
581
|
+
|
|
582
|
+
使用示例:
|
|
583
|
+
```
|
|
584
|
+
sms = SMSLogin(api_key="your_api_key")
|
|
585
|
+
# 发送验证码
|
|
586
|
+
success = await sms.send_code(phone="13800138000")
|
|
587
|
+
if success:
|
|
588
|
+
# 验证验证码并登录
|
|
589
|
+
user_info, success = await sms.login(phone="13800138000", code="123456")
|
|
590
|
+
```
|
|
591
|
+
"""
|
|
592
|
+
|
|
593
|
+
def __init__(self, api_key: str):
|
|
594
|
+
super().__init__(api_key)
|
|
595
|
+
|
|
596
|
+
async def send_code(self, phone: str) -> bool:
|
|
597
|
+
"""
|
|
598
|
+
发送短信验证码
|
|
599
|
+
|
|
600
|
+
:param phone: 手机号码
|
|
601
|
+
:return: success 是否成功
|
|
602
|
+
"""
|
|
603
|
+
data, success = await self._request(
|
|
604
|
+
"POST", "/api/unified-login/sms/send-code", json={"phone": phone}
|
|
605
|
+
)
|
|
606
|
+
return bool(success)
|
|
607
|
+
|
|
608
|
+
async def verify_code(self, phone: str, code: str) -> bool:
|
|
609
|
+
"""
|
|
610
|
+
验证短信验证码
|
|
611
|
+
|
|
612
|
+
:param phone: 手机号码
|
|
613
|
+
:param code: 验证码
|
|
614
|
+
:return: success 是否成功
|
|
615
|
+
"""
|
|
616
|
+
_, success = await self._request(
|
|
617
|
+
"POST",
|
|
618
|
+
"/api/unified-login/sms/verify-code",
|
|
619
|
+
json={"phone": phone, "code": code},
|
|
620
|
+
)
|
|
621
|
+
return bool(success)
|
|
622
|
+
|
|
623
|
+
async def _wait_for_login(
|
|
624
|
+
self, phone: str, timeout: int
|
|
625
|
+
) -> Tuple[Dict, bool]:
|
|
626
|
+
"""
|
|
627
|
+
等待短信登录结果
|
|
628
|
+
|
|
629
|
+
:param phone: 手机号码
|
|
630
|
+
:param timeout: 超时时间(秒)
|
|
631
|
+
"""
|
|
632
|
+
interval = 2
|
|
633
|
+
total_checks = max(1, timeout // interval) if timeout > 0 else 1
|
|
634
|
+
|
|
635
|
+
for _ in range(total_checks):
|
|
636
|
+
status, response = await self._request_raw(
|
|
637
|
+
"POST",
|
|
638
|
+
"/api/unified-login/sms/wait-sms-login",
|
|
639
|
+
json={"phone": phone},
|
|
640
|
+
)
|
|
641
|
+
|
|
642
|
+
if status == 200 and response.get("success") is True:
|
|
643
|
+
return response.get("data", {}), True
|
|
644
|
+
|
|
645
|
+
if status in (400, 408):
|
|
646
|
+
break
|
|
647
|
+
|
|
648
|
+
await asyncio.sleep(interval)
|
|
649
|
+
|
|
650
|
+
return {}, False
|
|
651
|
+
|
|
652
|
+
async def login(
|
|
653
|
+
self, phone: str, code: str, timeout: int = 180
|
|
654
|
+
) -> Tuple[Dict, bool]:
|
|
655
|
+
"""
|
|
656
|
+
验证验证码并等待登录结果
|
|
657
|
+
|
|
658
|
+
:param phone: 手机号码
|
|
659
|
+
:param code: 验证码
|
|
660
|
+
:param timeout: 等待登录结果的超时时间(秒)
|
|
661
|
+
:return: (用户信息, 是否成功)
|
|
662
|
+
"""
|
|
663
|
+
success = await self.verify_code(phone, code)
|
|
664
|
+
if not success:
|
|
665
|
+
return {}, False
|
|
666
|
+
|
|
667
|
+
return await self._wait_for_login(phone, timeout)
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
class EmailLogin(AsyncClient):
|
|
671
|
+
"""
|
|
672
|
+
邮箱验证码登录客户端
|
|
673
|
+
|
|
674
|
+
使用示例:
|
|
675
|
+
```
|
|
676
|
+
email = EmailLogin(api_key="your_api_key")
|
|
677
|
+
# 发送验证码
|
|
678
|
+
success = await email.send_code(email="user@example.com")
|
|
679
|
+
if success:
|
|
680
|
+
# 验证验证码并登录
|
|
681
|
+
user_info, success = await email.login(email="user@example.com", code="123456")
|
|
682
|
+
```
|
|
683
|
+
"""
|
|
684
|
+
|
|
685
|
+
def __init__(self, api_key: str):
|
|
686
|
+
super().__init__(api_key)
|
|
687
|
+
|
|
688
|
+
async def send_code(self, email: str) -> bool:
|
|
689
|
+
"""
|
|
690
|
+
发送邮箱验证码
|
|
691
|
+
|
|
692
|
+
:param email: 邮箱地址
|
|
693
|
+
:return: success 是否成功
|
|
694
|
+
"""
|
|
695
|
+
_, success = await self._request(
|
|
696
|
+
"POST", "/api/unified-login/email/send-code", json={"email": email}
|
|
697
|
+
)
|
|
698
|
+
return bool(success)
|
|
699
|
+
|
|
700
|
+
async def verify_code(self, email: str, code: str) -> bool:
|
|
701
|
+
"""
|
|
702
|
+
验证邮箱验证码
|
|
703
|
+
|
|
704
|
+
:param email: 邮箱地址
|
|
705
|
+
:param code: 验证码
|
|
706
|
+
:return: success 是否成功
|
|
707
|
+
"""
|
|
708
|
+
_, success = await self._request(
|
|
709
|
+
"POST",
|
|
710
|
+
"/api/unified-login/email/verify-code",
|
|
711
|
+
json={"email": email, "code": code},
|
|
712
|
+
)
|
|
713
|
+
return bool(success)
|
|
714
|
+
|
|
715
|
+
async def _wait_for_login(
|
|
716
|
+
self, email: str, timeout: int
|
|
717
|
+
) -> Tuple[Dict, bool]:
|
|
718
|
+
"""
|
|
719
|
+
等待邮箱登录结果
|
|
720
|
+
|
|
721
|
+
:param email: 邮箱地址
|
|
722
|
+
:param timeout: 超时时间(秒)
|
|
723
|
+
"""
|
|
724
|
+
interval = 2
|
|
725
|
+
total_checks = max(1, timeout // interval) if timeout > 0 else 1
|
|
726
|
+
|
|
727
|
+
for _ in range(total_checks):
|
|
728
|
+
status, response = await self._request_raw(
|
|
729
|
+
"POST",
|
|
730
|
+
"/api/unified-login/email/wait-email-login",
|
|
731
|
+
json={"email": email},
|
|
732
|
+
)
|
|
733
|
+
|
|
734
|
+
if status == 200 and response.get("success") is True:
|
|
735
|
+
return response.get("data", {}), True
|
|
736
|
+
|
|
737
|
+
if status in (400, 408):
|
|
738
|
+
break
|
|
739
|
+
|
|
740
|
+
await asyncio.sleep(interval)
|
|
741
|
+
|
|
742
|
+
return {}, False
|
|
743
|
+
|
|
744
|
+
async def login(
|
|
745
|
+
self, email: str, code: str, timeout: int = 180
|
|
746
|
+
) -> Tuple[Dict, bool]:
|
|
747
|
+
"""
|
|
748
|
+
验证验证码并等待登录结果
|
|
749
|
+
|
|
750
|
+
:param email: 邮箱地址
|
|
751
|
+
:param code: 验证码
|
|
752
|
+
:param timeout: 等待登录结果的超时时间(秒)
|
|
753
|
+
:return: (用户信息, 是否成功)
|
|
754
|
+
"""
|
|
755
|
+
success = await self.verify_code(email, code)
|
|
756
|
+
if not success:
|
|
757
|
+
return {}, False
|
|
758
|
+
|
|
759
|
+
return await self._wait_for_login(email, timeout)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
pixelarraythirdparty/__init__.py,sha256=CavOkBWTHTd6_ND5ztZNmYWhOESL2XpC9XIdrwSS2Pk,582
|
|
2
|
+
pixelarraythirdparty/client.py,sha256=Ym_IZ6cdoZJoMKW61ZRTfQ81-Hay5dpLTgExoANZwI4,3171
|
|
3
|
+
pixelarraythirdparty/cron/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
pixelarraythirdparty/cron/cron.py,sha256=nv4e2hX_UkEJ-kbEARbInU2J6aREyYZ61dZ-4b9UWJI,3146
|
|
5
|
+
pixelarraythirdparty/feedback/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
pixelarraythirdparty/feedback/feedback.py,sha256=ckyJgsk7TWefJhJ3WYhsNvOEQDipLmKF2zwb1eIF83E,9621
|
|
7
|
+
pixelarraythirdparty/order/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
pixelarraythirdparty/order/order.py,sha256=nnmmn40l459EmU-Oq8AFjfm2z6xcBgSxOjA6Xe_8FFk,15494
|
|
9
|
+
pixelarraythirdparty/product/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
pixelarraythirdparty/product/product.py,sha256=5fgv2Ck860epYXxipY83vePziubCIlocFu43mGt_bhM,7497
|
|
11
|
+
pixelarraythirdparty/project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
pixelarraythirdparty/project/project.py,sha256=a8swjckyn4y3NlIx0-tMbcRwDY9woOijGi7lb1wN7Ok,2455
|
|
13
|
+
pixelarraythirdparty/unified_login/__init__.py,sha256=_PeHBsyrPLfcNjbdcsBgQ_75fr1CoRZCNu7S33fsh_Y,291
|
|
14
|
+
pixelarraythirdparty/unified_login/unified_login.py,sha256=A_VBbtIR5clT5HoP56w8cOjOy9T_QfM7s3m9zpxpYLw,23468
|
|
15
|
+
pixelarraythirdparty/user/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
pixelarraythirdparty/user/user.py,sha256=SqufSAVMxQElz-NqtlOZs_dG7UtbuE-ygLAyml9oKpY,5761
|
|
17
|
+
pixelarraythirdparty-1.2.0.dist-info/licenses/LICENSE,sha256=O-g1dUr0U50rSIvmWE9toiVkSgFpVt72_MHITbWvAqA,1067
|
|
18
|
+
pixelarraythirdparty-1.2.0.dist-info/METADATA,sha256=NUdsnHIXVRU9OPFzt50AyoLkcGyWQZphj7oSOiIQxhM,993
|
|
19
|
+
pixelarraythirdparty-1.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
20
|
+
pixelarraythirdparty-1.2.0.dist-info/top_level.txt,sha256=dzG2Ut8j7noUqj_0ZQjcIDAeHYCh_9WtlxjAxtoyufo,21
|
|
21
|
+
pixelarraythirdparty-1.2.0.dist-info/RECORD,,
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
pixelarraythirdparty/__init__.py,sha256=DJD0HApoVd_9RhJ6bMGjlWfs3eBJm3jHrWNJ5KPdYjE,582
|
|
2
|
-
pixelarraythirdparty/client.py,sha256=DY8w2DYsoGQ6cZYqT-FfoovmVd3Ry-aJbJls2Cxat8M,2006
|
|
3
|
-
pixelarraythirdparty/cron/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
pixelarraythirdparty/cron/cron.py,sha256=nv4e2hX_UkEJ-kbEARbInU2J6aREyYZ61dZ-4b9UWJI,3146
|
|
5
|
-
pixelarraythirdparty/feedback/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
pixelarraythirdparty/feedback/feedback.py,sha256=O0d3eyTDZekMEpgACiVi-ms3oVqJAu7g8k5sCgmXMH4,9259
|
|
7
|
-
pixelarraythirdparty/order/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
pixelarraythirdparty/order/order.py,sha256=PGdffVffpUjUeAYBtf6PrMiF2u6pd0LCoro2wZiPcVg,14274
|
|
9
|
-
pixelarraythirdparty/product/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
pixelarraythirdparty/product/product.py,sha256=5fgv2Ck860epYXxipY83vePziubCIlocFu43mGt_bhM,7497
|
|
11
|
-
pixelarraythirdparty/project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
pixelarraythirdparty/project/project.py,sha256=a8swjckyn4y3NlIx0-tMbcRwDY9woOijGi7lb1wN7Ok,2455
|
|
13
|
-
pixelarraythirdparty/unified_login/__init__.py,sha256=2ejSpO0gp-J-XkT5p19V45FlYOW7-NO_aCExukfUTIo,123
|
|
14
|
-
pixelarraythirdparty/unified_login/unified_login.py,sha256=bbbUvYlcR_G5zdfXaDHRZkg9gGjKniVD6cUaKOxhfPQ,14134
|
|
15
|
-
pixelarraythirdparty/user/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
-
pixelarraythirdparty/user/user.py,sha256=v018iisB5AQNs7AtrHIGfu8YIorX0vflDClsKrt3ZMU,5898
|
|
17
|
-
pixelarraythirdparty-1.1.7.dist-info/licenses/LICENSE,sha256=O-g1dUr0U50rSIvmWE9toiVkSgFpVt72_MHITbWvAqA,1067
|
|
18
|
-
pixelarraythirdparty-1.1.7.dist-info/METADATA,sha256=V4Dj8eyc9jYwDZWYPDn_Wi5WGyFGC9JRqOyB5alOf5I,993
|
|
19
|
-
pixelarraythirdparty-1.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
20
|
-
pixelarraythirdparty-1.1.7.dist-info/top_level.txt,sha256=dzG2Ut8j7noUqj_0ZQjcIDAeHYCh_9WtlxjAxtoyufo,21
|
|
21
|
-
pixelarraythirdparty-1.1.7.dist-info/RECORD,,
|
|
File without changes
|
{pixelarraythirdparty-1.1.7.dist-info → pixelarraythirdparty-1.2.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|