完成扫码获取 gameToken

This commit is contained in:
BTMuli
2023-09-03 16:07:30 +08:00
parent 3cac2057f8
commit 3a7ca20b14
5 changed files with 323 additions and 7 deletions

View File

@@ -28,6 +28,7 @@
"https://bbs-api.miyoushe.com/*", "https://bbs-api.miyoushe.com/*",
"https://bbs.mihoyo.com/*", "https://bbs.mihoyo.com/*",
"https://hk4e-api.mihoyo.com/*", "https://hk4e-api.mihoyo.com/*",
"https://hk4e-sdk.mihoyo.com/*",
"https://passport-api.mihoyo.com/*", "https://passport-api.mihoyo.com/*",
"https://passport-api.miyoushe.com/*", "https://passport-api.miyoushe.com/*",
"https://passport-api-v4.mihoyo.com/*", "https://passport-api-v4.mihoyo.com/*",

View File

@@ -0,0 +1,166 @@
<template>
<TOverlay v-model="visible" hide blur-val="20px">
<div class="tog-box">
<div class="tog-top">
<div class="tog-title">请使用米游社APP进行扫码操作</div>
<div class="tog-subtitle">所需米游社版本 >= 2.57.1</div>
</div>
<div class="tog-mid">
<qrcode-vue class="tog-qr" :value="qrCode" render-as="svg" />
</div>
<div class="tog-bottom">
<v-btn class="tog-btn" @click="onCancel">取消</v-btn>
<v-btn class="tog-btn" @click="freshQr">刷新</v-btn>
<v-btn class="tog-btn" @click="getData">已扫码</v-btn>
</div>
</div>
</TOverlay>
</template>
<script setup lang="ts">
// vue
import { computed, onMounted, ref } from "vue";
import showSnackbar from "../func/snackbar";
import TOverlay from "../main/t-overlay.vue";
import QrcodeVue from "qrcode.vue";
// store
import { useUserStore } from "../../store/modules/user";
// utils
import { getLoginQr, getLoginStatus } from "../../plugins/Mys/utils/doGameLogin";
interface ToWebLoginProps {
modelValue: boolean;
}
type ToWebLoginEmits = (e: "update:modelValue", value: boolean) => void;
const props = withDefaults(defineProps<ToWebLoginProps>(), {
modelValue: false,
});
const emits = defineEmits<ToWebLoginEmits>();
const visible = computed({
get: () => props.modelValue,
set: (value) => {
emits("update:modelValue", value);
},
});
const qrCode = ref<string>("");
const ticket = ref<string>("");
const userStore = useUserStore();
onMounted(async () => {
await freshQr();
});
async function freshQr(): Promise<void> {
const res = await getLoginQr();
if ("retcode" in res) {
showSnackbar({
text: `[${res.retcode}] ${res.message}`,
color: "error",
});
return;
}
qrCode.value = res.url;
const ticketReg = /ticket=(\w+)/;
const ticketRes = ticketReg.exec(res.url);
if (ticketRes) {
ticket.value = ticketRes[1];
} else {
showSnackbar({
text: "获取ticket失败",
color: "error",
});
}
}
async function getData(): Promise<void> {
const res = await getLoginStatus(ticket.value);
if ("retcode" in res) {
showSnackbar({
text: `[${res.retcode}] ${res.message}`,
color: "error",
});
} else if (res.stat === "Init") {
showSnackbar({
text: "请先扫码",
color: "error",
});
} else if (res.stat === "Scanned") {
showSnackbar({
text: "请在米游社APP上确认登录",
color: "error",
});
} else {
const data: TGApp.Plugins.Mys.GameLogin.StatusPayloadRaw = JSON.parse(res.payload.raw);
await userStore.saveCookie("account_id", data.uid);
await userStore.saveCookie("game_token", data.token);
showSnackbar({
text: "登录成功",
color: "success",
});
visible.value = false;
}
}
function onCancel(): void {
visible.value = false;
}
</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-subtitle {
font-size: 14px;
opacity: 0.6;
}
.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;
}
.tog-bottom {
display: flex;
align-items: center;
justify-content: space-between;
}
.tog-btn {
background: var(--tgc-btn-1);
color: var(--btn-text);
}
</style>

90
src/plugins/Mys/types/GameLogin.d.ts vendored Normal file
View File

@@ -0,0 +1,90 @@
/**
* @file plugins Mys types GameLogin.d.ts
* @description Mys 插件 Game 登录类型定义文件
* @author BTMuli <bt-muli@outlook.com>
* @since Beta v0.3.0
*/
/**
* @description Mys 插件 Game 登录类型
* @since Beta v0.3.0
* @namespace GameLogin
* @return GameLogin
*/
declare namespace TGApp.Plugins.Mys.GameLogin {
/**
* @description 获取登录二维码返回数据
* @since Beta v0.3.0
* @interface GetLoginQrResponse
* @extends TGApp.Plugins.Mys.Base.Response
* @property {GetLoginQrData} data 数据
* @return GetLoginQrResponse
*/
export interface GetLoginQrResponse extends TGApp.Plugins.Mys.Base.Response {
data: GetLoginQrData;
}
/**
* @description 获取登录二维码返回数据
* @since Beta v0.3.0
* @interface GetLoginQrData
* @property {string} url 二维码链接
* @return GetLoginQrData
*/
export interface GetLoginQrData {
url: string;
}
/**
* @description 获取登录状态返回数据
* @since Beta v0.3.0
* @interface GetLoginStatusResponse
* @extends TGApp.Plugins.Mys.Base.Response
* @property {GetLoginStatusData} data 数据
* @return GetLoginStatusResponse
*/
export interface GetLoginStatusResponse extends TGApp.Plugins.Mys.Base.Response {
data: GetLoginStatusData;
}
/**
* @description 获取登录状态返回数据
* @since Beta v0.3.0
* @interface GetLoginStatusData
* @property {string} stat 状态 // Init: 未扫码Scanned: 已扫码Confirmed: 已确认
* @property {StatusPayload} payload 状态数据
* @return GetLoginStatusData
*/
export 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
*/
export interface StatusPayload {
ext: string;
proto: string;
raw: string;
}
/**
* @description 反序列化后的登录状态数据
* @since Beta v0.3.0
* @interface StatusPayloadRaw
* @property {string} uid 用户 UID
* @property {string} token 用户 token
* @return StatusPayloadRaw
*/
export interface StatusPayloadRaw {
uid: string;
token: string;
}
}

View File

@@ -0,0 +1,58 @@
/**
* @file plugins Mys utils doGameLogin
* @description 获取 gameToken曲线获取 stoken
* @author BTMuli <bt-muli@outlook.com>
* @since Beta v0.3.0
*/
// tauri
import { http } from "@tauri-apps/api";
const device = crypto.randomUUID();
/**
* @description 获取登录二维码
* @since Beta v0.3.0
* @returns {Promise<TGApp.Plugins.Mys.GameLogin.GetLoginQrData|TGApp.Plugins.Mys.Base.Response>}
*/
export async function getLoginQr(): Promise<
TGApp.Plugins.Mys.GameLogin.GetLoginQrData | TGApp.Plugins.Mys.Base.Response
> {
const url = "https://hk4e-sdk.mihoyo.com/hk4e_cn/combo/panda/qrcode/fetch";
const data = {
app_id: "4",
device,
};
return await http
.fetch<TGApp.Plugins.Mys.GameLogin.GetLoginQrResponse>(url, {
method: "POST",
body: http.Body.json(data),
})
.then((res) => {
if (res.data.retcode === 0) return res.data.data;
return res.data;
});
}
/**
* @description 获取登录状态
* @since Beta v0.3.0
* @param {string} ticket 二维码 ticket
* @returns {Promise<TGApp.Plugins.Mys.GameLogin.GetLoginStatusData | TGApp.Plugins.Mys.Base.Response>}
*/
export async function getLoginStatus(
ticket: string,
): Promise<TGApp.Plugins.Mys.GameLogin.GetLoginStatusData | TGApp.Plugins.Mys.Base.Response> {
const url = "https://hk4e-sdk.mihoyo.com/hk4e_cn/combo/panda/qrcode/query";
const data = { app_id: "4", device, ticket };
console.log(data);
return await http
.fetch<TGApp.Plugins.Mys.GameLogin.GetLoginStatusResponse>(url, {
method: "POST",
body: http.Body.json(data),
})
.then((res) => {
if (res.data.retcode === 0) return res.data.data;
return res.data;
});
}

View File

@@ -1,14 +1,16 @@
/** /**
* @file store modules user.ts * @file store modules user.ts
* @description User store module * @description User store module
* @author BTMuli<bt-muli@outlook.com> * @author BTMuli <bt-muli@outlook.com>
* @since Alpha v0.2.0 * @since Beta v0.3.0
*/ */
// vue // vue
import { ref } from "vue"; import { ref } from "vue";
// pinia // pinia
import { defineStore } from "pinia"; import { defineStore } from "pinia";
// sqlite
import TGSqlite from "../../plugins/Sqlite";
export const useUserStore = defineStore( export const useUserStore = defineStore(
"user", "user",
@@ -81,10 +83,9 @@ export const useUserStore = defineStore(
}; };
} }
function initCookie(ck: Record<string, string>): void { async function saveCookie(type: string, val: string): Promise<void> {
if (cookie.value !== ck) { cookie.value[type] = val;
cookie.value = ck; await TGSqlite.saveAppData("cookie", JSON.stringify(cookie.value));
}
} }
return { return {
@@ -100,7 +101,7 @@ export const useUserStore = defineStore(
getCookieGroup2, getCookieGroup2,
getCookieGroup3, getCookieGroup3,
getCookieGroup4, getCookieGroup4,
initCookie, saveCookie,
}; };
}, },
{ {