feat(huya): 整合 App 协议登录获取 Cookie 全链路并与 Web 登录解耦

- core/huya: 新增 app_login, wup_encoder, nonce_forge, cert_forge, envelope_forge, device_profile, udb_aes
- core/huya/__init__.py: 导出 login_huya_app_password 与 HuyaAppPasswordLogin
- web/backend: 新增 /accounts/app-password-login 与 /accounts/app-password-login/selected 路由及 Schema,与原 Web 密码登录独立分开
- web/frontend: 增加 App 登录 API 与前端界面“App 登录选中”操作,弹窗结果明确区分
- tests: 新增 test_huya_app_login.py 单元测试覆盖全链路
This commit is contained in:
yml2213
2026-08-26 15:51:30 +08:00
parent 4eec01568f
commit da2cf5a61d
22 changed files with 2572 additions and 11 deletions
+5
View File
@@ -15,6 +15,7 @@ import type {
HuyaPasswordAccountImportResult,
HuyaPasswordLoginBatchResult,
HuyaPasswordLoginRequest,
HuyaAppPasswordLoginRequest,
HuyaPasswordLoginResult,
HuyaPasswordLoginSelectedRequest,
HuyaRechargeGoodsItem,
@@ -49,6 +50,10 @@ export const huyaApi = {
api.post<HuyaPasswordLoginResult, HuyaPasswordLoginResult>('/huya/accounts/password-login', data),
passwordLoginSelected: (data: HuyaPasswordLoginSelectedRequest) =>
api.post<HuyaPasswordLoginBatchResult, HuyaPasswordLoginBatchResult>('/huya/accounts/password-login/selected', data),
appPasswordLogin: (data: HuyaAppPasswordLoginRequest) =>
api.post<HuyaPasswordLoginResult, HuyaPasswordLoginResult>('/huya/accounts/app-password-login', data),
appPasswordLoginSelected: (data: HuyaPasswordLoginSelectedRequest) =>
api.post<HuyaPasswordLoginBatchResult, HuyaPasswordLoginBatchResult>('/huya/accounts/app-password-login/selected', data),
smsCode: (data: HuyaSmsCodeRequest) =>
api.post<HuyaSmsCodeResult, HuyaSmsCodeResult>('/huya/accounts/sms-code', data, { timeout: 120000 }),
smsLogin: (data: HuyaSmsLoginRequest) =>
+8
View File
@@ -478,6 +478,13 @@ export interface HuyaPasswordLoginRequest {
cookie?: string;
}
export interface HuyaAppPasswordLoginRequest {
username: string;
password: string;
tag?: string;
force_new_device?: boolean;
}
export interface HuyaPasswordLoginResult extends MessageResponse {
account: HuyaAccountItem;
sdid: string;
@@ -615,6 +622,7 @@ export interface HuyaPasswordLoginSelectedRequest {
tag?: string;
assigned_only?: boolean;
has_cookie?: boolean;
force_new_device?: boolean;
}
export interface HuyaCookieItem {
+54 -5
View File
@@ -56,6 +56,8 @@ export default function HuyaAccountsPage() {
const [passwordImporting, setPasswordImporting] = useState(false);
const [passwordLoginResultOpen, setPasswordLoginResultOpen] = useState(false);
const [passwordLogging, setPasswordLogging] = useState(false);
const [appPasswordLogging, setAppPasswordLogging] = useState(false);
const [loginModalTitle, setLoginModalTitle] = useState('虎牙 App 协议登录结果 (推荐)');
const [passwordLoginResults, setPasswordLoginResults] = useState<HuyaPasswordLoginBatchItem[]>([]);
const [smsLoginOpen, setSmsLoginOpen] = useState(false);
const [smsPhone, setSmsPhone] = useState('');
@@ -291,7 +293,35 @@ export default function HuyaAccountsPage() {
}
};
const handleLoginSelected = async () => {
const handleAppLoginSelected = async () => {
if (selectedCount === 0) {
message.warning('请先选择虎牙账号');
return;
}
setAppPasswordLogging(true);
try {
const result = await huyaApi.appPasswordLoginSelected({
...selectionPayload,
});
setPasswordLoginResults(result.results || []);
setLoginModalTitle('虎牙 App 协议登录结果 (推荐)');
setPasswordLoginResultOpen(true);
if (result.failed > 0) {
message.warning(result.message);
} else {
message.success(result.message || 'App 登录成功,Cookie 已保存');
}
loadAccounts();
loadTags();
loadSummary();
} catch (e: unknown) {
message.error(getErrorMessage(e));
} finally {
setAppPasswordLogging(false);
}
};
const handleWebLoginSelected = async () => {
if (selectedCount === 0) {
message.warning('请先选择虎牙账号');
return;
@@ -302,11 +332,12 @@ export default function HuyaAccountsPage() {
...selectionPayload,
});
setPasswordLoginResults(result.results || []);
setLoginModalTitle('虎牙 Web 密码登录结果 (旧版)');
setPasswordLoginResultOpen(true);
if (result.failed > 0) {
message.warning(result.message);
} else {
message.success(result.message || '登录成功,Cookie 已保存');
message.success(result.message || 'Web 登录成功,Cookie 已保存');
}
loadAccounts();
loadTags();
@@ -574,14 +605,25 @@ export default function HuyaAccountsPage() {
</Button>
)}
{canImport && (
<Button
type="primary"
disabled={selectedCount === 0}
icon={<MobileOutlined />}
loading={appPasswordLogging}
onClick={handleAppLoginSelected}
>
App ({selectedCount})
</Button>
)}
{canImport && (
<Button
disabled={selectedCount === 0}
icon={<LoginOutlined />}
loading={passwordLogging}
onClick={handleLoginSelected}
onClick={handleWebLoginSelected}
>
Web ()
</Button>
)}
{canDelete && selectedCount > 0 && (
@@ -861,7 +903,7 @@ export default function HuyaAccountsPage() {
</Modal>
<Modal
title="虎牙登录结果"
title={loginModalTitle}
open={passwordLoginResultOpen}
onCancel={() => setPasswordLoginResultOpen(false)}
footer={[
@@ -871,6 +913,13 @@ export default function HuyaAccountsPage() {
]}
width={860}
>
<Alert
type="info"
showIcon
style={{ marginBottom: 12 }}
message="登录说明"
description="使用 App 协议登录时,系统已自动识别并通过 safe_auth 乱序滑块;若个别新账号提示“要求扫码验证 (qr_auth)”,则需在手机虎牙 App 上正常登录一次以建立设备信任。"
/>
{passwordLoginResults.length > 0 && (
<Table
columns={passwordLoginResultColumns}