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),
};