mirror of
https://github.com/ZZZure/ZZZ-Plugin.git
synced 2025-12-16 13:17:32 +00:00
fix: gacha
This commit is contained in:
parent
08c15fd378
commit
f838c0823d
11 changed files with 249 additions and 23 deletions
65
lib/assets.js
Normal file
65
lib/assets.js
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { findLowestLatencyUrl } from '../utils/network.js';
|
||||||
|
|
||||||
|
let lastFindFastestUrl = {
|
||||||
|
url: null,
|
||||||
|
time: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const URL_LIB = {
|
||||||
|
'[JPFRP]': 'http://jp-3.lcf.1l1.icu:17217',
|
||||||
|
'[HKFRP]': 'http://hk-1.lcf.1l1.icu:10200',
|
||||||
|
'[USFRP]': 'http://us-6.lcf.1l1.icu:28596',
|
||||||
|
'[XiaoWu]': 'http://frp.xiaowuap.com:63481',
|
||||||
|
'[Chuncheon]': 'https://kr.qxqx.cf',
|
||||||
|
'[Seoul]': 'https://kr-s.qxqx.cf',
|
||||||
|
'[Singapore]': 'https://sg.qxqx.cf',
|
||||||
|
};
|
||||||
|
|
||||||
|
const TYPE_PATH = {
|
||||||
|
wiki: 'wiki',
|
||||||
|
resource: 'resource',
|
||||||
|
guide: 'guide',
|
||||||
|
};
|
||||||
|
|
||||||
|
const RESOURCE_PATH = {
|
||||||
|
role: 'role',
|
||||||
|
role_circle: 'role_circle',
|
||||||
|
weapon: 'weapon',
|
||||||
|
};
|
||||||
|
|
||||||
|
const GUIDE_PATH = {
|
||||||
|
flower: 'flower',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFatestUrl = async () => {
|
||||||
|
if (
|
||||||
|
lastFindFastestUrl.url &&
|
||||||
|
Date.now() - lastFindFastestUrl.time < 1000 * 60 * 5
|
||||||
|
) {
|
||||||
|
return lastFindFastestUrl.url;
|
||||||
|
}
|
||||||
|
const urls = Object.values(URL_LIB);
|
||||||
|
const url = findLowestLatencyUrl(urls);
|
||||||
|
lastFindFastestUrl = {
|
||||||
|
url,
|
||||||
|
time: Date.now(),
|
||||||
|
};
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get resource remote path
|
||||||
|
* @param {keyof TYPE_PATH} type
|
||||||
|
* @param {keyof RESOURCE_PATH | keyof GUIDE_PATH} label
|
||||||
|
* @param {string} name
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const getRemotePath = async (type, label, name) => {
|
||||||
|
const url = await getFatestUrl();
|
||||||
|
return `${url}/ZZZeroUID/${type}/${label}/${name}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取资源远程路径
|
||||||
|
export const getResourceRemotePath = async (label, name) => {
|
||||||
|
return getRemotePath(TYPE_PATH.resource, label, name);
|
||||||
|
};
|
||||||
|
|
@ -1 +1,5 @@
|
||||||
export * as element from './convert/element.js';
|
export * as element from './convert/element.js';
|
||||||
|
|
||||||
|
export * as char from './convert/char.js';
|
||||||
|
|
||||||
|
export * as weapon from './convert/weapon.js';
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import settings from '../settings.js';
|
import settings from '../settings.js';
|
||||||
import PartnerId2SpriteId from '../../resources/map/PartnerId2SpriteId.json' assert { type: "json" };
|
import PartnerId2SpriteId from '../../resources/map/PartnerId2Data.json' assert { type: 'json' };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import WeaponId2Sprite from '../../resources/map/WeaponId2Sprite.json' assert { type: "json" };
|
import WeaponId2Sprite from '../../resources/map/WeaponId2Sprite.json' assert { type: 'json' };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} id
|
* @param {string} id
|
||||||
* @returns string
|
* @returns string
|
||||||
*/
|
*/
|
||||||
export const IDToWeaponName = id => {
|
export const IDToWeaponFileName = id => {
|
||||||
const data = WeaponId2Sprite?.[id];
|
const data = WeaponId2Sprite?.[id];
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
@ -13,7 +13,7 @@ export const IDToWeaponName = id => {
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @returns string
|
* @returns string
|
||||||
*/
|
*/
|
||||||
export const weaponNameToID = name => {
|
export const weaponFileNameToID = name => {
|
||||||
for (const [id, data] of Object.entries(WeaponId2Sprite)) {
|
for (const [id, data] of Object.entries(WeaponId2Sprite)) {
|
||||||
if (data === name) return id;
|
if (data === name) return id;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,39 @@ import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { ZZZ_SQUARE_AVATAR, ZZZ_SQUARE_BANGBOO } from './mysapi/api.js';
|
import { ZZZ_SQUARE_AVATAR, ZZZ_SQUARE_BANGBOO } from './mysapi/api.js';
|
||||||
import { imageResourcesPath } from './path.js';
|
import { imageResourcesPath } from './path.js';
|
||||||
|
import { weapon } from './convert.js';
|
||||||
|
import { getResourceRemotePath } from './assets.js';
|
||||||
|
|
||||||
const ZZZ_SQUARE_AVATAR_PATH = path.join(imageResourcesPath, 'square_avatar');
|
const ZZZ_SQUARE_AVATAR_PATH = path.join(imageResourcesPath, 'square_avatar');
|
||||||
const ZZZ_SQUARE_BANGBOO_PATH = path.join(
|
const ZZZ_SQUARE_BANGBOO_PATH = path.join(
|
||||||
imageResourcesPath,
|
imageResourcesPath,
|
||||||
'bangboo_square_avatar'
|
'bangboo_square_avatar'
|
||||||
);
|
);
|
||||||
|
const ZZZ_WEAPON_PATH = path.join(imageResourcesPath, 'weapon');
|
||||||
const ZZZ_GUIDES_PATH = path.join(imageResourcesPath, 'guides');
|
const ZZZ_GUIDES_PATH = path.join(imageResourcesPath, 'guides');
|
||||||
|
|
||||||
|
// 将下面的下载封装起来,支持错误重试5次
|
||||||
|
const downloadFile = async (url, savePath) => {
|
||||||
|
const _download = async (url, savePath, retry = 0) => {
|
||||||
|
if (retry > 5) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const download = await fetch(url);
|
||||||
|
const arrayBuffer = await download.arrayBuffer();
|
||||||
|
const buffer = Buffer.from(arrayBuffer);
|
||||||
|
if (!fs.existsSync(path.dirname(savePath))) {
|
||||||
|
fs.mkdirSync(path.dirname(savePath), { recursive: true });
|
||||||
|
}
|
||||||
|
fs.writeFileSync(savePath, buffer);
|
||||||
|
return savePath;
|
||||||
|
} catch (error) {
|
||||||
|
return await _download(url, savePath, retry + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return await _download(url, savePath);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {string | number} charID
|
* @param {string | number} charID
|
||||||
|
|
@ -21,14 +46,8 @@ export const getSquareAvatar = async charID => {
|
||||||
if (fs.existsSync(avatarPath)) return avatarPath;
|
if (fs.existsSync(avatarPath)) return avatarPath;
|
||||||
const url = `${ZZZ_SQUARE_AVATAR}/${filename}`;
|
const url = `${ZZZ_SQUARE_AVATAR}/${filename}`;
|
||||||
const savePath = avatarPath;
|
const savePath = avatarPath;
|
||||||
const download = await fetch(url);
|
const download = await downloadFile(url, savePath);
|
||||||
const arrayBuffer = await download.arrayBuffer();
|
return download;
|
||||||
const buffer = Buffer.from(arrayBuffer);
|
|
||||||
if (!fs.existsSync(ZZZ_SQUARE_AVATAR_PATH)) {
|
|
||||||
fs.mkdirSync(ZZZ_SQUARE_AVATAR_PATH, { recursive: true });
|
|
||||||
}
|
|
||||||
fs.writeFileSync(savePath, buffer);
|
|
||||||
return avatarPath;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -42,12 +61,25 @@ export const getSquareBangboo = async bangbooId => {
|
||||||
if (fs.existsSync(bangbooPath)) return bangbooPath;
|
if (fs.existsSync(bangbooPath)) return bangbooPath;
|
||||||
const url = `${ZZZ_SQUARE_BANGBOO}/${filename}`;
|
const url = `${ZZZ_SQUARE_BANGBOO}/${filename}`;
|
||||||
const savePath = bangbooPath;
|
const savePath = bangbooPath;
|
||||||
const download = await fetch(url);
|
const download = await downloadFile(url, savePath);
|
||||||
const arrayBuffer = await download.arrayBuffer();
|
return download;
|
||||||
const buffer = Buffer.from(arrayBuffer);
|
};
|
||||||
if (!fs.existsSync(ZZZ_SQUARE_BANGBOO_PATH)) {
|
|
||||||
fs.mkdirSync(ZZZ_SQUARE_BANGBOO_PATH, { recursive: true });
|
/**
|
||||||
}
|
* Get weapon image path
|
||||||
fs.writeFileSync(savePath, buffer);
|
* @param {string} id
|
||||||
return bangbooPath;
|
* @returns Promise<string>
|
||||||
|
*/
|
||||||
|
export const getWeaponImage = async id => {
|
||||||
|
logger.mark('getWeaponImage', id);
|
||||||
|
const name = weapon.IDToWeaponFileName(id);
|
||||||
|
logger.mark('getWeaponImage', name);
|
||||||
|
const filename = `${name}.png`;
|
||||||
|
const weaponPath = path.join(ZZZ_WEAPON_PATH, filename);
|
||||||
|
if (fs.existsSync(weaponPath)) return weaponPath;
|
||||||
|
const url = await getResourceRemotePath('weapon', filename);
|
||||||
|
const savePath = weaponPath;
|
||||||
|
const download = await downloadFile(url, savePath);
|
||||||
|
logger.mark('getWeaponImage', download);
|
||||||
|
return download;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ export async function updateGachaLog(authKey, uid) {
|
||||||
const lastSaved = previousLog[name]?.[0];
|
const lastSaved = previousLog[name]?.[0];
|
||||||
let page = 1;
|
let page = 1;
|
||||||
let endId = '0';
|
let endId = '0';
|
||||||
|
const newData = [];
|
||||||
for (const type of gacha_type_meta_data[name]) {
|
for (const type of gacha_type_meta_data[name]) {
|
||||||
queryLabel: while (true) {
|
queryLabel: while (true) {
|
||||||
const log = await getZZZGachaLogByAuthkey(
|
const log = await getZZZGachaLogByAuthkey(
|
||||||
|
|
@ -116,13 +117,14 @@ export async function updateGachaLog(authKey, uid) {
|
||||||
if (lastSaved && lastSaved.equals(item)) {
|
if (lastSaved && lastSaved.equals(item)) {
|
||||||
break queryLabel;
|
break queryLabel;
|
||||||
}
|
}
|
||||||
previousLog[name].push(item);
|
newData.push(item);
|
||||||
}
|
}
|
||||||
endId = log.list[log.list.length - 1]?.id || endId;
|
endId = log.list[log.list.length - 1]?.id || endId;
|
||||||
page++;
|
page++;
|
||||||
await sleep(400);
|
await sleep(400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
previousLog[name] = [...newData, ...previousLog[name]];
|
||||||
}
|
}
|
||||||
saveGachaLog(uid, previousLog);
|
saveGachaLog(uid, previousLog);
|
||||||
return previousLog;
|
return previousLog;
|
||||||
|
|
@ -180,9 +182,9 @@ export async function anaylizeGachaLog(uid) {
|
||||||
let luck = 0;
|
let luck = 0;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (const item of data) {
|
for (const item of data) {
|
||||||
await item.get_assets();
|
|
||||||
let isUp = true;
|
let isUp = true;
|
||||||
if (item.rank_type === '4') {
|
if (item.rank_type === '4') {
|
||||||
|
await item.get_assets();
|
||||||
if (NORMAL_LIST.includes(item.name)) {
|
if (NORMAL_LIST.includes(item.name)) {
|
||||||
isUp = false;
|
isUp = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
import { getSquareAvatar, getSquareBangboo } from '../lib/download.js';
|
import {
|
||||||
|
getSquareAvatar,
|
||||||
|
getSquareBangboo,
|
||||||
|
getWeaponImage,
|
||||||
|
} from '../lib/download.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class
|
* @class
|
||||||
|
|
@ -59,6 +63,8 @@ export class SingleGachaLog {
|
||||||
|
|
||||||
async get_assets() {
|
async get_assets() {
|
||||||
if (this.item_type === '音擎') {
|
if (this.item_type === '音擎') {
|
||||||
|
const result = await getWeaponImage(this.item_id);
|
||||||
|
this.square_icon = result;
|
||||||
} else if (this.item_type === '邦布') {
|
} else if (this.item_type === '邦布') {
|
||||||
const result = await getSquareBangboo(this.item_id);
|
const result = await getSquareBangboo(this.item_id);
|
||||||
this.square_icon = result;
|
this.square_icon = result;
|
||||||
|
|
|
||||||
86
resources/map/EquipId2Data_1.0.0.json
Normal file
86
resources/map/EquipId2Data_1.0.0.json
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
{
|
||||||
|
"31000": {
|
||||||
|
"equip_id_list": [
|
||||||
|
31021, 31022, 31023, 31024, 31025, 31026, 31031, 31032, 31033, 31034,
|
||||||
|
31035, 31036, 31041, 31042, 31043, 31044, 31045, 31046
|
||||||
|
],
|
||||||
|
"sprite_file": "3DSuitWoodpeckerElectro"
|
||||||
|
},
|
||||||
|
"31100": {
|
||||||
|
"equip_id_list": [
|
||||||
|
31121, 31122, 31123, 31124, 31125, 31126, 31131, 31132, 31133, 31134,
|
||||||
|
31135, 31136, 31141, 31142, 31143, 31144, 31145, 31146
|
||||||
|
],
|
||||||
|
"sprite_file": "3DSuitPufferElectro"
|
||||||
|
},
|
||||||
|
"31200": {
|
||||||
|
"equip_id_list": [
|
||||||
|
31221, 31222, 31223, 31224, 31225, 31226, 31231, 31232, 31233, 31234,
|
||||||
|
31235, 31236, 31241, 31242, 31243, 31244, 31245, 31246
|
||||||
|
],
|
||||||
|
"sprite_file": "3DSuitShockstarDisco"
|
||||||
|
},
|
||||||
|
"31300": {
|
||||||
|
"equip_id_list": [
|
||||||
|
31321, 31322, 31323, 31324, 31325, 31326, 31331, 31332, 31333, 31334,
|
||||||
|
31335, 31336, 31341, 31342, 31343, 31344, 31345, 31346
|
||||||
|
],
|
||||||
|
"sprite_file": "3DSuitFreedomBlues"
|
||||||
|
},
|
||||||
|
"31400": {
|
||||||
|
"equip_id_list": [
|
||||||
|
31421, 31422, 31423, 31424, 31425, 31426, 31431, 31432, 31433, 31434,
|
||||||
|
31435, 31436, 31441, 31442, 31443, 31444, 31445, 31446
|
||||||
|
],
|
||||||
|
"sprite_file": "3DSuitHormonePunk"
|
||||||
|
},
|
||||||
|
"31500": {
|
||||||
|
"equip_id_list": [
|
||||||
|
31521, 31522, 31523, 31524, 31525, 31526, 31531, 31532, 31533, 31534,
|
||||||
|
31535, 31536, 31541, 31542, 31543, 31544, 31545, 31546
|
||||||
|
],
|
||||||
|
"sprite_file": "3DSuitSoulRock"
|
||||||
|
},
|
||||||
|
"31600": {
|
||||||
|
"equip_id_list": [
|
||||||
|
31621, 31622, 31623, 31624, 31625, 31626, 31631, 31632, 31633, 31634,
|
||||||
|
31635, 31636, 31641, 31642, 31643, 31644, 31645, 31646
|
||||||
|
],
|
||||||
|
"sprite_file": "3DSuitSwingJazz"
|
||||||
|
},
|
||||||
|
"32200": {
|
||||||
|
"equip_id_list": [
|
||||||
|
32221, 32222, 32223, 32224, 32225, 32226, 32231, 32232, 32233, 32234,
|
||||||
|
32235, 32236, 32241, 32242, 32243, 32244, 32245, 32246
|
||||||
|
],
|
||||||
|
"sprite_file": "3DSuitInfernoMetal"
|
||||||
|
},
|
||||||
|
"32300": {
|
||||||
|
"equip_id_list": [
|
||||||
|
32321, 32322, 32323, 32324, 32325, 32326, 32331, 32332, 32333, 32334,
|
||||||
|
32335, 32336, 32341, 32342, 32343, 32344, 32345, 32346
|
||||||
|
],
|
||||||
|
"sprite_file": "3DSuitChaosMetal"
|
||||||
|
},
|
||||||
|
"32400": {
|
||||||
|
"equip_id_list": [
|
||||||
|
32421, 32422, 32423, 32424, 32425, 32426, 32431, 32432, 32433, 32434,
|
||||||
|
32435, 32436, 32441, 32442, 32443, 32444, 32445, 32446
|
||||||
|
],
|
||||||
|
"sprite_file": "3DSuitThunderMetal"
|
||||||
|
},
|
||||||
|
"32500": {
|
||||||
|
"equip_id_list": [
|
||||||
|
32521, 32522, 32523, 32524, 32525, 32526, 32531, 32532, 32533, 32534,
|
||||||
|
32535, 32536, 32541, 32542, 32543, 32544, 32545, 32546
|
||||||
|
],
|
||||||
|
"sprite_file": "3DSuitPolarMetal"
|
||||||
|
},
|
||||||
|
"32600": {
|
||||||
|
"equip_id_list": [
|
||||||
|
32621, 32622, 32623, 32624, 32625, 32626, 32631, 32632, 32633, 32634,
|
||||||
|
32635, 32636, 32641, 32642, 32643, 32644, 32645, 32646
|
||||||
|
],
|
||||||
|
"sprite_file": "3DSuitFangedMetal"
|
||||||
|
}
|
||||||
|
}
|
||||||
31
utils/network.js
Normal file
31
utils/network.js
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import http from 'http';
|
||||||
|
import https from 'https';
|
||||||
|
export async function checkLatency(url) {
|
||||||
|
let request = http;
|
||||||
|
if (url.startsWith('https')) {
|
||||||
|
request = https;
|
||||||
|
}
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const start = Date.now();
|
||||||
|
request
|
||||||
|
.get(url, res => {
|
||||||
|
res.on('data', () => {});
|
||||||
|
res.on('end', () => {
|
||||||
|
const latency = Date.now() - start;
|
||||||
|
resolve({ url, latency });
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.on('error', err => {
|
||||||
|
logger.mark(`Error checking ${url}:`, err.message);
|
||||||
|
resolve({ url, latency: Infinity });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function findLowestLatencyUrl(urls) {
|
||||||
|
const results = await Promise.all(urls.map(checkLatency));
|
||||||
|
const lowestLatencyResult = results.reduce((prev, curr) =>
|
||||||
|
prev.latency < curr.latency ? prev : curr
|
||||||
|
);
|
||||||
|
return lowestLatencyResult.url;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue