feat(weibo): 修正绑定小程序码并增加账号批量操作
This commit is contained in:
@@ -5,6 +5,7 @@ from .client import (
|
||||
WeiboApiError,
|
||||
WeiboAuthError,
|
||||
WeiboBindError,
|
||||
WeiboBindQrCode,
|
||||
WeiboClient,
|
||||
WeiboError,
|
||||
WeiboExchangeError,
|
||||
@@ -24,6 +25,7 @@ __all__ = [
|
||||
"WeiboApiError",
|
||||
"WeiboAuthError",
|
||||
"WeiboBindError",
|
||||
"WeiboBindQrCode",
|
||||
"WeiboClient",
|
||||
"WeiboError",
|
||||
"WeiboExchangeError",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import secrets
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from html.parser import HTMLParser
|
||||
@@ -25,6 +26,7 @@ WEIBO_API_BASE = "https://vipclub.e.weibo.com/aj"
|
||||
WEIBO_VPLUS_API_BASE = "https://vipclub.weibo.com/aj"
|
||||
WEIBO_PAY_BASE = "https://pay.sc.weibo.com"
|
||||
WEIBO_SCHEME_URL = "https://livelinkbind.game.qq.com/livelink"
|
||||
WEIBO_QRCODE_URL = "https://livelinkbind.game.qq.com/api/qrcode/getMiniAppQrcode"
|
||||
WEIBO_MOBILE_UA = (
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) "
|
||||
"AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Weibo"
|
||||
@@ -103,6 +105,14 @@ class WeiboPaymentStatus:
|
||||
sub_status: str
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class WeiboBindQrCode:
|
||||
"""来联返回的小程序码图片。"""
|
||||
|
||||
image_src: str
|
||||
token: str = ""
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class WeiboExchangeResult:
|
||||
goods_id: int
|
||||
@@ -264,6 +274,19 @@ def extract_bind_raw_url(bind_url: str) -> str:
|
||||
return raw_url
|
||||
|
||||
|
||||
def _livelink_qrcode_image_src(qrcode: str) -> str:
|
||||
"""把来联返回的二维码内容转成可直接渲染的图片地址。"""
|
||||
|
||||
text = (qrcode or "").strip()
|
||||
if not text:
|
||||
return ""
|
||||
lower = text.lower()
|
||||
if lower.startswith(("https://", "http://", "data:image/")):
|
||||
return text
|
||||
mime = "image/png" if text.startswith("iVBOR") else "image/jpeg"
|
||||
return f"data:{mime};base64,{text}"
|
||||
|
||||
|
||||
class WeiboClient:
|
||||
"""微博微豆基础客户端。"""
|
||||
|
||||
@@ -1000,3 +1023,61 @@ class WeiboClient:
|
||||
if not scheme:
|
||||
raise WeiboBindError("来联接口没有返回 urlScheme")
|
||||
return scheme
|
||||
|
||||
def generate_bind_qrcode(
|
||||
self,
|
||||
bind_url: str,
|
||||
account_type: str,
|
||||
) -> WeiboBindQrCode:
|
||||
"""生成微信或 QQ 可直接扫码的小程序码图片。"""
|
||||
|
||||
if account_type not in {"wx", "qq"}:
|
||||
raise ValueError("account_type 只能是 wx 或 qq")
|
||||
raw_url = extract_bind_raw_url(bind_url)
|
||||
query = parse_qs(raw_url, keep_blank_values=True)
|
||||
live_plat_id = str((query.get("livePlatId") or [""])[0])
|
||||
act_id = str((query.get("actId") or [""])[0])
|
||||
from_id = str((query.get("fromId") or [""])[0])
|
||||
if not live_plat_id or not act_id:
|
||||
raise WeiboBindError("bind_url 缺少 livePlatId 或 actId")
|
||||
if len(from_id) > 32:
|
||||
from_id = ""
|
||||
|
||||
suffix = "".join(
|
||||
secrets.choice("abcdefghijklmnopqrstuvwxyz0123456789") for _ in range(8)
|
||||
)
|
||||
session_id = f"{int(time.time())}_{suffix}"
|
||||
payload = {
|
||||
"queryString": f"{raw_url}&sessionId={session_id}",
|
||||
"accountType": account_type,
|
||||
}
|
||||
try:
|
||||
data = self._request_json(
|
||||
"POST",
|
||||
WEIBO_QRCODE_URL,
|
||||
headers={
|
||||
"Content-Type": "application/json;charset=UTF-8",
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"Origin": "https://livelink.qq.com",
|
||||
"Referer": "https://livelink.qq.com/",
|
||||
"Gap-Plat-Id": live_plat_id,
|
||||
"Gap-Act-Id": act_id,
|
||||
"Gap-User-From": "pc",
|
||||
"Gap-From-Id": from_id,
|
||||
"Vulcan-AccType": "auth_in_h5",
|
||||
},
|
||||
json=payload,
|
||||
)
|
||||
except WeiboTransportError as exc:
|
||||
raise WeiboBindError(f"请求来联二维码失败: {exc}") from exc
|
||||
if data.get("iRet") != 0:
|
||||
raise WeiboBindError(f"生成绑定二维码失败: {data.get('sMsg') or data}")
|
||||
raw_jdata = data.get("jData")
|
||||
jdata: dict[str, Any] = raw_jdata if isinstance(raw_jdata, dict) else {}
|
||||
image_src = _livelink_qrcode_image_src(str(jdata.get("qrcode") or ""))
|
||||
if not image_src:
|
||||
raise WeiboBindError("来联接口没有返回二维码图片")
|
||||
return WeiboBindQrCode(
|
||||
image_src=image_src,
|
||||
token=str(jdata.get("qrcodeToken") or ""),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user