实装 getDeviceFp

close #58
This commit is contained in:
BTMuli
2023-11-16 14:19:18 +08:00
parent 13e9440c6f
commit 712a09131e
11 changed files with 274 additions and 46 deletions

View File

@@ -123,6 +123,7 @@
/> />
</template> </template>
</v-list-item> </v-list-item>
<v-list-item prepend-icon="mdi-refresh" title="刷新设备信息" @click="confirmUpdateDevice" />
<v-list-item prepend-icon="mdi-database-remove" title="清除缓存" @click="confirmDelCache" /> <v-list-item prepend-icon="mdi-database-remove" title="清除缓存" @click="confirmDelCache" />
<v-list-item <v-list-item
v-show="showReset" v-show="showReset"
@@ -161,7 +162,7 @@ import { useAppStore } from "../../store/modules/app";
import { useHomeStore } from "../../store/modules/home"; import { useHomeStore } from "../../store/modules/home";
import { useUserStore } from "../../store/modules/user"; import { useUserStore } from "../../store/modules/user";
import { getBuildTime } from "../../utils/TGBuild"; import { getBuildTime } from "../../utils/TGBuild";
import { bytesToSize, getCacheDir } from "../../utils/toolFunc"; import { bytesToSize, getCacheDir, getDeviceInfo } from "../../utils/toolFunc";
import { backupUiafData, restoreUiafData } from "../../utils/UIAF"; import { backupUiafData, restoreUiafData } from "../../utils/UIAF";
import TGRequest from "../../web/request/TGRequest"; import TGRequest from "../../web/request/TGRequest";
import { backupAbyssData, backupCookieData } from "../../web/utils/backupData"; import { backupAbyssData, backupCookieData } from "../../web/utils/backupData";
@@ -443,6 +444,29 @@ async function confirmUpdate(title?: string): Promise<void> {
window.location.reload(); window.location.reload();
} }
// 更新设备信息
async function confirmUpdateDevice(): Promise<void> {
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<void> { async function confirmDelCache(): Promise<void> {
const CacheDir = await getCacheDir(); const CacheDir = await getCacheDir();

View File

@@ -1,13 +1,15 @@
/** /**
* @file store modules app.ts * @file store/modules/app.ts
* @description App store module * @description App store module
* @since Beta v0.3.3 * @since Beta v0.3.6
*/ */
import { path } from "@tauri-apps/api"; import { path } from "@tauri-apps/api";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { reactive, ref } from "vue"; import { reactive, ref } from "vue";
import { getInitDeviceInfo } from "../../utils/toolFunc";
// 用于存储用户数据的路径 // 用于存储用户数据的路径
const userDataDir = `${await path.appLocalDataDir()}userData`; const userDataDir = `${await path.appLocalDataDir()}userData`;
// 用于存放数据库的路径 // 用于存放数据库的路径
@@ -43,6 +45,8 @@ export const useAppStore = defineStore(
const userPath = ref({ const userPath = ref({
UIAF: `${dataPath.userDataDir}/UIAF.json`, UIAF: `${dataPath.userDataDir}/UIAF.json`,
}); });
// 设备信息
const deviceInfo = ref<TGApp.App.Device.DeviceInfo>(getInitDeviceInfo());
// 初始化 // 初始化
function init(): void { function init(): void {
@@ -52,6 +56,7 @@ export const useAppStore = defineStore(
wiki: false, wiki: false,
}; };
theme.value = "default"; theme.value = "default";
initDevice();
} }
function getSubmenu(): string[] { function getSubmenu(): string[] {
@@ -65,6 +70,10 @@ export const useAppStore = defineStore(
else theme.value = "default"; else theme.value = "default";
} }
function initDevice(): void {
deviceInfo.value = getInitDeviceInfo();
}
return { return {
theme, theme,
loading, loading,
@@ -73,6 +82,7 @@ export const useAppStore = defineStore(
devMode, devMode,
dataPath, dataPath,
userPath, userPath,
deviceInfo,
init, init,
getSubmenu, getSubmenu,
changeTheme, changeTheme,
@@ -100,6 +110,11 @@ export const useAppStore = defineStore(
storage: window.localStorage, storage: window.localStorage,
paths: ["theme"], paths: ["theme"],
}, },
{
key: "deviceInfo",
storage: window.localStorage,
paths: ["deviceInfo"],
},
], ],
}, },
); );

32
src/types/App/Device.d.ts vendored Normal file
View File

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

View File

@@ -182,4 +182,22 @@ declare namespace TGApp.BBS.Response {
account_info: TGApp.BBS.Account.getActionTicketBySTokenInfo; 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;
};
}
} }

View File

@@ -1,14 +1,14 @@
/** /**
* @file utils/TGClient.ts * @file utils/TGClient.ts
* @desc 负责米游社客户端的 callback 处理 * @desc 负责米游社客户端的 callback 处理
* @since Beta v0.3.5 * @since Beta v0.3.6
*/ */
import { event, invoke, path } from "@tauri-apps/api"; import { event, invoke, path } from "@tauri-apps/api";
import type { Event } from "@tauri-apps/api/event"; import type { Event } from "@tauri-apps/api/event";
import { WebviewWindow } from "@tauri-apps/api/window"; import { WebviewWindow } from "@tauri-apps/api/window";
import { getDeviceID } from "./toolFunc"; import { getDeviceInfo } from "./toolFunc";
import { useUserStore } from "../store/modules/user"; import { useUserStore } from "../store/modules/user";
import TGConstant from "../web/constant/TGConstant"; import TGConstant from "../web/constant/TGConstant";
import TGRequest from "../web/request/TGRequest"; import TGRequest from "../web/request/TGRequest";
@@ -321,16 +321,20 @@ class TGClient {
/** /**
* @func getHTTPRequestHeaders * @func getHTTPRequestHeaders
* @since Beta v0.3.4 * @since Beta v0.3.6
* @desc 获取米游社客户端的 HTTP 请求头 * @desc 获取米游社客户端的 HTTP 请求头
* @param {string} callback - 回调函数名 * @param {string} callback - 回调函数名
* @returns {void} - 无返回值 * @returns {void} - 无返回值
*/ */
async getHTTPRequestHeaders(callback: string): Promise<void> { async getHTTPRequestHeaders(callback: string): Promise<void> {
const localFp = getDeviceInfo("device_fp");
if (localFp === "0000000000000") await TGRequest.Device.getFp();
const data = { const data = {
"user-agent": TGConstant.BBS.UA_MOBILE,
"x-rpc-client_type": "5", "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-app_version": TGConstant.BBS.VERSION,
"x-rpc-device_fp": getDeviceInfo("device_fp"),
}; };
await this.callback(callback, data); await this.callback(callback, data);
} }

View File

@@ -1,7 +1,7 @@
/** /**
* @file utils toolFunc.ts * @file utils/toolFunc.ts
* @description 一些工具函数 * @description 一些工具函数
* @since Beta v0.3.5 * @since Beta v0.3.6
*/ */
import { os, path } from "@tauri-apps/api"; import { os, path } from "@tauri-apps/api";
@@ -40,17 +40,36 @@ export function timestampToDate(timestamp: number): string {
} }
/** /**
* @description 获取 deviceID * @description 获取设备信息(初始化时)
* @since Beta v0.3.4 * @since Beta v0.3.6
* @returns {string} deviceID * @returns {TGApp.App.Device.DeviceInfo} 设备信息
*/ */
export function getDeviceID(): string { export function getInitDeviceInfo(): TGApp.App.Device.DeviceInfo {
let deviceID = localStorage.getItem("deviceID"); return {
if (deviceID === null) { device_id: v4(),
deviceID = v4(); model: getRandomString(6),
localStorage.setItem("deviceID", deviceID); 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<string[] | false> {
} }
return false; 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;
}

View File

@@ -1,17 +1,18 @@
/** /**
* @file web constant TGConstant.ts * @file web/constant/TGConstant.ts
* @description 常量 * @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 SERVER from "./server";
import { GAME_BIZ } from "./utils"; import { GAME_BIZ } from "./utils";
const TGConstant = { const TGConstant = {
BBS: { BBS: {
VERSION: BBS_VERSION, VERSION: BBS_VERSION,
USER_AGENT: BBS_HEADER_AGENT, UA_PC: BBS_UA_PC,
UA_MOBILE: BBS_UA_MOBILE,
APP_ID: BBS_APP_ID, APP_ID: BBS_APP_ID,
}, },
Salt: BBS_SALT, Salt: BBS_SALT,

View File

@@ -1,11 +1,12 @@
/** /**
* @file web constant bbs.ts * @file web/constant/bbs.ts
* @description 常量-应用数据 * @description 常量-应用数据
* @since Beta v0.3.3 * @since Beta v0.3.6
*/ */
export const BBS_VERSION = "2.59.1"; 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"; export const BBS_APP_ID = "bll8iq97cem8";
/** /**

View File

@@ -1,7 +1,7 @@
/** /**
* @file web request TGRequest.ts * @file web/request/TGRequest.ts
* @description 应用用到的请求函数 * @description 应用用到的请求函数
* @since Beta v0.3.4 * @since Beta v0.3.6
*/ */
import { genAuthkey } from "./genAuthkey"; import { genAuthkey } from "./genAuthkey";
@@ -9,6 +9,7 @@ import { getAbyss } from "./getAbyss";
import { getActionTicketBySToken } from "./getActionTicket"; import { getActionTicketBySToken } from "./getActionTicket";
import { getAnnoContent, getAnnoList } from "./getAnno"; import { getAnnoContent, getAnnoList } from "./getAnno";
import { getCookieTokenByGameToken, getCookieTokenBySToken } from "./getCookieToken"; import { getCookieTokenByGameToken, getCookieTokenBySToken } from "./getCookieToken";
import { getDeviceFp } from "./getDeviceFp";
// import * from "./getEnkaData.ts"; // import * from "./getEnkaData.ts";
import { getGachaLog } from "./getGachaLog"; import { getGachaLog } from "./getGachaLog";
import { getGameAccountsByCookie, getGameAccountsBySToken } from "./getGameAccounts"; import { getGameAccountsByCookie, getGameAccountsBySToken } from "./getGameAccounts";
@@ -27,6 +28,9 @@ const TGRequest = {
getList: getAnnoList, getList: getAnnoList,
getContent: getAnnoContent, getContent: getAnnoContent,
}, },
Device: {
getFp: getDeviceFp,
},
User: { User: {
getAuthkey: genAuthkey, getAuthkey: genAuthkey,
getGachaLog, getGachaLog,

View File

@@ -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<TGApp.BBS.Response.getDeviceFp>} 设备指纹
*/
export async function getDeviceFp(): Promise<void> {
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<TGApp.BBS.Response.getDeviceFp>(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 }));
}

View File

@@ -1,12 +1,13 @@
/** /**
* @file web utils getRequestHeader.ts * @file web/utils/getRequestHeader.ts
* @description 获取请求头 * @description 获取请求头
* @since Beta v0.3.4 * @since Beta v0.3.6
*/ */
import Md5 from "js-md5"; import Md5 from "js-md5";
import { transCookie, transParams } from "./tools"; import { transCookie, transParams } from "./tools";
import { getDeviceInfo, getRandomString } from "../../utils/toolFunc";
import TGConstant from "../constant/TGConstant"; 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); 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 * @description 获取 ds
* @since Beta v0.3.3 * @since Beta v0.3.3
@@ -80,7 +66,7 @@ function getDS(method: string, data: string, saltType: string, isSign: boolean):
/** /**
* @description 获取请求头 * @description 获取请求头
* @since Beta v0.3.0 * @since Beta v0.3.6
* @param {Record<string, string>} cookie cookie * @param {Record<string, string>} cookie cookie
* @param {string} method 请求方法 * @param {string} method 请求方法
* @param {Record<string, string|number>|string} data 请求数据 * @param {Record<string, string|number>|string} data 请求数据
@@ -102,11 +88,13 @@ export function getRequestHeader(
ds = getDS(method, transParams(data), saltType, isSign); ds = getDS(method, transParams(data), saltType, isSign);
} }
return { return {
"user-agent": TGConstant.BBS.USER_AGENT, "user-agent": TGConstant.BBS.UA_PC,
"x-rpc-app_version": TGConstant.BBS.VERSION, "x-rpc-app_version": TGConstant.BBS.VERSION,
"x-rpc-client_type": "5", "x-rpc-client_type": "5",
"x-requested-with": "com.mihoyo.hyperion", "x-requested-with": "com.mihoyo.hyperion",
referer: "https://webstatic.mihoyo.com", referer: "https://webstatic.mihoyo.com",
"x-rpc-device_id": getDeviceInfo("device_id"),
"x-rpc-device_fp": getDeviceInfo("device_fp"),
ds, ds,
cookie: transCookie(cookie), cookie: transCookie(cookie),
}; };