mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2025-12-12 09:18:14 +08:00
✨ 扫码登录
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<template>
|
||||
<ToGameLogin v-model="showLoginQr" @success="tryGetTokens" />
|
||||
<v-card class="tcu-box">
|
||||
<template #prepend>
|
||||
<v-avatar :image="userInfo.avatar" />
|
||||
@@ -36,12 +37,18 @@
|
||||
</template>
|
||||
<template #actions>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
variant="outlined"
|
||||
@click="tryCaptchaLogin()"
|
||||
icon="mdi-cellphone"
|
||||
title="验证码登录"
|
||||
/>
|
||||
<!-- <v-btn-->
|
||||
<!-- variant="outlined"-->
|
||||
<!-- @click="showLoginQr = true"-->
|
||||
<!-- icon="mdi-qrcode-scan"-->
|
||||
<!-- title="扫码登录"-->
|
||||
<!-- />-->
|
||||
<!-- <v-btn-->
|
||||
<!-- variant="outlined"-->
|
||||
<!-- @click="tryCaptchaLogin()"-->
|
||||
<!-- icon="mdi-cellphone"-->
|
||||
<!-- title="验证码登录"-->
|
||||
<!-- />-->
|
||||
<v-btn
|
||||
variant="outlined"
|
||||
@click="confirmRefreshUser(uid!)"
|
||||
@@ -62,6 +69,7 @@
|
||||
variant="outlined"
|
||||
icon="mdi-account-switch"
|
||||
title="切换账户"
|
||||
:disabled="accounts.length === 0"
|
||||
@click="showMenu"
|
||||
v-bind="props"
|
||||
/>
|
||||
@@ -90,10 +98,25 @@
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<v-menu location="start">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn variant="outlined" icon="mdi-login" title="登录" v-bind="props" />
|
||||
</template>
|
||||
<v-list>
|
||||
<v-list-item @click="addByCookie()" append-icon="mdi-account-plus">
|
||||
<v-list-item-title>手动添加</v-list-item-title>
|
||||
<v-list-item-subtitle>手动输入Cookie</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item @click="tryCaptchaLogin()" append-icon="mdi-cellphone">
|
||||
<v-list-item-title>验证码登录</v-list-item-title>
|
||||
<v-list-item-subtitle>使用手机号登录</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
<v-list-item @click="showLoginQr = true" append-icon="mdi-qrcode-scan">
|
||||
<v-list-item-title>扫码登录</v-list-item-title>
|
||||
<v-list-item-subtitle>使用米游社扫码登录</v-list-item-subtitle>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
@@ -104,10 +127,11 @@ import showDialog from "@comp/func/dialog.js";
|
||||
import showGeetest from "@comp/func/geetest.js";
|
||||
import showLoading from "@comp/func/loading.js";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import ToGameLogin from "@comp/pageConfig/tco-gameLogin.vue";
|
||||
import Mys from "@Mys/index.js";
|
||||
import TSUserAccount from "@Sqlite/modules/userAccount.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, shallowRef } from "vue";
|
||||
import { computed, ref, shallowRef } from "vue";
|
||||
|
||||
import { useAppStore } from "@/store/modules/app.js";
|
||||
import { useUserStore } from "@/store/modules/user.js";
|
||||
@@ -119,6 +143,7 @@ import TakumiApi from "@/web/request/takumiReq.js";
|
||||
const { isLogin } = storeToRefs(useAppStore());
|
||||
const { uid, briefInfo, cookie, account } = storeToRefs(useUserStore());
|
||||
|
||||
const showLoginQr = ref<boolean>(false);
|
||||
const accounts = shallowRef<Array<TGApp.App.Account.User>>([]);
|
||||
const gameAccounts = shallowRef<Array<TGApp.Sqlite.Account.Game>>([]);
|
||||
const userInfo = computed<TGApp.App.Account.BriefInfo>(() => {
|
||||
@@ -131,37 +156,7 @@ const userInfo = computed<TGApp.App.Account.BriefInfo>(() => {
|
||||
};
|
||||
});
|
||||
|
||||
async function tryCaptchaLogin(): Promise<void> {
|
||||
const phone = await showDialog.input("请输入手机号", "+86");
|
||||
if (!phone) {
|
||||
showSnackbar.cancel("已取消验证码登录");
|
||||
return;
|
||||
}
|
||||
const phoneReg = /^1[3-9]\d{9}$/;
|
||||
if (!phoneReg.test(phone)) {
|
||||
showSnackbar.warn("请输入正确的手机号");
|
||||
return;
|
||||
}
|
||||
const actionType = await tryGetCaptcha(phone);
|
||||
if (!actionType) return;
|
||||
showSnackbar.success(`已发送验证码到 ${phone}`);
|
||||
const captcha = await showDialog.input("请输入验证码", "验证码:", undefined, false);
|
||||
if (!captcha) {
|
||||
showSnackbar.warn("输入验证码为空");
|
||||
return;
|
||||
}
|
||||
const loginResp = await tryLoginByCaptcha(phone, captcha, actionType);
|
||||
if (!loginResp) return;
|
||||
await showLoading.start("正在尝试登录...");
|
||||
const ck: TGApp.App.Account.Cookie = {
|
||||
account_id: loginResp.user_info.aid,
|
||||
ltuid: loginResp.user_info.aid,
|
||||
stuid: loginResp.user_info.aid,
|
||||
mid: loginResp.user_info.mid,
|
||||
cookie_token: "",
|
||||
stoken: loginResp.token.token,
|
||||
ltoken: "",
|
||||
};
|
||||
async function tryGetTokens(ck: TGApp.App.Account.Cookie): Promise<void> {
|
||||
await showLoading.update("正在获取 LToken");
|
||||
const ltokenRes = await PassportApi.lToken.get(ck);
|
||||
if (typeof ltokenRes !== "string") {
|
||||
@@ -231,6 +226,40 @@ async function tryCaptchaLogin(): Promise<void> {
|
||||
showSnackbar.success("成功登录!");
|
||||
}
|
||||
|
||||
async function tryCaptchaLogin(): Promise<void> {
|
||||
const phone = await showDialog.input("请输入手机号", "+86");
|
||||
if (!phone) {
|
||||
showSnackbar.cancel("已取消验证码登录");
|
||||
return;
|
||||
}
|
||||
const phoneReg = /^1[3-9]\d{9}$/;
|
||||
if (!phoneReg.test(phone)) {
|
||||
showSnackbar.warn("请输入正确的手机号");
|
||||
return;
|
||||
}
|
||||
const actionType = await tryGetCaptcha(phone);
|
||||
if (!actionType) return;
|
||||
showSnackbar.success(`已发送验证码到 ${phone}`);
|
||||
const captcha = await showDialog.input("请输入验证码", "验证码:", undefined, false);
|
||||
if (!captcha) {
|
||||
showSnackbar.warn("输入验证码为空");
|
||||
return;
|
||||
}
|
||||
const loginResp = await tryLoginByCaptcha(phone, captcha, actionType);
|
||||
if (!loginResp) return;
|
||||
await showLoading.start("正在尝试登录...");
|
||||
const ck: TGApp.App.Account.Cookie = {
|
||||
account_id: loginResp.user_info.aid,
|
||||
ltuid: loginResp.user_info.aid,
|
||||
stuid: loginResp.user_info.aid,
|
||||
mid: loginResp.user_info.mid,
|
||||
cookie_token: "",
|
||||
stoken: loginResp.token.token,
|
||||
ltoken: "",
|
||||
};
|
||||
await tryGetTokens(ck);
|
||||
}
|
||||
|
||||
async function refreshUser(uid: string) {
|
||||
let account = await TSUserAccount.account.getAccount(uid);
|
||||
if (!account) {
|
||||
@@ -375,6 +404,7 @@ async function tryGetCaptcha(phone: string, aigis?: string): Promise<string | fa
|
||||
showSnackbar.error(`[${captchaResp.retcode}] ${captchaResp.message}`);
|
||||
return false;
|
||||
}
|
||||
// @ts-expect-error type {} is not assignable to type string
|
||||
const aigisResp: TGApp.Plugins.Mys.CaptchaLogin.CaptchaAigis = JSON.parse(captchaResp.data);
|
||||
const resp = await showGeetest(JSON.parse(aigisResp.data));
|
||||
const aigisStr = `${aigisResp.session_id};${btoa(JSON.stringify(resp))}`;
|
||||
|
||||
129
src/components/pageConfig/tco-gameLogin.vue
Normal file
129
src/components/pageConfig/tco-gameLogin.vue
Normal file
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<TOverlay v-model="model" hide blur-val="20px">
|
||||
<div class="tog-box">
|
||||
<div class="tog-top">
|
||||
<div class="tog-title">请使用米游社进行扫码操作</div>
|
||||
</div>
|
||||
<div class="tog-mid">
|
||||
<qrcode-vue
|
||||
v-if="codeData"
|
||||
class="tog-qr"
|
||||
:value="codeData.url"
|
||||
render-as="svg"
|
||||
:background="'var(--box-bg-1)'"
|
||||
foreground="var(--box-text-1)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</TOverlay>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import TOverlay from "@comp/app/t-overlay.vue";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import QrcodeVue from "qrcode.vue";
|
||||
import { onUnmounted, shallowRef, watch } from "vue";
|
||||
|
||||
import PassportReq from "@/web/request/passportReq.js";
|
||||
|
||||
type ToGameLoginEmits = (e: "success", data: TGApp.App.Account.Cookie) => void;
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
let cycleTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
const model = defineModel<boolean>({ default: false });
|
||||
const emits = defineEmits<ToGameLoginEmits>();
|
||||
const codeData = shallowRef<TGApp.BBS.GameLogin.GetLoginQrData>();
|
||||
|
||||
watch(model, async (value) => {
|
||||
if (value) {
|
||||
await freshQr();
|
||||
cycleTimer = setInterval(cycleGetData, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
async function freshQr(): Promise<void> {
|
||||
const res = await PassportReq.qrLogin.create();
|
||||
if ("retcode" in res) {
|
||||
showSnackbar.error(`[${res.retcode}] ${res.message}`);
|
||||
return;
|
||||
}
|
||||
codeData.value = res;
|
||||
}
|
||||
|
||||
async function cycleGetData() {
|
||||
if (cycleTimer === null || !codeData.value) return;
|
||||
const res = await PassportReq.qrLogin.query(codeData.value.ticket);
|
||||
console.log(res);
|
||||
if ("retcode" in res) {
|
||||
showSnackbar.error(`[${res.retcode}] ${res.message}`);
|
||||
if (res.retcode === -106) {
|
||||
await freshQr();
|
||||
} else {
|
||||
clearInterval(cycleTimer);
|
||||
cycleTimer = null;
|
||||
model.value = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (res.status === "Created" || res.status === "Scanned") return;
|
||||
if (res.status === "Confirmed") {
|
||||
clearInterval(cycleTimer);
|
||||
cycleTimer = null;
|
||||
const ck: TGApp.App.Account.Cookie = {
|
||||
mid: res.user_info.mid,
|
||||
stoken: res.tokens[0].token,
|
||||
stuid: res.user_info.aid,
|
||||
account_id: res.user_info.aid,
|
||||
cookie_token: "",
|
||||
ltoken: "",
|
||||
ltuid: "",
|
||||
};
|
||||
emits("success", ck);
|
||||
model.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (cycleTimer !== null) clearInterval(cycleTimer);
|
||||
cycleTimer = null;
|
||||
});
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
.tog-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
background-color: var(--box-bg-1);
|
||||
color: var(--app-page-content);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tog-top {
|
||||
border-bottom: 1px solid var(--common-shadow-4);
|
||||
font-family: var(--font-title);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tog-title {
|
||||
color: var(--common-text-title);
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.tog-mid {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
border: 1px solid var(--common-shadow-2);
|
||||
border-radius: 5px;
|
||||
aspect-ratio: 1;
|
||||
background: var(--box-bg-2);
|
||||
}
|
||||
|
||||
.tog-qr {
|
||||
width: 256px;
|
||||
height: 256px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,12 +1,11 @@
|
||||
/**
|
||||
* @file plugins/Mys/index.ts
|
||||
* @description Mys plugin index
|
||||
* @since Beta v0.6.3
|
||||
* @since Beta v0.6.8
|
||||
*/
|
||||
|
||||
import * as ApiHub from "./request/apiHubReq.js";
|
||||
import { doCaptchaLogin, getCaptcha } from "./request/doCaptchaLogin.js";
|
||||
import { getLoginQr, getLoginStatus } from "./request/doGameLogin.js";
|
||||
import { getGachaData, getPositionData } from "./request/obcReq.js";
|
||||
import * as Painter from "./request/painterReq.js";
|
||||
import * as Post from "./request/postReq.js";
|
||||
@@ -21,7 +20,7 @@ const Mys = {
|
||||
Gacha: { get: getGachaData, card: getGachaCard },
|
||||
Position: { get: getPositionData, card: getPositionCard },
|
||||
Lottery: { get: Painter.lotteryUserShow, card: getLotteryCard },
|
||||
User: { getQr: getLoginQr, getData: getLoginStatus, getCaptcha, login: doCaptchaLogin },
|
||||
User: { getCaptcha, login: doCaptchaLogin },
|
||||
};
|
||||
|
||||
export default Mys;
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/**
|
||||
* @file plugins/Mys/request/doGameLogin
|
||||
* @description 获取 gameToken,曲线获取 stoken
|
||||
* @since Beta v0.5.0
|
||||
*/
|
||||
|
||||
import TGHttp from "@/utils/TGHttp.js";
|
||||
import { getDeviceInfo } from "@/utils/toolFunc.js";
|
||||
import { getRequestHeader } from "@/web/utils/getRequestHeader.js";
|
||||
|
||||
const APP_ID = 8;
|
||||
|
||||
/**
|
||||
* @description 获取登录二维码
|
||||
* @since Beta v0.5.0
|
||||
* @returns {Promise<TGApp.Plugins.Mys.GameLogin.GetLoginQrData|TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
export async function getLoginQr(): Promise<
|
||||
TGApp.Plugins.Mys.GameLogin.GetLoginQrData | TGApp.BBS.Response.Base
|
||||
> {
|
||||
const data: Record<string, string | number> = {
|
||||
app_id: APP_ID,
|
||||
device: getDeviceInfo("device_id"),
|
||||
};
|
||||
const resp = await TGHttp<
|
||||
TGApp.Plugins.Mys.GameLogin.GetLoginQrResponse | TGApp.BBS.Response.Base
|
||||
>("https://hk4e-sdk.mihoyo.com/hk4e_cn/combo/panda/qrcode/fetch", {
|
||||
method: "POST",
|
||||
headers: getRequestHeader({}, "POST", data),
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取登录状态
|
||||
* @since Beta v0.5.0
|
||||
* @param {string} ticket 二维码 ticket
|
||||
* @returns {Promise<TGApp.Plugins.Mys.GameLogin.GetLoginStatusData | TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
export async function getLoginStatus(
|
||||
ticket: string,
|
||||
): Promise<TGApp.Plugins.Mys.GameLogin.GetLoginStatusData | TGApp.BBS.Response.Base> {
|
||||
const data: Record<string, string | number> = {
|
||||
app_id: APP_ID,
|
||||
device: getDeviceInfo("device_id"),
|
||||
ticket,
|
||||
};
|
||||
const resp = await TGHttp<
|
||||
TGApp.Plugins.Mys.GameLogin.GetLoginStatusResponse | TGApp.BBS.Response.Base
|
||||
>("https://hk4e-sdk.mihoyo.com/hk4e_cn/combo/panda/qrcode/query", {
|
||||
method: "POST",
|
||||
headers: getRequestHeader({}, "POST", data),
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data;
|
||||
}
|
||||
102
src/plugins/Mys/types/GameLogin.d.ts
vendored
102
src/plugins/Mys/types/GameLogin.d.ts
vendored
@@ -1,102 +0,0 @@
|
||||
/**
|
||||
* @file plugins/Mys/types/GameLogin.d.ts
|
||||
* @description Mys 插件 Game 登录类型定义文件
|
||||
* @since Beta v0.4.4
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description Mys 插件 Game 登录类型
|
||||
* @since Beta v0.4.4
|
||||
* @namespace TGApp.Plugins.Mys.GameLogin
|
||||
* @memberof TGApp.Plugins.Mys
|
||||
*/
|
||||
declare namespace TGApp.Plugins.Mys.GameLogin {
|
||||
/**
|
||||
* @description 获取登录二维码返回数据
|
||||
* @since Beta v0.3.0
|
||||
* @interface GetLoginQrResponse
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {GetLoginQrData} data 数据
|
||||
* @return GetLoginQrResponse
|
||||
*/
|
||||
interface GetLoginQrResponse extends TGApp.BBS.Response.BaseWithData {
|
||||
data: GetLoginQrData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取登录二维码返回数据
|
||||
* @since Beta v0.3.0
|
||||
* @interface GetLoginQrData
|
||||
* @property {string} url 二维码链接
|
||||
* @return GetLoginQrData
|
||||
*/
|
||||
interface GetLoginQrData {
|
||||
url: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取登录状态返回数据
|
||||
* @since Beta v0.3.0
|
||||
* @interface GetLoginStatusResponse
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {GetLoginStatusData} data 数据
|
||||
* @return GetLoginStatusResponse
|
||||
*/
|
||||
interface GetLoginStatusResponse extends TGApp.BBS.Response.BaseWithData {
|
||||
data: GetLoginStatusData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取登录状态返回数据
|
||||
* @since Beta v0.3.0
|
||||
* @interface GetLoginStatusData
|
||||
* @property {string} stat 状态 // Init: 未扫码,Scanned: 已扫码,Confirmed: 已确认
|
||||
* @property {StatusPayload} payload 状态数据
|
||||
* @return GetLoginStatusData
|
||||
*/
|
||||
interface GetLoginStatusData {
|
||||
stat: string;
|
||||
payload: StatusPayload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取登录状态返回数据
|
||||
* @since Beta v0.3.0
|
||||
* @interface StatusPayload
|
||||
* @property {string} ext 未知
|
||||
* @property {string} proto 未知
|
||||
* @property {string} raw 序列化数据,反序列化后是 {uid: string, token: string}
|
||||
* @return StatusPayload
|
||||
*/
|
||||
interface StatusPayload {
|
||||
ext: string;
|
||||
proto: string;
|
||||
raw: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 反序列化后的登录状态数据
|
||||
* @since Beta v0.4.4
|
||||
* @interface StatusPayloadRawOpen
|
||||
* @property {string} uid 用户 UID
|
||||
* @property {string} token 用户 token
|
||||
* @return StatusPayloadRawOpen
|
||||
*/
|
||||
interface StatusPayloadRawOpen {
|
||||
open_id: string;
|
||||
open_token: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 反序列化后的登录状态数据
|
||||
* @since Beta v0.4.4
|
||||
* @interface StatusPayloadRawAccount
|
||||
* @property {string} uid 用户 UID
|
||||
* @property {string} token 用户 token
|
||||
* @return StatusPayloadRawOpen
|
||||
*/
|
||||
interface StatusPayloadRawAccount {
|
||||
uid: string;
|
||||
token: string;
|
||||
}
|
||||
}
|
||||
152
src/types/BBS/GameLogin.d.ts
vendored
Normal file
152
src/types/BBS/GameLogin.d.ts
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* @file types/BBS/GameLogin.d.ts
|
||||
* @description Mys 插件 Game 登录类型定义文件
|
||||
* @since Beta v0.6.8
|
||||
*/
|
||||
|
||||
declare namespace TGApp.BBS.GameLogin {
|
||||
/**
|
||||
* @description 获取登录二维码返回数据
|
||||
* @since Beta v0.3.0
|
||||
* @interface GetLoginQrResponse
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {GetLoginQrData} data 数据
|
||||
* @return GetLoginQrResponse
|
||||
*/
|
||||
type GetLoginQrResponse = TGApp.BBS.Response.BaseWithData & { data: GetLoginQrData };
|
||||
|
||||
/**
|
||||
* @description 获取登录二维码返回数据
|
||||
* @since Beta v0.6.8
|
||||
* @interface GetLoginQrData
|
||||
* @property {string} ticket 二维码 ticket,用于查询登录状态
|
||||
* @property {string} url 二维码链接
|
||||
* @return GetLoginQrData
|
||||
*/
|
||||
type GetLoginQrData = { ticket: string; url: string };
|
||||
|
||||
/**
|
||||
* @description 获取登录状态返回数据
|
||||
* @since Beta v0.3.0
|
||||
* @interface GetLoginStatusResponse
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {GetLoginStatusData} data 数据
|
||||
* @return GetLoginStatusResponse
|
||||
*/
|
||||
type GetLoginStatusResponse = TGApp.BBS.Response.BaseWithData & { data: GetLoginStatusData };
|
||||
|
||||
/**
|
||||
* @description 获取登录状态返回数据-通用
|
||||
* @since Beta v0.6.8
|
||||
* @interface GetLoginStatusDataCommon
|
||||
* @property {string} app_id 应用 ID
|
||||
* @property {number} client_type 客户端类型
|
||||
* @property {string} created_at 创建时间
|
||||
* @property {string} ext 未知
|
||||
* @property {boolean} need_realperson 是否需要真人验证
|
||||
* @property {unknown} realname_info 未知
|
||||
* @property {string} scan_game_biz 未知
|
||||
* @property {string} scanned_at 扫描时间
|
||||
* @return GetLoginStatusDataCommon
|
||||
*/
|
||||
type GetLoginStatusDataCommon = {
|
||||
app_id: string;
|
||||
client_type: number;
|
||||
created_at: string;
|
||||
ext: string;
|
||||
need_realperson: boolean;
|
||||
realname_info: unknown;
|
||||
scan_game_biz: string;
|
||||
scanned_at: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 获取登录状态返回数据-未确认
|
||||
* @since Beta v0.6.8
|
||||
* @interface GetLoginStatusDataUnconfirmed
|
||||
* @extends GetLoginStatusDataCommon
|
||||
* @property {"Created" | "Scanned"} status 状态
|
||||
* @return GetLoginStatusDataUnconfirmed
|
||||
*/
|
||||
type GetLoginStatusDataUnconfirmed = GetLoginStatusDataCommon & {
|
||||
status: "Created" | "Scanned";
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 获取登录状态返回数据-已确认
|
||||
* @since Beta v0.6.8
|
||||
* @interface GetLoginStatusDataConfirmed
|
||||
* @extends GetLoginStatusDataCommon
|
||||
*/
|
||||
type GetLoginStatusDataConfirmed = GetLoginStatusDataCommon & {
|
||||
status: "Confirmed";
|
||||
tokens: Array<GetLoginStatusDataToken>;
|
||||
user_info: GetLoginStatusDataUserInfo;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 获取登录状态返回数据-Token
|
||||
* @since Beta v0.6.8
|
||||
* @interface GetLoginStatusDataToken
|
||||
* @property {string} token token
|
||||
* @property {number} token_type token 类型
|
||||
* @return GetLoginStatusDataToken
|
||||
*/
|
||||
type GetLoginStatusDataToken = { token: string; token_type: number };
|
||||
|
||||
/**
|
||||
* @description 获取登录状态返回数据-用户信息
|
||||
* @since Beta v0.6.8
|
||||
* @interface GetLoginStatusDataUserInfo
|
||||
* @property {string} account_name 账号名称
|
||||
* @property {string} aid 账号 ID
|
||||
* @property {string} area_code 区域代码
|
||||
* @property {string} country 国家
|
||||
* @property {string} email 邮箱
|
||||
* @property {string} identity_code 身份证号
|
||||
* @property {number} is_email_verify 是否验证邮箱
|
||||
* @property {Array<unknown>} links 链接
|
||||
* @property {string} mid mid
|
||||
* @property {string} mobile 手机号
|
||||
* @property {string} password_time 密码时间
|
||||
* @property {string} realname 真实姓名
|
||||
* @property {string} rebind_area_code 重新绑定区域代码
|
||||
* @property {string} rebind_mobile 重新绑定手机号
|
||||
* @property {string} rebind_mobile_time 重新绑定手机号时间
|
||||
* @property {string} safe_area_code 安全区域代码
|
||||
* @property {string} safe_mobile 安全手机号
|
||||
* @property {string} unmasked_email 未屏蔽邮箱
|
||||
* @property {number} unmasked_email_type 未屏蔽邮箱类型
|
||||
* @return GetLoginStatusDataUserInfo
|
||||
*/
|
||||
type GetLoginStatusDataUserInfo = {
|
||||
account_name: string;
|
||||
aid: string;
|
||||
area_code: string;
|
||||
country: string;
|
||||
email: string;
|
||||
identity_code: string;
|
||||
is_email_verify: number;
|
||||
links: Array<unknown>;
|
||||
mid: string;
|
||||
mobile: string;
|
||||
password_time: string;
|
||||
realname: string;
|
||||
rebind_area_code: string;
|
||||
rebind_mobile: string;
|
||||
rebind_mobile_time: string;
|
||||
safe_area_code: string;
|
||||
safe_mobile: string;
|
||||
unmasked_email: string;
|
||||
unmasked_email_type: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 获取登录状态返回数据
|
||||
* @since Beta v0.6.8
|
||||
* @interface GetLoginStatusData
|
||||
* @property {GetLoginStatusDataUnconfirmed | GetLoginStatusDataConfirmed} data 数据
|
||||
* @return GetLoginStatusData
|
||||
*/
|
||||
type GetLoginStatusData = GetLoginStatusDataUnconfirmed | GetLoginStatusDataConfirmed;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* @file web/constant/bbs.ts
|
||||
* @description 常量-应用数据
|
||||
* @since Beta v0.6.7
|
||||
* @since Beta v0.6.8
|
||||
*/
|
||||
|
||||
export const BBS_VERSION: Readonly<string> = "2.79.1";
|
||||
export const BBS_VERSION: Readonly<string> = "2.80.1";
|
||||
export const BBS_UA_MOBILE: Readonly<string> = `Mozilla/5.0 (Linux; Android 12) Mobile miHoYoBBS/${BBS_VERSION}`;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
/**
|
||||
* @file web/request/passportReq.ts
|
||||
* @description Passport 相关请求
|
||||
* @since Beta v0.6.3
|
||||
* @since Beta v0.6.8
|
||||
*/
|
||||
import TGHttp from "@/utils/TGHttp.js";
|
||||
import { getDeviceInfo } from "@/utils/toolFunc.js";
|
||||
import { getRequestHeader } from "@/web/utils/getRequestHeader.js";
|
||||
|
||||
// PassportApiBaseUrl => pAbu
|
||||
const pAbu: Readonly<string> = "https://passport-api.mihoyo.com/";
|
||||
// PassportV4ApiBaseUrl => p4Abu
|
||||
const p4Abu: Readonly<string> = "https://passport-api-v4.mihoyo.com/";
|
||||
// HoyoLauncherVersion
|
||||
const hlv: Readonly<string> = "1.3.3.182";
|
||||
|
||||
/**
|
||||
* @description 获取登录ticket
|
||||
@@ -40,6 +43,30 @@ async function createAuthTicketByGameBiz(
|
||||
return resp.data.ticket;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 创建登录二维码
|
||||
* @since Beta v0.6.8
|
||||
* @returns {Promise<TGApp.BBS.Response.Base|TGApp.BBS.GameLogin.GetLoginQrData>}
|
||||
*/
|
||||
async function createQrLogin(): Promise<
|
||||
TGApp.BBS.Response.Base | TGApp.BBS.GameLogin.GetLoginQrData
|
||||
> {
|
||||
const resp = await TGHttp<TGApp.BBS.GameLogin.GetLoginQrResponse>(
|
||||
`${pAbu}account/ma-cn-passport/app/createQRLogin`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"x-rpc-device_id": getDeviceInfo("device_id"),
|
||||
"user-agent": `HYPContainer/${hlv}`,
|
||||
"x-rpc-app_id": "ddxf5dufpuyo",
|
||||
"x-rpc-client_type": "3",
|
||||
},
|
||||
},
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 根据 stoken 获取 cookie_token
|
||||
* @since Beta v0.6.3
|
||||
@@ -78,6 +105,32 @@ async function getLTokenBySToken(
|
||||
return resp.data.ltoken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取登录状态
|
||||
* @since Beta v0.6.8
|
||||
* @param {string} ticket - 二维码 ticket
|
||||
* @returns {Promise<TGApp.BBS.Response.Base|TGApp.BBS.GameLogin.GetLoginStatusData>}
|
||||
*/
|
||||
async function queryLoginStatus(
|
||||
ticket: string,
|
||||
): Promise<TGApp.BBS.Response.Base | TGApp.BBS.GameLogin.GetLoginStatusData> {
|
||||
const resp = await TGHttp<TGApp.BBS.GameLogin.GetLoginStatusResponse>(
|
||||
`${pAbu}account/ma-cn-passport/app/queryQRLoginStatus`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"x-rpc-device_id": getDeviceInfo("device_id"),
|
||||
"user-agent": `HYPContainer/${hlv}`,
|
||||
"x-rpc-app_id": "ddxf5dufpuyo",
|
||||
"x-rpc-client_type": "3",
|
||||
},
|
||||
body: JSON.stringify({ ticket }),
|
||||
},
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 验证 ltoken 有效性,返回 mid
|
||||
* @since Beta v0.6.5
|
||||
@@ -102,6 +155,7 @@ const PassportApi = {
|
||||
authTicket: createAuthTicketByGameBiz,
|
||||
cookieToken: getCookieAccountInfoBySToken,
|
||||
lToken: { get: getLTokenBySToken, verify: verifyLToken },
|
||||
qrLogin: { create: createQrLogin, query: queryLoginStatus },
|
||||
};
|
||||
|
||||
export default PassportApi;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @file web/utils/getRequestHeader.ts
|
||||
* @description 获取请求头
|
||||
* @since Beta v0.6.7
|
||||
* @since Beta v0.6.8
|
||||
*/
|
||||
|
||||
import Md5 from "js-md5";
|
||||
@@ -24,12 +24,12 @@ const enum SaltType {
|
||||
|
||||
/**
|
||||
* @description salt 值
|
||||
* @version 2.79.1
|
||||
* @version 2.80.1
|
||||
* @since Beta v0.6.7
|
||||
*/
|
||||
const Salt: Readonly<Record<keyof typeof SaltType, string>> = {
|
||||
K2: "eOzxpHXVGBVdFBtkbkTvwyCgRpqRFeBr",
|
||||
LK2: "AbuxbruiFDIgxLXksUNMAMvDyciznofM",
|
||||
K2: "G1rXOpMLQS77VPWEGycOSxekCTZe2Q8M",
|
||||
LK2: "sd1e1gQJuvqBfZxas1oeAACXzbim5cge",
|
||||
X4: "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs",
|
||||
X6: "t0qEgfub6cvueAPgR5m9aQWWVciEer7v",
|
||||
PROD: "t0qEgfub6cvueAPgR5m9aQWWVciEer7v",
|
||||
|
||||
Reference in New Issue
Block a user