comboToken

This commit is contained in:
目棃
2025-03-12 17:47:04 +08:00
parent f842975f21
commit b504f043b4
7 changed files with 159 additions and 57 deletions

View File

@@ -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>

View File

@@ -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%;

View File

@@ -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>

View File

@@ -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;
};
}

View File

@@ -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`, {

View File

@@ -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;
}
/**

View File

@@ -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,