From 712a09131ef069850da1a85c0ee5dece04dfae7f Mon Sep 17 00:00:00 2001 From: BTMuli Date: Thu, 16 Nov 2023 14:19:18 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E5=AE=9E=E8=A3=85=20getDeviceFp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close #58 --- src/pages/common/Config.vue | 26 +++++++++- src/store/modules/app.ts | 19 ++++++- src/types/App/Device.d.ts | 32 ++++++++++++ src/types/BBS/Response.d.ts | 18 +++++++ src/utils/TGClient.ts | 12 +++-- src/utils/toolFunc.ts | 81 +++++++++++++++++++++++++----- src/web/constant/TGConstant.ts | 9 ++-- src/web/constant/bbs.ts | 7 +-- src/web/request/TGRequest.ts | 8 ++- src/web/request/getDeviceFp.ts | 82 +++++++++++++++++++++++++++++++ src/web/utils/getRequestHeader.ts | 26 +++------- 11 files changed, 274 insertions(+), 46 deletions(-) create mode 100644 src/types/App/Device.d.ts create mode 100644 src/web/request/getDeviceFp.ts diff --git a/src/pages/common/Config.vue b/src/pages/common/Config.vue index 11c14836..d6d5f934 100644 --- a/src/pages/common/Config.vue +++ b/src/pages/common/Config.vue @@ -123,6 +123,7 @@ /> + { window.location.reload(); } +// 更新设备信息 +async function confirmUpdateDevice(): Promise { + const localFp = getDeviceInfo("device_fp"); + if (localFp !== "0000000000000") { + const res = await showConfirm({ + title: "确认更新设备信息吗?", + text: `DeviceFp:${localFp}`, + }); + if (res === false) { + showSnackbar({ + text: "已取消更新设备信息", + color: "cancel", + }); + return; + } + } + await TGRequest.Device.getFp(); + appStore.deviceInfo.device_fp = getDeviceInfo("device_fp"); + showSnackbar({ + text: "设备信息已更新! DeviceFp: " + getDeviceInfo("device_fp"), + }); +} + // 清除用户缓存 async function confirmDelCache(): Promise { const CacheDir = await getCacheDir(); diff --git a/src/store/modules/app.ts b/src/store/modules/app.ts index 62ccff1c..521f2e47 100644 --- a/src/store/modules/app.ts +++ b/src/store/modules/app.ts @@ -1,13 +1,15 @@ /** - * @file store modules app.ts + * @file store/modules/app.ts * @description App store module - * @since Beta v0.3.3 + * @since Beta v0.3.6 */ import { path } from "@tauri-apps/api"; import { defineStore } from "pinia"; import { reactive, ref } from "vue"; +import { getInitDeviceInfo } from "../../utils/toolFunc"; + // 用于存储用户数据的路径 const userDataDir = `${await path.appLocalDataDir()}userData`; // 用于存放数据库的路径 @@ -43,6 +45,8 @@ export const useAppStore = defineStore( const userPath = ref({ UIAF: `${dataPath.userDataDir}/UIAF.json`, }); + // 设备信息 + const deviceInfo = ref(getInitDeviceInfo()); // 初始化 function init(): void { @@ -52,6 +56,7 @@ export const useAppStore = defineStore( wiki: false, }; theme.value = "default"; + initDevice(); } function getSubmenu(): string[] { @@ -65,6 +70,10 @@ export const useAppStore = defineStore( else theme.value = "default"; } + function initDevice(): void { + deviceInfo.value = getInitDeviceInfo(); + } + return { theme, loading, @@ -73,6 +82,7 @@ export const useAppStore = defineStore( devMode, dataPath, userPath, + deviceInfo, init, getSubmenu, changeTheme, @@ -100,6 +110,11 @@ export const useAppStore = defineStore( storage: window.localStorage, paths: ["theme"], }, + { + key: "deviceInfo", + storage: window.localStorage, + paths: ["deviceInfo"], + }, ], }, ); diff --git a/src/types/App/Device.d.ts b/src/types/App/Device.d.ts new file mode 100644 index 00000000..b92da952 --- /dev/null +++ b/src/types/App/Device.d.ts @@ -0,0 +1,32 @@ +/** + * @file types/App/Device.d.ts + * @description App 设备信息类型定义文件 + * @since Beta v0.3.6 + */ + +/** + * @description App 设备信息类型 namespace + * @since Beta v0.3.6 + * @namespace TGApp.App.Device + * @memberof TGApp.App + */ +declare namespace TGApp.App.Device { + /** + * @description 设备信息 + * @since Beta v0.3.6 + * @interface DeviceInfo + * @property {string} device_id - 设备 ID + * @property {string} model - 设备型号 + * @property {string} seed_id - 种子 ID + * @property {string} seed_time - 种子时间 + * @property {string} device_fp - 设备指纹 + * @return DeviceInfo + */ + interface DeviceInfo { + device_id: string; + model: string; + seed_id: string; + seed_time: string; + device_fp: string; + } +} diff --git a/src/types/BBS/Response.d.ts b/src/types/BBS/Response.d.ts index ea588c2a..f5db03a4 100644 --- a/src/types/BBS/Response.d.ts +++ b/src/types/BBS/Response.d.ts @@ -182,4 +182,22 @@ declare namespace TGApp.BBS.Response { account_info: TGApp.BBS.Account.getActionTicketBySTokenInfo; }; } + + /** + * @description 获取 deviceFp 的返回类型 + * @interface getDeviceFp + * @since Beta v0.3.6 + * @extends BaseWithData + * @property {string} data.device_fp - deviceFp 值 + * @property {number} data.code - code 值 + * @property {string} data.msg - msg 值 + * @return getDeviceFp + */ + interface getDeviceFp extends BaseWithData { + data: { + device_fp: string; + code: number; + msg: string; + }; + } } diff --git a/src/utils/TGClient.ts b/src/utils/TGClient.ts index ef476de7..c036fd13 100644 --- a/src/utils/TGClient.ts +++ b/src/utils/TGClient.ts @@ -1,14 +1,14 @@ /** * @file utils/TGClient.ts * @desc 负责米游社客户端的 callback 处理 - * @since Beta v0.3.5 + * @since Beta v0.3.6 */ import { event, invoke, path } from "@tauri-apps/api"; import type { Event } from "@tauri-apps/api/event"; import { WebviewWindow } from "@tauri-apps/api/window"; -import { getDeviceID } from "./toolFunc"; +import { getDeviceInfo } from "./toolFunc"; import { useUserStore } from "../store/modules/user"; import TGConstant from "../web/constant/TGConstant"; import TGRequest from "../web/request/TGRequest"; @@ -321,16 +321,20 @@ class TGClient { /** * @func getHTTPRequestHeaders - * @since Beta v0.3.4 + * @since Beta v0.3.6 * @desc 获取米游社客户端的 HTTP 请求头 * @param {string} callback - 回调函数名 * @returns {void} - 无返回值 */ async getHTTPRequestHeaders(callback: string): Promise { + const localFp = getDeviceInfo("device_fp"); + if (localFp === "0000000000000") await TGRequest.Device.getFp(); const data = { + "user-agent": TGConstant.BBS.UA_MOBILE, "x-rpc-client_type": "5", - "x-rpc-device_id": getDeviceID(), + "x-rpc-device_id": getDeviceInfo("device_id"), "x-rpc-app_version": TGConstant.BBS.VERSION, + "x-rpc-device_fp": getDeviceInfo("device_fp"), }; await this.callback(callback, data); } diff --git a/src/utils/toolFunc.ts b/src/utils/toolFunc.ts index 225a0367..39727bd8 100644 --- a/src/utils/toolFunc.ts +++ b/src/utils/toolFunc.ts @@ -1,7 +1,7 @@ /** - * @file utils toolFunc.ts + * @file utils/toolFunc.ts * @description 一些工具函数 - * @since Beta v0.3.5 + * @since Beta v0.3.6 */ import { os, path } from "@tauri-apps/api"; @@ -40,17 +40,36 @@ export function timestampToDate(timestamp: number): string { } /** - * @description 获取 deviceID - * @since Beta v0.3.4 - * @returns {string} deviceID + * @description 获取设备信息(初始化时) + * @since Beta v0.3.6 + * @returns {TGApp.App.Device.DeviceInfo} 设备信息 */ -export function getDeviceID(): string { - let deviceID = localStorage.getItem("deviceID"); - if (deviceID === null) { - deviceID = v4(); - localStorage.setItem("deviceID", deviceID); +export function getInitDeviceInfo(): TGApp.App.Device.DeviceInfo { + return { + device_id: v4(), + model: getRandomString(6), + seed_id: v4(), + seed_time: Date.now().toString(), + device_fp: "0000000000000", + }; +} + +/** + * @description 获取设备信息(登录时) + * @since Beta v0.3.6 + * @param {string} key - 设备信息 key + * @returns {string} 设备信息 + */ +export function getDeviceInfo(key: "device_id" | "device_fp"): string { + const localDevice = localStorage.getItem("deviceInfo"); + let deviceInfo: TGApp.App.Device.DeviceInfo; + if (localDevice === null) { + deviceInfo = getInitDeviceInfo(); + localStorage.setItem("deviceInfo", JSON.stringify({ deviceInfo })); + } else { + deviceInfo = JSON.parse(localDevice).deviceInfo; } - return deviceID; + return deviceInfo[key]; } /** @@ -84,3 +103,43 @@ export async function getCacheDir(): Promise { } return false; } + +/** + * @description 获取随机字符串 + * @since Beta v0.3.6 + * @param {number} length 字符串长度 + * @param {string} type + * @returns {string} 随机字符串 + */ +export function getRandomString(length: number, type: string = "all"): string { + const char = "abcdefghijklmnopqrstuvwxyz"; + const num = "0123456789"; + let str = ""; + switch (type) { + case "all": + str = char + char.toUpperCase() + num; + break; + case "number": + str = num; + break; + case "lower": + str = char; + break; + case "upper": + str = char.toUpperCase(); + break; + case "letter": + str = char + char.toUpperCase(); + break; + case "hex": + str = num + "abcdef"; + break; + default: + str = char + char.toUpperCase() + num; + } + let res = ""; + for (let i = 0; i < length; i++) { + res += str.charAt(Math.floor(Math.random() * str.length)); + } + return res; +} diff --git a/src/web/constant/TGConstant.ts b/src/web/constant/TGConstant.ts index 082e4aa4..02122b95 100644 --- a/src/web/constant/TGConstant.ts +++ b/src/web/constant/TGConstant.ts @@ -1,17 +1,18 @@ /** - * @file web constant TGConstant.ts + * @file web/constant/TGConstant.ts * @description 常量 - * @since Beta v0.3.3 + * @since Beta v0.3.6 */ -import { BBS_VERSION, BBS_HEADER_AGENT, BBS_APP_ID, BBS_SALT } from "./bbs"; +import { BBS_APP_ID, BBS_SALT, BBS_UA_MOBILE, BBS_UA_PC, BBS_VERSION } from "./bbs"; import SERVER from "./server"; import { GAME_BIZ } from "./utils"; const TGConstant = { BBS: { VERSION: BBS_VERSION, - USER_AGENT: BBS_HEADER_AGENT, + UA_PC: BBS_UA_PC, + UA_MOBILE: BBS_UA_MOBILE, APP_ID: BBS_APP_ID, }, Salt: BBS_SALT, diff --git a/src/web/constant/bbs.ts b/src/web/constant/bbs.ts index 301f0a44..93c6baf9 100644 --- a/src/web/constant/bbs.ts +++ b/src/web/constant/bbs.ts @@ -1,11 +1,12 @@ /** - * @file web constant bbs.ts + * @file web/constant/bbs.ts * @description 常量-应用数据 - * @since Beta v0.3.3 + * @since Beta v0.3.6 */ export const BBS_VERSION = "2.59.1"; -export const BBS_HEADER_AGENT = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) miHoYoBBS/${BBS_VERSION}`; +export const BBS_UA_PC = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) miHoYoBBS/${BBS_VERSION}`; +export const BBS_UA_MOBILE = `Mozilla/5.0 (Linux; Android 12) Mobile miHoYoBBS/${BBS_VERSION}`; export const BBS_APP_ID = "bll8iq97cem8"; /** diff --git a/src/web/request/TGRequest.ts b/src/web/request/TGRequest.ts index 01313f39..3739d9a7 100644 --- a/src/web/request/TGRequest.ts +++ b/src/web/request/TGRequest.ts @@ -1,7 +1,7 @@ /** - * @file web request TGRequest.ts + * @file web/request/TGRequest.ts * @description 应用用到的请求函数 - * @since Beta v0.3.4 + * @since Beta v0.3.6 */ import { genAuthkey } from "./genAuthkey"; @@ -9,6 +9,7 @@ import { getAbyss } from "./getAbyss"; import { getActionTicketBySToken } from "./getActionTicket"; import { getAnnoContent, getAnnoList } from "./getAnno"; import { getCookieTokenByGameToken, getCookieTokenBySToken } from "./getCookieToken"; +import { getDeviceFp } from "./getDeviceFp"; // import * from "./getEnkaData.ts"; import { getGachaLog } from "./getGachaLog"; import { getGameAccountsByCookie, getGameAccountsBySToken } from "./getGameAccounts"; @@ -27,6 +28,9 @@ const TGRequest = { getList: getAnnoList, getContent: getAnnoContent, }, + Device: { + getFp: getDeviceFp, + }, User: { getAuthkey: genAuthkey, getGachaLog, diff --git a/src/web/request/getDeviceFp.ts b/src/web/request/getDeviceFp.ts new file mode 100644 index 00000000..91d23fc8 --- /dev/null +++ b/src/web/request/getDeviceFp.ts @@ -0,0 +1,82 @@ +/** + * @file src/web/request/getDeviceFp.ts + * @description 获取设备指纹 + * @since Beta v0.3.6 + */ + +import { http } from "@tauri-apps/api"; + +import { getInitDeviceInfo } from "../../utils/toolFunc"; +import TGConstant from "../constant/TGConstant"; + +/** + * @description 获取设备指纹 + * @since Beta v0.3.6 + * @returns {Promise} 设备指纹 + */ +export async function getDeviceFp(): Promise { + const info = getInitDeviceInfo(); + const deviceFPHeader = { + cpuType: "arm64-v8a", + romCapacity: "512", + productName: info.model, + romRemain: "256", + manufacturer: "Xiaomi", + appMemory: "512", + hostname: "dg02-pool03-kvm87", + screenSize: "1080x1920", + osVersion: "13", + aaid: "", + vendor: "中国移动", + accelerometer: "true", + buildTags: "release-keys", + model: info.model, + brand: "Xiaomi", + oaid: "", + hardware: "qcom", + deviceType: "OP5913L1", + devId: "unknown", + serialNumber: "unknown", + buildTime: "1588876800000", // 2020-05-08 + buildUser: "root", + ramCapacity: "2048", + magnetometer: "true", + display: `OP5913L1-user ${info.model} 10 QKQ1.190825.002 V12.0.1.0.QFJCNXM release-keys`, + ramRemain: "1024", + deviceInfo: "unknown", + gyroscope: "true", + vaid: "", + buildType: "user", + sdkVersion: "29", + board: "sdm660", + }; + const url = "https://public-data-api.mihoyo.com/device-fp/api/getFp"; + const data = { + device_id: info.device_id, + seed_id: info.seed_id, + platform: "2", + seed_time: info.seed_time, + ext_fields: JSON.stringify(deviceFPHeader), + app_name: "bbs_cn", + bbs_device_id: info.device_id, + device_fp: info.device_fp, + }; + const header = { + "User-Agent": `Mozilla/5.0 (Linux; Android 12) Mobile miHoYoBBS/${TGConstant.BBS.VERSION}`, + "x-rpc-app_version": TGConstant.BBS.VERSION, + "x-rpc-client_type": "5", + "x-requested-with": "com.mihoyo.hyperion", + Referer: "https://webstatic.mihoyo.com/", + }; + info.device_fp = await http + .fetch(url, { + method: "POST", + body: http.Body.json(data), + headers: header, + }) + .then((res) => { + if (res.data.data.code === 200) return res.data.data.device_fp; + return "0000000000000"; + }); + localStorage.setItem("deviceInfo", JSON.stringify({ deviceInfo: info })); +} diff --git a/src/web/utils/getRequestHeader.ts b/src/web/utils/getRequestHeader.ts index 87b6cb37..7aec6273 100644 --- a/src/web/utils/getRequestHeader.ts +++ b/src/web/utils/getRequestHeader.ts @@ -1,12 +1,13 @@ /** - * @file web utils getRequestHeader.ts + * @file web/utils/getRequestHeader.ts * @description 获取请求头 - * @since Beta v0.3.4 + * @since Beta v0.3.6 */ import Md5 from "js-md5"; import { transCookie, transParams } from "./tools"; +import { getDeviceInfo, getRandomString } from "../../utils/toolFunc"; import TGConstant from "../constant/TGConstant"; /** @@ -40,21 +41,6 @@ function getRandomNumber(min: number, max: number): number { return Math.floor(Math.random() * (max - min + 1) + min); } -/** - * @description 获取随机字符串 - * @since Alpha v0.2.0 - * @param {number} length 字符串长度 - * @returns {string} 随机字符串 - */ -function getRandomString(length: number): string { - const str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - let res = ""; - for (let i = 0; i < length; i++) { - res += str.charAt(Math.floor(Math.random() * str.length)); - } - return res; -} - /** * @description 获取 ds * @since Beta v0.3.3 @@ -80,7 +66,7 @@ function getDS(method: string, data: string, saltType: string, isSign: boolean): /** * @description 获取请求头 - * @since Beta v0.3.0 + * @since Beta v0.3.6 * @param {Record} cookie cookie * @param {string} method 请求方法 * @param {Record|string} data 请求数据 @@ -102,11 +88,13 @@ export function getRequestHeader( ds = getDS(method, transParams(data), saltType, isSign); } return { - "user-agent": TGConstant.BBS.USER_AGENT, + "user-agent": TGConstant.BBS.UA_PC, "x-rpc-app_version": TGConstant.BBS.VERSION, "x-rpc-client_type": "5", "x-requested-with": "com.mihoyo.hyperion", referer: "https://webstatic.mihoyo.com", + "x-rpc-device_id": getDeviceInfo("device_id"), + "x-rpc-device_fp": getDeviceInfo("device_fp"), ds, cookie: transCookie(cookie), };