feat(weibo): 修正绑定小程序码并增加账号批量操作

This commit is contained in:
yml2213
2026-11-08 13:17:29 +08:00
parent 79cfd4a634
commit ed15701392
11 changed files with 591 additions and 54 deletions
+2
View File
@@ -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",
+81
View File
@@ -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 ""),
)