🌱 完成登录请求

#202
This commit is contained in:
BTMuli
2026-01-04 03:38:46 +08:00
parent 1fa1f2b780
commit edb7088846
8 changed files with 195 additions and 13 deletions

View File

@@ -1,6 +1,6 @@
/**
* Hutao 插件入口
* @since Beta v0.6.3
* @since Beta v0.9.1
*/
import {
@@ -12,10 +12,13 @@ import {
getTeamCollect,
uploadAbyssData,
} from "./request/abyssReq.js";
import { loginPassport } from "./request/accountReq.js";
import { getCombatStatistic, uploadCombatData } from "./request/combatReq.js";
import { transAbyssAvatars, transAbyssLocal } from "./utils/abyssUtil.js";
import { transCombatLocal } from "./utils/combatUtil.js";
const _ = "Not Implemented";
const Hutao = {
Abyss: {
avatar: {
@@ -37,6 +40,27 @@ const Hutao = {
data: getCombatStatistic,
trans: transCombatLocal,
},
Account: {
register: _,
login: loginPassport,
verify: _,
cancel: _,
reset: {
username: _,
password: _,
},
token: {
refresh: _,
revoke: _,
revokeAll: _,
},
info: _,
},
Gacha: {
log: _,
upload: _,
delete: _,
},
};
export default Hutao;

View File

@@ -0,0 +1,66 @@
/**
* 账号相关请求
* @since Beta v0.9.1
*/
import TGHttp from "@utils/TGHttp.js";
import { importPublicKey, sha1 } from "rsa-oaep-encryption";
import { getReqHeader } from "../utils/authUtils.js";
/** 加密公钥 */
const HUTAO_PUB_KEY: Readonly<string> = `
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5W2SEyZSlP2zBI1Sn8Gd
TwbZoXlUGNKyoVrY8SVYu9GMefdGZCrUQNkCG/Np8pWPmSSEFGd5oeug/oIMtCZQ
NOn0drlR+pul/XZ1KQhKmj/arWjN1XNok2qXF7uxhqD0JyNT/Fxy6QvzqIpBsM9S
7ajm8/BOGlPG1SInDPaqTdTRTT30AuN+IhWEEFwT3Ctv1SmDupHs2Oan5qM7Y3uw
b6K1rbnk5YokiV2FzHajGUymmSKXqtG1USZzwPqImpYb4Z0M/StPFWdsKqexBqMM
mkXckI5O98GdlszEmQ0Ejv5Fx9fR2rXRwM76S4iZTfabYpiMbb4bM42mHMauupj6
9QIDAQAB
-----END PUBLIC KEY-----`;
const encrypt = importPublicKey(HUTAO_PUB_KEY);
const PassportUrl = "https://homa.gentle.house/Passport/v2/";
/**
* rsa 加密
* @since Beta v0.9.1
* @param data - 待加密数据
* @returns 加密后数据
*/
function rsaEncrypt(data: string): string {
const res = encrypt.encrypt(data, sha1.create());
const bytes = new Uint8Array(res);
let binary = "";
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
/**
* 登录
* @since Beta v0.9.1
* @param username - 用户名(邮箱)
* @param password - 密码
* @returns
*/
export async function loginPassport(
username: string,
password: string,
): Promise<TGApp.Plugins.Hutao.Account.LoginRes | TGApp.Plugins.Hutao.Base.Resp> {
const url = `${PassportUrl}Login`;
const data = {
UserName: rsaEncrypt(username),
Password: rsaEncrypt(password),
};
const header = await getReqHeader();
const resp = await TGHttp<TGApp.Plugins.Hutao.Account.LoginResp>(url, {
method: "POST",
headers: header,
body: JSON.stringify(data),
});
if (resp.retcode !== 0) return <TGApp.Plugins.Hutao.Base.Resp>resp;
return resp.data;
}

58
src/plugins/Hutao/types/Account.d.ts vendored Normal file
View File

@@ -0,0 +1,58 @@
/**
* 账号请求相关类型
* @since Beta v0.9.1
*/
declare namespace TGApp.Plugins.Hutao.Account {
/**
* Passport 请求参数
* @since Beta v0.9.1
*/
type PassportReqParams = {
/** 设备ID */
DeviceId: string;
/** 邮箱 */
UserName: string | null;
/**
* 新邮箱
* @remarks 用于账号迁移
*/
NewUserName: string | null;
/** 密码 */
Password: string | null;
/** 邮箱验证码 */
VerifyCode: string | null;
/**
* 新邮箱验证码
* @remarks 用于账号迁移
*/
NewVerifyCode: string | null;
/** 重置密码Flag */
IsResetPassword: boolean;
/** 校验邮箱Flag */
IsResetUserName: boolean;
/** 校验新邮箱Flag */
IsResetNewUserName: boolean;
/** 注销账号Flag */
IsCancelRegistration: boolean;
};
/**
* 登录请求响应
* @since Beta v0.9.1
*/
type LoginResp = TGApp.Plugins.Hutao.Base.Resp<LoginRes>;
/**
* 登录请求返回
* @since Beta v0.9.1
*/
type LoginRes = {
/** token */
AccessToken: string;
/** expire */
ExpiresIn: number;
/** refresh */
RefreshToken: string;
};
}

View File

@@ -0,0 +1,27 @@
/**
* 账号认证相关方法
* @since Beta v0.9.1
*/
import { commands } from "@skipperndt/plugin-machine-uid";
import { getVersion } from "@tauri-apps/api/app";
let DEVICE_ID: string | null = null;
/**
* 获取请求头
* @since Beta v0.9.1
*/
export async function getReqHeader(tk?: string): Promise<Record<string, string>> {
const version = await getVersion();
if (DEVICE_ID === null) {
const deviceRes = await commands.getMachineUid();
if (deviceRes.status === "ok") DEVICE_ID = deviceRes.data.id;
}
const device = DEVICE_ID ?? "";
return {
"Content-Type": "application/json",
"x-hutao-device-id": device,
Authorization: tk ? `Bearer ${tk}` : "",
"User-Agent": `TeyvatGuide/${version}`,
};
}