diff --git a/package.json b/package.json index 5905f04f..e45c4835 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,9 @@ "js-md5": "^0.7.3", "pinia": "^2.1.6", "pinia-plugin-persistedstate": "^3.2.0", + "qrcode.vue": "^3.4.1", "tauri-plugin-sql-api": "github:tauri-apps/tauri-plugin-sql#v1", + "uuid": "^9.0.0", "vue": "^3.3.4", "vue-echarts": "^6.6.1", "vue-json-viewer": "^3.0.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ed523b05..dba3ad3b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,9 +29,15 @@ dependencies: pinia-plugin-persistedstate: specifier: ^3.2.0 version: 3.2.0(pinia@2.1.6) + qrcode.vue: + specifier: ^3.4.1 + version: 3.4.1(vue@3.3.4) tauri-plugin-sql-api: specifier: github:tauri-apps/tauri-plugin-sql#v1 version: git@github.com+tauri-apps/tauri-plugin-sql/b8fd19dac907cc8c3d78681cd4803a326b8b861e + uuid: + specifier: ^9.0.0 + version: 9.0.0 vue: specifier: ^3.3.4 version: 3.3.4 @@ -3756,6 +3762,14 @@ packages: engines: {node: '>=6'} dev: true + /qrcode.vue@3.4.1(vue@3.3.4): + resolution: {integrity: sha512-wq/zHsifH4FJ1GXQi8/wNxD1KfQkckIpjK1KPTc/qwYU5/Bkd4me0w4xZSg6EXk6xLBkVDE0zxVagewv5EMAVA==} + peerDependencies: + vue: ^3.0.0 + dependencies: + vue: 3.3.4 + dev: false + /qs@6.11.0: resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} engines: {node: '>=0.6'} @@ -4684,6 +4698,11 @@ packages: base64-arraybuffer: 1.0.2 dev: false + /uuid@9.0.0: + resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} + hasBin: true + dev: false + /validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 85075cf4..6db1ef04 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -29,6 +29,7 @@ "https://bbs.mihoyo.com/*", "https://hk4e-api.mihoyo.com/*", "https://passport-api.mihoyo.com/*", + "https://passport-api.miyoushe.com/*", "https://passport-api-v4.mihoyo.com/*", "https://act-webstatic.mihoyo.com/*", "https://sdk-webstatic.mihoyo.com/*", diff --git a/src/pages/common/Test.vue b/src/pages/common/Test.vue index 0da2b42c..41d15fdb 100644 --- a/src/pages/common/Test.vue +++ b/src/pages/common/Test.vue @@ -1,6 +1,57 @@ - + + diff --git a/src/plugins/Mys/types/WebLogin.d.ts b/src/plugins/Mys/types/WebLogin.d.ts new file mode 100644 index 00000000..d87e7738 --- /dev/null +++ b/src/plugins/Mys/types/WebLogin.d.ts @@ -0,0 +1,134 @@ +/** + * @file plugins Mys types WebLogin.d.ts + * @description Mys 插件 Web 登录类型定义文件 + * @author BTMuli + * @since Beta v0.3.0 + */ + +/** + * @description Mys 插件 Web 登录类型 + * @since Beta v0.3.0 + * @namespace WebLogin + * @return WebLogin + */ +declare namespace TGApp.Plugins.Mys.WebLogin { + /** + * @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} ticket 二维码票据 + * @property {string} url 二维码链接 + * @return GetLoginQrData + */ + export interface GetLoginQrData { + ticket: string; + 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} app_id 应用 ID + * @property {number} client_type 客户端类型 + * @property {string} created_at 创建时间 + * @property {boolean} need_realperson 是否需要人机验证 + * @property {RealNameInfo} realname_info 实名信息 + * @property {string} scanned_at 扫码时间 + * @property {string} status 状态 + * @property {unknown[]} tokens 令牌 + * @property {UserInfo} user_info 用户信息 + * @return GetLoginStatusData + */ + export interface GetLoginStatusData { + app_id: string; + client_type: number; + created_at: string; + need_realperson: boolean; + realname_info: RealNameInfo; + scanned_at: string; + status: string; + tokens: unknown[]; + user_info: UserInfo; + } + + /** + * @description 实名信息 + * @since Beta v0.3.0 + * @interface RealNameInfo + * @property {string} action_ticket 动作票据 + * @property {string} action_type 动作类型 + * @property {boolean} required 是否必须 + * @return RealNameInfo + */ + interface RealNameInfo { + action_ticket: string; + action_type: string; + required: boolean; + } + + /** + * @description 用户信息 + * @since Beta v0.3.0 + * @interface UserInfo + * @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 是否邮箱验证 // 1 为已验证 + * @property {unknown[]} links 链接 + * @property {string} mid 用户 ID + * @property {string} mobile 手机号 // 为加密后的手机号包含 * 号 + * @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 安全手机号 + * @return UserInfo + */ + interface UserInfo { + account_name: string; + aid: string; + area_code: string; + country: string; + email: string; + identity_code: string; + is_email_verify: number; + links: unknown[]; + mid: string; + mobile: string; + realname: string; + rebind_area_code: string; + rebind_mobile: string; + rebind_mobile_time: string; + safe_area_code: string; + safe_mobile: string; + } +} diff --git a/src/plugins/Mys/utils/doWebLogin.ts b/src/plugins/Mys/utils/doWebLogin.ts new file mode 100644 index 00000000..30aba6d1 --- /dev/null +++ b/src/plugins/Mys/utils/doWebLogin.ts @@ -0,0 +1,90 @@ +/** + * @file plugins Mys utils getLoginQr + * @description 获取登录二维码 + * @author BTMuli + * @since Beta v0.3.0 + */ + +// tauri +import { http } from "@tauri-apps/api"; + +const uuid = crypto.randomUUID(); + +const headers: Record = { + Accept: "application/json, text/plain, */*", + "Accept-Encoding": "gzip, deflate, br", + "Accept-Language": "zh-CN,zh;q=0.9", + Connection: "keep-alive", + Origin: "https://user.miyoushe.com", + Referer: "https://user.miyoushe.com/", + "User-Agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36", + "x-rpc-app_id": "bll8iq97cem8", + "x-rpc-client_type": "4", + "x-rpc-device_model": "Chrome 116.0.0.0", + "x-rpc-device_name": "Chrome", + "x-rpc-device_os": "Windows 10 64-bit", + "x-rpc-game_biz": "bbs_cn", + "x-rpc-mi_referrer": + "https://user.miyoushe.com/login-platform/index.html?app_id=bll8iq97cem8&app_version=2.58.0&theme=&token_type=4&game_biz=bbs_cn&message_origin=https%253A%252F%252Fwww.miyoushe.com&succ_back_type=message%253Alogin-platform%253Alogin-success&fail_back_type=message%253Alogin-platform%253Alogin-fail&sync_login_status=popup&ux_mode=popup&iframe_level=1&ap_mps=1#/login/qr", + "x-rpc-sdk_version": "2.17.0", + "X-Rpc-Device_fp": "", + "X-Rpc-Device_id": uuid, +}; + +/** + * @description 获取登录二维码 + * @since Beta v0.3.0 + * @returns {Promise} + */ +export async function getLoginQr(): Promise< + TGApp.Plugins.Mys.WebLogin.GetLoginQrData | TGApp.Plugins.Mys.Base.Response +> { + const url = "https://passport-api.miyoushe.com/account/ma-cn-passport/web/createQRLogin"; + return await http + .fetch(url, { + method: "POST", + headers, + body: http.Body.json({}), + }) + .then((res) => { + if (res.data.retcode === 0) { + return res.data.data; + } else { + return res.data; + } + }); +} + +/** + * @description 获取登录状态 + * @since Beta v0.3.0 + * @param {string} ticket 二维码票据 + * @returns {Promise} + */ +export async function getLoginStatus( + ticket: string, +): Promise> { + const url = "https://passport-api.miyoushe.com/account/ma-cn-passport/web/queryQRLoginStatus"; + const resp = await http.fetch(url, { + method: "POST", + headers, + body: http.Body.json({ ticket }), + }); + const data = resp.data; + if (data.retcode !== 0) return resp.data; + if (data.data.status === "Created" || data.data.status === "Scanned") { + await new Promise((resolve) => setTimeout(resolve, 5000)); + return await getLoginStatus(ticket); + } + if (data.data.status === "Confirmed") { + const cookieList = resp.rawHeaders["set-cookie"].map((cookie) => cookie.split(";")[0]); + const cookieRecord: Record = {}; + cookieList.forEach((cookie) => { + const [key, value] = cookie.split("="); + cookieRecord[key] = value; + }); + return cookieRecord; + } + return resp.data; +}