pixelarraythirdparty 1.1.2__py3-none-any.whl → 1.1.3__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.
@@ -11,7 +11,7 @@ PixelArray 第三方微服务客户端
11
11
  - unified_login: 统一登录模块
12
12
  """
13
13
 
14
- __version__ = "1.1.2"
14
+ __version__ = "1.1.3"
15
15
  __author__ = "Lu qi"
16
16
  __email__ = "qi.lu@pixelarrayai.com"
17
17
 
@@ -92,21 +92,38 @@ class GoogleLogin(AsyncClient):
92
92
  class WechatLogin(AsyncClient):
93
93
  """
94
94
  微信 OAuth2 登录客户端
95
+
96
+ 支持两种登录方式:
97
+ - desktop: PC端扫码登录(使用微信开放平台)
98
+ - mobile: 微信公众号OAuth登录(手机端微信内打开)
95
99
 
96
100
  使用示例:
97
101
  ```
98
102
  wechat = WechatLogin(api_key="your_api_key")
99
- user_info, success = await wechat.login()
103
+ # PC端扫码登录
104
+ user_info, success = await wechat.login(login_type="desktop")
105
+ # 微信公众号登录(手机端)
106
+ user_info, success = await wechat.login(login_type="mobile")
100
107
  ```
101
108
  """
102
109
 
103
110
  def __init__(self, api_key: str):
104
111
  super().__init__(api_key)
105
112
 
106
- async def _get_auth_url(self) -> Tuple[Optional[Dict[str, str]], bool]:
107
- data, success = await self._request(
108
- "POST", "/api/unified-login/wechat/auth-url"
109
- )
113
+ async def _get_auth_url(self, login_type: str = "desktop") -> Tuple[Optional[Dict[str, str]], bool]:
114
+ """
115
+ 获取微信授权URL
116
+
117
+ :param login_type: 登录类型,desktop 表示PC端扫码登录,mobile 表示微信公众号登录
118
+ """
119
+ if login_type == "mobile":
120
+ # 微信公众号OAuth登录(手机端)
121
+ endpoint = "/api/unified-login/wechat-official/auth-url"
122
+ else:
123
+ # PC端扫码登录
124
+ endpoint = "/api/unified-login/wechat/auth-url"
125
+
126
+ data, success = await self._request("POST", endpoint)
110
127
  if not success:
111
128
  return None, False
112
129
  auth_url = data.get("auth_url")
@@ -114,14 +131,24 @@ class WechatLogin(AsyncClient):
114
131
  return None, False
115
132
  return data, True
116
133
 
117
- async def login(self, timeout: int = 180) -> Tuple[Dict, bool]:
134
+ async def login(self, login_type: str = "desktop", timeout: int = 180) -> Tuple[Dict, bool]:
118
135
  """
119
136
  仿 Supabase CLI 的一键登录流程:打开浏览器完成授权,
120
137
  终端端轮询等待登录结果
121
138
 
139
+ :param login_type: 登录类型,desktop 表示PC端扫码登录,mobile 表示微信公众号登录
122
140
  :param timeout: 等待用户完成授权的超时时间(秒)
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
+ ```
123
150
  """
124
- auth_data, success = await self._get_auth_url()
151
+ auth_data, success = await self._get_auth_url(login_type)
125
152
  if not success or not auth_data:
126
153
  return {}, False
127
154
 
@@ -133,7 +160,7 @@ class WechatLogin(AsyncClient):
133
160
 
134
161
  webbrowser.open(auth_url, new=2)
135
162
 
136
- return await self._wait_for_wechat_login(state, timeout)
163
+ return await self._wait_for_wechat_login(state, timeout, login_type)
137
164
 
138
165
  def _extract_state(self, auth_url: Optional[str]) -> Optional[str]:
139
166
  if not auth_url:
@@ -149,15 +176,28 @@ class WechatLogin(AsyncClient):
149
176
  return None
150
177
 
151
178
  async def _wait_for_wechat_login(
152
- self, state: str, timeout: int
179
+ self, state: str, timeout: int, login_type: str = "desktop"
153
180
  ) -> Tuple[Dict, bool]:
181
+ """
182
+ 等待微信登录结果
183
+
184
+ :param state: 登录状态标识
185
+ :param timeout: 超时时间(秒)
186
+ :param login_type: 登录类型,desktop 表示PC端扫码登录,mobile 表示微信公众号登录
187
+ """
154
188
  interval = 2
155
189
  total_checks = max(1, timeout // interval) if timeout > 0 else 1
156
190
 
191
+ # 根据登录类型选择不同的等待接口
192
+ if login_type == "mobile":
193
+ endpoint = "/api/unified-login/wechat-official/wait-login"
194
+ else:
195
+ endpoint = "/api/unified-login/wechat/wait-login"
196
+
157
197
  for _ in range(total_checks):
158
198
  status, response = await self._request_raw(
159
199
  "POST",
160
- "/api/unified-login/wechat/wait-login",
200
+ endpoint,
161
201
  json={"state": state},
162
202
  )
163
203
 
@@ -255,16 +295,84 @@ class GitHubLogin(AsyncClient):
255
295
  return {}, False
256
296
 
257
297
 
258
- class UnifiedLogin(AsyncClient):
298
+ class DouyinLogin(AsyncClient):
299
+ """
300
+ 抖音 OAuth2 登录客户端
301
+
302
+ 使用示例:
303
+ ```
304
+ douyin = DouyinLogin(api_key="your_api_key")
305
+ user_info, success = await douyin.login()
306
+ ```
307
+ """
308
+
259
309
  def __init__(self, api_key: str):
260
310
  super().__init__(api_key)
261
311
 
262
- async def login(self, provider: str, timeout: int = 180) -> Tuple[Dict, bool]:
263
- if provider == "google":
264
- return await GoogleLogin(self.api_key).login(timeout)
265
- elif provider == "wechat":
266
- return await WechatLogin(self.api_key).login(timeout)
267
- elif provider == "github":
268
- return await GitHubLogin(self.api_key).login(timeout)
269
- else:
312
+ async def _get_auth_url(self) -> Tuple[Optional[Dict[str, str]], bool]:
313
+ data, success = await self._request(
314
+ "POST", "/api/unified-login/douyin/auth-url"
315
+ )
316
+ if not success:
317
+ return None, False
318
+ auth_url = data.get("auth_url")
319
+ if not auth_url:
320
+ return None, False
321
+ return data, True
322
+
323
+ async def login(self, timeout: int = 180) -> Tuple[Dict, bool]:
324
+ """
325
+ 仿 Supabase CLI 的一键登录流程:打开浏览器完成授权,
326
+ 终端端轮询等待登录结果
327
+
328
+ :param timeout: 等待用户完成授权的超时时间(秒)
329
+ """
330
+ auth_data, success = await self._get_auth_url()
331
+ if not success or not auth_data:
270
332
  return {}, False
333
+
334
+ auth_url = auth_data.get("auth_url")
335
+ state = auth_data.get("state") or self._extract_state(auth_url)
336
+
337
+ if not auth_url or not state:
338
+ return {}, False
339
+
340
+ webbrowser.open(auth_url, new=2)
341
+
342
+ return await self._wait_for_douyin_login(state, timeout)
343
+
344
+ def _extract_state(self, auth_url: Optional[str]) -> Optional[str]:
345
+ if not auth_url:
346
+ return None
347
+ try:
348
+ parsed = urllib.parse.urlparse(auth_url)
349
+ query = urllib.parse.parse_qs(parsed.query)
350
+ values = query.get("state")
351
+ if values:
352
+ return values[0]
353
+ except Exception:
354
+ return None
355
+ return None
356
+
357
+ async def _wait_for_douyin_login(
358
+ self, state: str, timeout: int
359
+ ) -> Tuple[Dict, bool]:
360
+ interval = 2
361
+ total_checks = max(1, timeout // interval) if timeout > 0 else 1
362
+
363
+ for _ in range(total_checks):
364
+ status, response = await self._request_raw(
365
+ "POST",
366
+ "/api/unified-login/douyin/wait-login",
367
+ json={"state": state},
368
+ )
369
+
370
+ if status == 200 and response.get("success") is True:
371
+ return response.get("data", {}), True
372
+
373
+ if status in (400, 408):
374
+ break
375
+
376
+ await asyncio.sleep(interval)
377
+
378
+ return {}, False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pixelarraythirdparty
3
- Version: 1.1.2
3
+ Version: 1.1.3
4
4
  Summary: PixelArray 第三方微服务客户端
5
5
  Author-email: Lu qi <qi.lu@pixelarrayai.com>
6
6
  License-Expression: MIT
@@ -1,4 +1,4 @@
1
- pixelarraythirdparty/__init__.py,sha256=6Gb-EBJU0w8cIVBjgIA6hIcCmeY_PMAEutBpNtmTcyI,490
1
+ pixelarraythirdparty/__init__.py,sha256=4SwrWVbKxryIzsDKZuZiIojd6EcRc-zLC01GzoHQBsI,490
2
2
  pixelarraythirdparty/client.py,sha256=DY8w2DYsoGQ6cZYqT-FfoovmVd3Ry-aJbJls2Cxat8M,2006
3
3
  pixelarraythirdparty/cron/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  pixelarraythirdparty/cron/cron.py,sha256=2oxnIJvRY0A-ynhKSHJvZmJLj1EWzYcqSIl8r9yh9a0,4402
@@ -7,11 +7,11 @@ pixelarraythirdparty/order/order.py,sha256=rk9O73m5kOhjzavq3wIfN-JKj3cLwAItHVTJ2
7
7
  pixelarraythirdparty/product/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  pixelarraythirdparty/product/product.py,sha256=5fgv2Ck860epYXxipY83vePziubCIlocFu43mGt_bhM,7497
9
9
  pixelarraythirdparty/unified_login/__init__.py,sha256=2ejSpO0gp-J-XkT5p19V45FlYOW7-NO_aCExukfUTIo,123
10
- pixelarraythirdparty/unified_login/unified_login.py,sha256=em5qBdAWPS4Fs4SO2oEOBF2XQlR5drlDK8NQ0RyKAnc,8032
10
+ pixelarraythirdparty/unified_login/unified_login.py,sha256=q8_59HPEuD8YVBC-KCH9ozrVC5THdc-3xJqCr2-f5lU,11684
11
11
  pixelarraythirdparty/user/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  pixelarraythirdparty/user/user.py,sha256=v018iisB5AQNs7AtrHIGfu8YIorX0vflDClsKrt3ZMU,5898
13
- pixelarraythirdparty-1.1.2.dist-info/licenses/LICENSE,sha256=O-g1dUr0U50rSIvmWE9toiVkSgFpVt72_MHITbWvAqA,1067
14
- pixelarraythirdparty-1.1.2.dist-info/METADATA,sha256=7wSIsvbQQ8pQdVKqhxquRxfSg4IdxVcJ8zK5Qa2QpCw,993
15
- pixelarraythirdparty-1.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
- pixelarraythirdparty-1.1.2.dist-info/top_level.txt,sha256=dzG2Ut8j7noUqj_0ZQjcIDAeHYCh_9WtlxjAxtoyufo,21
17
- pixelarraythirdparty-1.1.2.dist-info/RECORD,,
13
+ pixelarraythirdparty-1.1.3.dist-info/licenses/LICENSE,sha256=O-g1dUr0U50rSIvmWE9toiVkSgFpVt72_MHITbWvAqA,1067
14
+ pixelarraythirdparty-1.1.3.dist-info/METADATA,sha256=nfpYNkKb-71etMWL6O66GsIDVfbAyOT1p8VGuEV5ng8,993
15
+ pixelarraythirdparty-1.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
+ pixelarraythirdparty-1.1.3.dist-info/top_level.txt,sha256=dzG2Ut8j7noUqj_0ZQjcIDAeHYCh_9WtlxjAxtoyufo,21
17
+ pixelarraythirdparty-1.1.3.dist-info/RECORD,,