mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-03-19 04:29:45 +08:00
✨ comboToken
This commit is contained in:
@@ -107,9 +107,12 @@
|
||||
<img src="/platforms/mhy/launcher.webp" alt="launcher" class="menu-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
<v-list-item @click="tryCodeLogin(false)" append-icon="mdi-qrcode-scan" v-if="false">
|
||||
<v-list-item @click="tryCodeLogin(false)">
|
||||
<v-list-item-title>扫码登录(游戏)</v-list-item-title>
|
||||
<v-list-item-subtitle>使用米游社扫码登录</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<img src="/platforms/mhy/mys.webp" alt="launcher" class="menu-icon" />
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
@@ -571,5 +574,6 @@ async function clearUser(user: TGApp.App.Account.User): Promise<void> {
|
||||
.menu-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,7 +3,20 @@
|
||||
<div class="tog-box">
|
||||
<div class="tog-top">
|
||||
<div class="tog-title">请使用米游社进行扫码操作</div>
|
||||
<div class="tog-select" v-if="!isLauncherCode">
|
||||
<div
|
||||
class="tog-select-item"
|
||||
v-for="item in selects"
|
||||
:key="item.value"
|
||||
:class="{ active: codeGid === item.value }"
|
||||
@click="codeGid = item.value"
|
||||
:title="item.title"
|
||||
>
|
||||
<img :src="item.icon" alt="icon" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tog-divider" />
|
||||
<div class="tog-mid">
|
||||
<qrcode-vue
|
||||
v-if="codeUrl"
|
||||
@@ -35,6 +48,20 @@ import passportReq from "@/web/request/passportReq.js";
|
||||
import takumiReq from "@/web/request/takumiReq.js";
|
||||
|
||||
type ToGameLoginEmits = (e: "success", data: TGApp.App.Account.Cookie) => void;
|
||||
type ToGameLoginSelect = { title: string; value: number; icon: string };
|
||||
|
||||
const selects: Array<ToGameLoginSelect> = [
|
||||
{
|
||||
title: "未定事件簿",
|
||||
value: 2,
|
||||
icon: "/platforms/mhy/wd.webp",
|
||||
},
|
||||
{
|
||||
title: "崩坏学园2",
|
||||
value: 7,
|
||||
icon: "/platforms/mhy/bh2.webp",
|
||||
},
|
||||
];
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
let cycleTimer: NodeJS.Timeout | null = null;
|
||||
@@ -42,6 +69,7 @@ let cycleTimer: NodeJS.Timeout | null = null;
|
||||
const model = defineModel<boolean>({ default: false });
|
||||
const isLauncherCode = defineModel<boolean>("launcher", { default: true });
|
||||
const emits = defineEmits<ToGameLoginEmits>();
|
||||
const codeGid = ref<number>(7);
|
||||
const codeUrl = ref<string>();
|
||||
const codeTicket = ref<string>("");
|
||||
|
||||
@@ -55,7 +83,7 @@ watch(
|
||||
cycleTimer = null;
|
||||
}
|
||||
if (isLauncherCode.value) cycleTimer = setInterval(cycleGetDataLauncher, 1000);
|
||||
else cycleTimer = setInterval(cycleGetDataGame, 5000);
|
||||
else cycleTimer = setInterval(cycleGetDataGame, 1000);
|
||||
} else {
|
||||
if (cycleTimer) clearInterval(cycleTimer);
|
||||
cycleTimer = null;
|
||||
@@ -63,6 +91,16 @@ watch(
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => codeGid.value,
|
||||
async () => {
|
||||
if (isLauncherCode.value) return;
|
||||
await freshQr();
|
||||
if (cycleTimer) clearInterval(cycleTimer);
|
||||
cycleTimer = setInterval(cycleGetDataGame, 1000);
|
||||
},
|
||||
);
|
||||
|
||||
async function share(): Promise<void> {
|
||||
const shareDom = document.querySelector<HTMLDivElement>(".tog-box");
|
||||
if (shareDom === null) {
|
||||
@@ -83,7 +121,7 @@ async function freshQr(): Promise<void> {
|
||||
codeTicket.value = resp.ticket;
|
||||
return;
|
||||
}
|
||||
const resp = await hk4eReq.loginQr.create();
|
||||
const resp = await hk4eReq.loginQr.create(codeGid.value);
|
||||
if ("retcode" in resp) {
|
||||
showSnackbar.error(`[${resp.retcode}] ${resp.message}`);
|
||||
return;
|
||||
@@ -125,7 +163,7 @@ async function cycleGetDataLauncher(): Promise<void> {
|
||||
}
|
||||
|
||||
async function cycleGetDataGame(): Promise<void> {
|
||||
const res = await hk4eReq.loginQr.state(codeTicket.value);
|
||||
const res = await hk4eReq.loginQr.state(codeTicket.value, codeGid.value);
|
||||
console.log(res);
|
||||
if ("retcode" in res) {
|
||||
showSnackbar.error(`[${res.retcode}] ${res.message}`);
|
||||
@@ -150,23 +188,22 @@ async function cycleGetDataGame(): Promise<void> {
|
||||
const statusRaw: TGApp.Game.Login.StatusPayloadRaw = JSON.parse(res.payload.raw);
|
||||
await showLoading.start("正在获取SToken");
|
||||
const stResp = await takumiReq.game.stoken(statusRaw);
|
||||
console.log(stResp);
|
||||
await showLoading.end();
|
||||
if ("retcode" in stResp) {
|
||||
showSnackbar.error(`[${stResp.retcode}] ${stResp.message}`);
|
||||
model.value = false;
|
||||
return;
|
||||
}
|
||||
// const ck: TGApp.App.Account.Cookie = {
|
||||
// account_id: statusRaw.uid,
|
||||
// ltuid: statusRaw.uid,
|
||||
// stuid: statusRaw.uid,
|
||||
// mid: res.user_info.mid,
|
||||
// cookie_token: "",
|
||||
// stoken: res.tokens[0].token,
|
||||
// ltoken: "",
|
||||
// };
|
||||
// emits("success", ck);
|
||||
const ck: TGApp.App.Account.Cookie = {
|
||||
account_id: statusRaw.uid,
|
||||
ltuid: statusRaw.uid,
|
||||
stuid: statusRaw.uid,
|
||||
mid: stResp.user_info.mid,
|
||||
cookie_token: "",
|
||||
stoken: stResp.token.token,
|
||||
ltoken: "",
|
||||
};
|
||||
emits("success", ck);
|
||||
model.value = false;
|
||||
}
|
||||
}
|
||||
@@ -188,8 +225,13 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.tog-top {
|
||||
border-bottom: 1px solid var(--common-shadow-4);
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: var(--font-title);
|
||||
row-gap: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -198,6 +240,41 @@ onUnmounted(() => {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.tog-select {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
column-gap: 4px;
|
||||
}
|
||||
|
||||
.tog-select-item {
|
||||
position: relative;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
border: 3px solid var(--tgc-od-orange);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.tog-divider {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: var(--common-shadow-2);
|
||||
}
|
||||
|
||||
.tog-mid {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
</template>
|
||||
<template #append>
|
||||
<span class="top-hint" @click="tryCkVerify()" title="点击验证">
|
||||
需要验证码登录所需cookie!!!
|
||||
需要验证码登录/游戏扫码登录所需cookie!!!
|
||||
</span>
|
||||
</template>
|
||||
</v-app-bar>
|
||||
@@ -76,6 +76,7 @@ import { onMounted, ref, shallowRef } from "vue";
|
||||
|
||||
import { useUserStore } from "@/store/modules/user.js";
|
||||
import apiHubReq from "@/web/request/apiHubReq.js";
|
||||
import miscReq from "@/web/request/miscReq.js";
|
||||
|
||||
const { uid, briefInfo, cookie, account } = storeToRefs(useUserStore());
|
||||
const accounts = shallowRef<Array<TGApp.App.Account.User>>([]);
|
||||
@@ -119,21 +120,31 @@ async function tryCkVerify(): Promise<void> {
|
||||
showSnackbar.cancel("已取消验证");
|
||||
return;
|
||||
}
|
||||
await showLoading.start("正在验证CK有效性");
|
||||
const ck = {
|
||||
stoken: cookie.value.stoken,
|
||||
stuid: cookie.value.stuid,
|
||||
mid: cookie.value.mid,
|
||||
};
|
||||
const resp = await apiHubReq.sign(ck);
|
||||
await showLoading.update(`[${resp.retcode}] ${resp.message}`);
|
||||
if (resp.retcode === -100) {
|
||||
showSnackbar.error("CK验证失败,请通过验证码登录重新获取CK");
|
||||
await showLoading.end();
|
||||
return;
|
||||
let flag = false;
|
||||
let challenge;
|
||||
while (!flag) {
|
||||
await showLoading.start("正在验证CK有效性");
|
||||
const resp = await apiHubReq.sign(ck, 2, challenge);
|
||||
await showLoading.update(`[${resp.retcode}] ${resp.message}`);
|
||||
if (resp.retcode === -100) {
|
||||
await showLoading.end();
|
||||
break;
|
||||
} else if (resp.retcode === 1034) {
|
||||
await showLoading.end();
|
||||
const cGet = await miscReq.challenge(ck);
|
||||
if (cGet !== false) challenge = cGet;
|
||||
} else {
|
||||
flag = true;
|
||||
await showLoading.end();
|
||||
}
|
||||
}
|
||||
await showLoading.end();
|
||||
showSnackbar.success("CK验证成功");
|
||||
if (!flag) showSnackbar.error("CK验证失败,请通过验证码登录重新获取CK");
|
||||
else showSnackbar.success("CK验证成功");
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
27
src/types/Game/Login.d.ts
vendored
27
src/types/Game/Login.d.ts
vendored
@@ -72,4 +72,31 @@ declare namespace TGApp.Game.Login {
|
||||
* @return StatusPayloadRaw
|
||||
*/
|
||||
type StatusPayloadRaw = { uid: string; token: string };
|
||||
|
||||
/**
|
||||
* @description GameToken获取Stoken返回
|
||||
* @since Beta v0.7.2
|
||||
* @interface StResp
|
||||
* @extends TGApp.BBS.Response.BaseWithData
|
||||
* @property {StRes} data 数据
|
||||
* @return StResp
|
||||
*/
|
||||
type StResp = TGApp.BBS.Response.BaseWithData<StRes>;
|
||||
|
||||
/**
|
||||
* @description GameToken获取Stoken返回数据
|
||||
* @since Beta v0.7.2
|
||||
* @interface StRes
|
||||
* @property {TGApp.BBS.GameLogin.GetLoginStatusDataToken} token token
|
||||
* @property {TGApp.BBS.GameLogin.GetLoginStatusDataUserInfo} user_info 用户信息
|
||||
* @property {unknown} realname_info 未知
|
||||
* @property {boolean} need_realperson 是否需要真人验证
|
||||
* @return StRes
|
||||
*/
|
||||
type StRes = {
|
||||
token: TGApp.BBS.GameLogin.GetLoginStatusDataToken;
|
||||
user_info: TGApp.BBS.GameLogin.GetLoginStatusDataUserInfo;
|
||||
realname_info: unknown;
|
||||
need_realperson: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -141,12 +141,12 @@ async function getGachaLog(
|
||||
|
||||
/**
|
||||
* @description 获取登录二维码
|
||||
* @since Beta v0.7.0
|
||||
* @since Beta v0.7.2
|
||||
* @param {string} appId 应用 ID // 目前只有2/7能用
|
||||
* @returns {Promise<TGApp.Game.Login.QrRes|TGApp.BBS.Response.Base>}
|
||||
*/
|
||||
async function fetchPandaQr(
|
||||
appId: string = "2",
|
||||
appId: number,
|
||||
): Promise<TGApp.Game.Login.QrRes | TGApp.BBS.Response.Base> {
|
||||
const data = { app_id: appId, device: getDeviceInfo("device_id") };
|
||||
const resp = await TGHttp<TGApp.Game.Login.QrResp>(`${SdkApi}combo/panda/qrcode/fetch`, {
|
||||
@@ -159,14 +159,14 @@ async function fetchPandaQr(
|
||||
|
||||
/**
|
||||
* @description 获取登录状态
|
||||
* @since Beta v0.7.0
|
||||
* @since Beta v0.7.2
|
||||
* @param {string} ticket 二维码 ticket
|
||||
* @param {string} appId 应用 ID
|
||||
* @returns {Promise<TGApp.BBS.Response.Base|TGApp.Game.Login.StatusRes>}
|
||||
*/
|
||||
async function queryPandaQr(
|
||||
ticket: string,
|
||||
appId: string = "2",
|
||||
appId: number,
|
||||
): Promise<TGApp.BBS.Response.Base | TGApp.Game.Login.StatusRes> {
|
||||
const data = { app_id: appId, ticket, device: getDeviceInfo("device_id") };
|
||||
const resp = await TGHttp<TGApp.Game.Login.StatusResp>(`${SdkApi}combo/panda/qrcode/query`, {
|
||||
|
||||
@@ -3,46 +3,30 @@
|
||||
* @description Takumi 相关请求函数
|
||||
* @since Beta v0.7.2
|
||||
*/
|
||||
import TGBbs from "@/utils/TGBbs.js";
|
||||
import TGHttp from "@/utils/TGHttp.js";
|
||||
import { getDeviceInfo } from "@/utils/toolFunc.js";
|
||||
import { getDS, getRequestHeader } from "@/web/utils/getRequestHeader.js";
|
||||
import { getRequestHeader } from "@/web/utils/getRequestHeader.js";
|
||||
|
||||
// TakumiApiBaseUrl => taBu
|
||||
const taBu: Readonly<string> = "https://api-takumi.mihoyo.com/";
|
||||
|
||||
/**
|
||||
* @description 根据gameToken获取stoken
|
||||
* @todo -100
|
||||
* @since Beta v0.7.2
|
||||
* @param {TGApp.Game.Login.StatusPayloadRaw} raw 状态数据
|
||||
* @returns {Promise<TGApp.BBS.Response.Base|string>}
|
||||
*/
|
||||
async function getSTokenByGameToken(
|
||||
raw: TGApp.Game.Login.StatusPayloadRaw,
|
||||
): Promise<TGApp.BBS.Response.Base> {
|
||||
const data = { account_id: raw.uid, game_token: raw.token };
|
||||
// const header = {
|
||||
// ...getRequestHeader(ck, "POST", JSON.stringify(data), "X6"),
|
||||
// "x-rpc-client_type": "4",
|
||||
// "x-rpc-app_id": "bll8iq97cem8",
|
||||
// "x-rpc-game_biz": "bbs_cn",
|
||||
// };
|
||||
): Promise<TGApp.BBS.Response.Base | TGApp.Game.Login.StRes> {
|
||||
const data = { account_id: Number(raw.uid), game_token: raw.token };
|
||||
const header = {
|
||||
"x-rpc-app_version": TGBbs.version,
|
||||
"x-rpc-aigis": "",
|
||||
"Content-Type": "application/json",
|
||||
...getRequestHeader({}, "POST", JSON.stringify(data), "X6"),
|
||||
"x-rpc-client_type": "4",
|
||||
"x-rpc-app_id": "bll8iq97cem8",
|
||||
"x-rpc-game_biz": "bbs_cn",
|
||||
"x-rpc-sys_version": "12",
|
||||
"x-rpc-device_id": getDeviceInfo("device_id"),
|
||||
"x-rpc-device_name": getDeviceInfo("device_name"),
|
||||
"x-rpc-device_model": getDeviceInfo("product"),
|
||||
"x-rpc-app_id": "bll8iq97cem8",
|
||||
"x-rpc-client_type": "4",
|
||||
"User-Agent": "okhttp/4.9.3",
|
||||
ds: getDS("POST", JSON.stringify(data), "X6", false),
|
||||
cookie: `account_id=${raw.uid};ltuid=${raw.uid};stuid=${raw.uid};game_token=${raw.token};`,
|
||||
};
|
||||
const resp = await TGHttp<TGApp.BBS.Response.Base>(
|
||||
const resp = await TGHttp<TGApp.Game.Login.StResp>(
|
||||
`${taBu}account/ma-cn-session/app/getTokenByGameToken`,
|
||||
{
|
||||
method: "POST",
|
||||
@@ -51,8 +35,7 @@ async function getSTokenByGameToken(
|
||||
},
|
||||
);
|
||||
if (resp.retcode !== 0) return <TGApp.BBS.Response.Base>resp;
|
||||
console.log(resp);
|
||||
return resp;
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -57,7 +57,7 @@ function getRandomNumber(min: number, max: number): number {
|
||||
* @param {boolean} isSign 是否为签名
|
||||
* @returns {string} ds
|
||||
*/
|
||||
export function getDS(
|
||||
function getDS(
|
||||
method: string,
|
||||
data: string,
|
||||
saltType: keyof typeof SaltType,
|
||||
|
||||
Reference in New Issue
Block a user