mirror of
https://github.com/ZZZure/ZZZ-Plugin.git
synced 2025-12-17 05:37:46 +00:00
refactor: 重构lib代码(无实质性功能更新,可不更新)
This commit is contained in:
parent
a7f06d404b
commit
aa3b7928ec
26 changed files with 547 additions and 483 deletions
|
|
@ -25,7 +25,10 @@ export class Abyss extends ZZZPlugin {
|
||||||
const method = this.e.msg.match(`(上期|往期)`)
|
const method = this.e.msg.match(`(上期|往期)`)
|
||||||
? 'zzzChallengePeriod'
|
? 'zzzChallengePeriod'
|
||||||
: 'zzzChallenge';
|
: 'zzzChallenge';
|
||||||
const abyssData = await api.getFinalData(this.e, method);
|
const abyssData = await api.getFinalData(method).catch(e => {
|
||||||
|
this.reply(e.message);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
if (!abyssData?.has_data) {
|
if (!abyssData?.has_data) {
|
||||||
await this.reply('没有式舆防卫战数据');
|
await this.reply('没有式舆防卫战数据');
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
15
apps/card.js
15
apps/card.js
|
|
@ -22,14 +22,23 @@ export class Card extends ZZZPlugin {
|
||||||
async card() {
|
async card() {
|
||||||
const { api } = await this.getAPI();
|
const { api } = await this.getAPI();
|
||||||
await this.getPlayerInfo();
|
await this.getPlayerInfo();
|
||||||
const indexData = await api.getFinalData(this.e, 'zzzIndex');
|
const indexData = await api.getFinalData('zzzIndex').catch(e => {
|
||||||
|
this.reply(e.message);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
if (!indexData) return false;
|
if (!indexData) return false;
|
||||||
|
|
||||||
let zzzAvatarList = await api.getFinalData(this.e, 'zzzAvatarList');
|
let zzzAvatarList = await api.getFinalData('zzzAvatarList').catch(e => {
|
||||||
|
this.reply(e.message);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
if (!zzzAvatarList) return false;
|
if (!zzzAvatarList) return false;
|
||||||
indexData.avatar_list = zzzAvatarList.avatar_list;
|
indexData.avatar_list = zzzAvatarList.avatar_list;
|
||||||
|
|
||||||
let zzzBuddyList = await api.getFinalData(this.e, 'zzzBuddyList');
|
let zzzBuddyList = await api.getFinalData('zzzBuddyList').catch(e => {
|
||||||
|
this.reply(e.message);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
if (!zzzBuddyList) return false;
|
if (!zzzBuddyList) return false;
|
||||||
indexData.buddy_list = zzzBuddyList.list;
|
indexData.buddy_list = zzzBuddyList.list;
|
||||||
const finalIndexData = new ZZZIndexResp(indexData);
|
const finalIndexData = new ZZZIndexResp(indexData);
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,9 @@ import { getAuthKey } from '../lib/authkey.js';
|
||||||
import settings from '../lib/settings.js';
|
import settings from '../lib/settings.js';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import common from '../../../lib/common/common.js';
|
import common from '../../../lib/common/common.js';
|
||||||
import {
|
import { anaylizeGachaLog, updateGachaLog } from '../lib/gacha.js';
|
||||||
anaylizeGachaLog,
|
import { getZZZGachaLink, getZZZGachaLogByAuthkey } from '../lib/gacha/core.js';
|
||||||
updateGachaLog,
|
import { gacha_type_meta_data } from '../lib/gacha/const.js';
|
||||||
getZZZGachaLink,
|
|
||||||
gacha_type_meta_data,
|
|
||||||
getZZZGachaLogByAuthkey,
|
|
||||||
} from '../lib/gacha.js';
|
|
||||||
import { getQueryVariable } from '../utils/network.js';
|
import { getQueryVariable } from '../utils/network.js';
|
||||||
import { rulePrefix } from '../lib/common.js';
|
import { rulePrefix } from '../lib/common.js';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { ZZZPlugin } from '../lib/plugin.js';
|
||||||
import { imageResourcesPath } from '../lib/path.js';
|
import { imageResourcesPath } from '../lib/path.js';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import settings from '../lib/settings.js';
|
import settings from '../lib/settings.js';
|
||||||
import { downloadFile } from '../lib/download.js';
|
import { downloadFile } from '../lib/download/core.js';
|
||||||
import { char } from '../lib/convert.js';
|
import { char } from '../lib/convert.js';
|
||||||
import guides from '../lib/guides.js';
|
import guides from '../lib/guides.js';
|
||||||
import { rulePrefix } from '../lib/common.js';
|
import { rulePrefix } from '../lib/common.js';
|
||||||
|
|
|
||||||
|
|
@ -251,7 +251,7 @@ export class Help extends ZZZPlugin {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '删除资源(需注意)',
|
title: '删除资源(需注意)',
|
||||||
desc: '请注意,此命令会删除自定义面板图,请确认做好备份后再执行!!!删除已经下载的资源,查询时需要再次下载(用于删除错误下载缓存)。',
|
desc: '删除已经下载的资源,查询时需要再次下载(用于删除错误下载缓存)。',
|
||||||
needCK: false,
|
needCK: false,
|
||||||
needSK: false,
|
needSK: false,
|
||||||
commands: ['删除全部/所有资源'],
|
commands: ['删除全部/所有资源'],
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import {
|
||||||
import { char } from '../../lib/convert.js';
|
import { char } from '../../lib/convert.js';
|
||||||
import { getAllEquipID } from '../../lib/convert/equip.js';
|
import { getAllEquipID } from '../../lib/convert/equip.js';
|
||||||
import { getAllWeaponID } from '../../lib/convert/weapon.js';
|
import { getAllWeaponID } from '../../lib/convert/weapon.js';
|
||||||
import { imageResourcesPath } from '../../lib/path.js';
|
import * as LocalURI from '../../lib/download/const.js';
|
||||||
|
|
||||||
export async function downloadAll() {
|
export async function downloadAll() {
|
||||||
if (!this.e.isMaster) return false;
|
if (!this.e.isMaster) return false;
|
||||||
|
|
@ -91,34 +91,11 @@ export async function downloadAll() {
|
||||||
}
|
}
|
||||||
const messages = [
|
const messages = [
|
||||||
'资源下载完成(成功的包含先前下载的图片)',
|
'资源下载完成(成功的包含先前下载的图片)',
|
||||||
'角色图需下载' +
|
`角色图需下载${charIDs.length}张,成功${result.char.success}张,失败${result.char.failed}张`,
|
||||||
charIDs.length +
|
`角色头像图需下载${charIDs.length}张,成功${esult.charSquare.success}张,失败${result.charSquare.failed}张`,
|
||||||
'张,成功' +
|
`角色头像图(练度统计)需下载${charIDs.length}张,成功${result.charSmallSquare.success}张,失败${result.charSmallSquare.failed}张`,
|
||||||
result.char.success +
|
`驱动盘套装图需下载${equipSprites.length}张,成功${result.equip.success}张,失败${result.equip.failed}张`,
|
||||||
'张,失败' +
|
`武器图需下载${weaponSprites.length}张,成功${result.weapon.success}张,失败${result.weapon.failed}张`,
|
||||||
result.char.failed +
|
|
||||||
'张',
|
|
||||||
'角色头像图需下载' +
|
|
||||||
charIDs.length +
|
|
||||||
'张,成功' +
|
|
||||||
result.charSquare.success +
|
|
||||||
'张,失败' +
|
|
||||||
result.charSquare.failed +
|
|
||||||
'张',
|
|
||||||
'套装图需下载' +
|
|
||||||
equipSprites.length +
|
|
||||||
'张,成功' +
|
|
||||||
result.equip.success +
|
|
||||||
'张,失败' +
|
|
||||||
result.equip.failed +
|
|
||||||
'张',
|
|
||||||
'武器图需下载' +
|
|
||||||
weaponSprites.length +
|
|
||||||
'张,成功' +
|
|
||||||
result.weapon.success +
|
|
||||||
'张,失败' +
|
|
||||||
result.weapon.failed +
|
|
||||||
'张',
|
|
||||||
];
|
];
|
||||||
await this.reply(messages.join('\n'));
|
await this.reply(messages.join('\n'));
|
||||||
}
|
}
|
||||||
|
|
@ -129,8 +106,10 @@ export async function deleteAll() {
|
||||||
false,
|
false,
|
||||||
{ at: true, recallMsg: 100 }
|
{ at: true, recallMsg: 100 }
|
||||||
);
|
);
|
||||||
if (fs.existsSync(imageResourcesPath)) {
|
for (const dir of LocalURI) {
|
||||||
fs.rmSync(imageResourcesPath, { recursive: true });
|
if (fs.existsSync(dir)) {
|
||||||
|
fs.rmSync(dir, { recursive: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await this.reply('资源图片已删除!', false, { at: true, recallMsg: 100 });
|
await this.reply('资源图片已删除!', false, { at: true, recallMsg: 100 });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { char } from '../../lib/convert.js';
|
import { char } from '../../lib/convert.js';
|
||||||
import { downloadFile } from '../../lib/download.js';
|
import { downloadFile } from '../../lib/download/core.js';
|
||||||
import { imageResourcesPath } from '../../lib/path.js';
|
import { imageResourcesPath } from '../../lib/path.js';
|
||||||
import common from '../../../../lib/common/common.js';
|
import common from '../../../../lib/common/common.js';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,10 @@ export class Note extends ZZZPlugin {
|
||||||
async note() {
|
async note() {
|
||||||
const { api } = await this.getAPI();
|
const { api } = await this.getAPI();
|
||||||
await this.getPlayerInfo();
|
await this.getPlayerInfo();
|
||||||
const noteResponse = await api.getFinalData(this.e, 'zzzNote');
|
const noteResponse = await api.getFinalData('zzzNote').catch(e => {
|
||||||
|
this.reply(e.message);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
if (!noteResponse) return false;
|
if (!noteResponse) return false;
|
||||||
const noteData = new ZZZNoteResp(noteResponse);
|
const noteData = new ZZZNoteResp(noteResponse);
|
||||||
const finalData = {
|
const finalData = {
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,10 @@ export class Panel extends ZZZPlugin {
|
||||||
await redis.set(`ZZZ:PANEL:${uid}:LASTTIME`, Date.now());
|
await redis.set(`ZZZ:PANEL:${uid}:LASTTIME`, Date.now());
|
||||||
await this.reply('正在刷新面板列表,请稍候...');
|
await this.reply('正在刷新面板列表,请稍候...');
|
||||||
await this.getPlayerInfo();
|
await this.getPlayerInfo();
|
||||||
const result = await refreshPanel(this.e, api, uid);
|
const result = await refreshPanel(api, uid).catch(e => {
|
||||||
|
this.reply(e.message);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
if (!result) {
|
if (!result) {
|
||||||
await this.reply('面板列表刷新失败,请稍后再试');
|
await this.reply('面板列表刷新失败,请稍后再试');
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,16 @@
|
||||||
import { findLowestLatencyUrl } from '../utils/network.js';
|
import { findLowestLatencyUrl } from '../utils/network.js';
|
||||||
|
import {
|
||||||
|
URL_LIB,
|
||||||
|
TYPE_PATH,
|
||||||
|
RESOURCE_PATH,
|
||||||
|
GUIDE_PATH,
|
||||||
|
} from './assets/const.js';
|
||||||
// 保存上次找的节点
|
// 保存上次找的节点
|
||||||
let lastFindFastestUrl = {
|
let lastFindFastestUrl = {
|
||||||
url: null,
|
url: null,
|
||||||
time: 0,
|
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',
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取最快节点
|
* 获取最快节点
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
|
|
@ -61,10 +36,10 @@ export const getFatestUrl = async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get resource remote path
|
* 获取远程路径
|
||||||
* @param {keyof TYPE_PATH} type
|
* @param {keyof TYPE_PATH} type 资源类型
|
||||||
* @param {keyof RESOURCE_PATH | keyof GUIDE_PATH} label
|
* @param {keyof RESOURCE_PATH | keyof GUIDE_PATH} label 资源标签
|
||||||
* @param {string} name
|
* @param {string} name 资源名称
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getRemotePath = async (type, label, name) => {
|
export const getRemotePath = async (type, label, name) => {
|
||||||
|
|
@ -72,7 +47,12 @@ export const getRemotePath = async (type, label, name) => {
|
||||||
return `${url}/ZZZeroUID/${type}/${label}/${name}`;
|
return `${url}/ZZZeroUID/${type}/${label}/${name}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取资源远程路径
|
/**
|
||||||
|
* 获取资源远程路径
|
||||||
|
* @param {keyof RESOURCE_PATH} label 资源标签
|
||||||
|
* @param {string} name 资源名称
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
export const getResourceRemotePath = async (label, name) => {
|
export const getResourceRemotePath = async (label, name) => {
|
||||||
return getRemotePath(TYPE_PATH.resource, label, name);
|
return getRemotePath(TYPE_PATH.resource, label, name);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
29
lib/assets/const.js
Normal file
29
lib/assets/const.js
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
// 节点列表
|
||||||
|
export 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',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 文件类型路径
|
||||||
|
export const TYPE_PATH = {
|
||||||
|
wiki: 'wiki',
|
||||||
|
resource: 'resource',
|
||||||
|
guide: 'guide',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 资源路径
|
||||||
|
export const RESOURCE_PATH = {
|
||||||
|
role: 'role',
|
||||||
|
role_circle: 'role_circle',
|
||||||
|
weapon: 'weapon',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 图鉴路径
|
||||||
|
export const GUIDE_PATH = {
|
||||||
|
flower: 'flower',
|
||||||
|
};
|
||||||
|
|
@ -5,14 +5,13 @@ import { char } from './convert.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取角色基础信息列表
|
* 获取角色基础信息列表
|
||||||
* @param {*} e 消息事件
|
|
||||||
* @param {MysZZZApi} api
|
* @param {MysZZZApi} api
|
||||||
* @param {boolean} origin 是否返回原始数据
|
* @param {boolean} origin 是否返回原始数据
|
||||||
* @returns {Promise<ZZZAvatarBasic[] | null>}
|
* @returns {Promise<ZZZAvatarBasic[] | null>}
|
||||||
*/
|
*/
|
||||||
export const getAvatarBasicList = async (e, api, origin = false) => {
|
export const getAvatarBasicList = async (api, origin = false) => {
|
||||||
// 获取米游社角色列表
|
// 获取米游社角色列表
|
||||||
const avatarBaseListData = await api.getFinalData(e, 'zzzAvatarList');
|
const avatarBaseListData = await api.getFinalData('zzzAvatarList');
|
||||||
if (!avatarBaseListData) return null;
|
if (!avatarBaseListData) return null;
|
||||||
// 是否返回原始数据
|
// 是否返回原始数据
|
||||||
if (origin) return avatarBaseListData.avatar_list;
|
if (origin) return avatarBaseListData.avatar_list;
|
||||||
|
|
@ -25,17 +24,16 @@ export const getAvatarBasicList = async (e, api, origin = false) => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取角色详细信息列表
|
* 获取角色详细信息列表
|
||||||
* @param {*} e 消息事件
|
|
||||||
* @param {MysZZZApi} api
|
* @param {MysZZZApi} api
|
||||||
* @returns {Promise<ZZZAvatarInfo[] | null>}
|
* @returns {Promise<ZZZAvatarInfo[] | null>}
|
||||||
* @param {boolean} origin 是否返回原始数据
|
* @param {boolean} origin 是否返回原始数据
|
||||||
*/
|
*/
|
||||||
export const getAvatarInfoList = async (e, api, origin = false) => {
|
export const getAvatarInfoList = async (api, origin = false) => {
|
||||||
// 获取角色基础信息列表
|
// 获取角色基础信息列表
|
||||||
const avatarBaseList = await getAvatarBasicList(e, api, origin);
|
const avatarBaseList = await getAvatarBasicList(api, origin);
|
||||||
if (!avatarBaseList) return null;
|
if (!avatarBaseList) return null;
|
||||||
// 获取角色详细信息
|
// 获取角色详细信息
|
||||||
const avatarInfoList = await api.getFinalData(e, 'zzzAvatarInfo', {
|
const avatarInfoList = await api.getFinalData('zzzAvatarInfo', {
|
||||||
query: {
|
query: {
|
||||||
id_list: avatarBaseList.map(item => item.id),
|
id_list: avatarBaseList.map(item => item.id),
|
||||||
},
|
},
|
||||||
|
|
@ -52,16 +50,15 @@ export const getAvatarInfoList = async (e, api, origin = false) => {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 刷新面板
|
* 刷新面板
|
||||||
* @param {*} e 消息事件
|
|
||||||
* @param {MysZZZApi} api
|
* @param {MysZZZApi} api
|
||||||
* @param {string} uid
|
* @param {string} uid
|
||||||
* @returns {Promise<ZZZAvatarInfo[] | null>}
|
* @returns {Promise<ZZZAvatarInfo[] | null>}
|
||||||
*/
|
*/
|
||||||
export const refreshPanel = async (e, api, uid) => {
|
export const refreshPanel = async (api, uid) => {
|
||||||
// 获取已保存数据
|
// 获取已保存数据
|
||||||
const originData = getPanelData(uid);
|
const originData = getPanelData(uid);
|
||||||
// 获取新数据
|
// 获取新数据
|
||||||
const newData = await getAvatarInfoList(e, api, true);
|
const newData = await getAvatarInfoList(api, true);
|
||||||
if (!newData) return null;
|
if (!newData) return null;
|
||||||
// 初始化最终数据
|
// 初始化最终数据
|
||||||
const finalData = [...newData];
|
const finalData = [...newData];
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import User from '../../genshin/model/user.js';
|
import User from '../../genshin/model/user.js';
|
||||||
import { getStoken } from './authkey.js';
|
import { getStoken } from './authkey.js';
|
||||||
|
|
||||||
export const rulePrefix = '^((#|%)?(zzz|ZZZ|绝区零))';
|
export const rulePrefix = '^((#|%|/)?(zzz|ZZZ|绝区零))';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取米游社用户的 cookie
|
* 获取米游社用户的 cookie
|
||||||
|
|
|
||||||
45
lib/db.js
45
lib/db.js
|
|
@ -1,47 +1,4 @@
|
||||||
import { readFileSync, writeFileSync } from 'fs';
|
import { getDB, setDB } from './db/core.js';
|
||||||
import path from 'path';
|
|
||||||
import { checkFolderExistAndCreate } from '../utils/file.js';
|
|
||||||
import { dataPath } from './path.js';
|
|
||||||
const dbPath = {
|
|
||||||
gacha: 'gacha',
|
|
||||||
panel: 'panel',
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {string} dbName
|
|
||||||
* @param {string} dbFile
|
|
||||||
* @returns {object | Array<object> | null}
|
|
||||||
*/
|
|
||||||
export function getDB(dbName, dbFile) {
|
|
||||||
const db = dbPath[dbName];
|
|
||||||
const dbFolder = path.join(dataPath, db);
|
|
||||||
try {
|
|
||||||
const dbPath = path.join(dbFolder, `${dbFile}.json`);
|
|
||||||
return JSON.parse(readFileSync(dbPath, 'utf-8'));
|
|
||||||
} catch (error) {
|
|
||||||
logger.debug(`读取数据库失败: ${error.message}`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {string} dbName
|
|
||||||
* @param {string} dbFile
|
|
||||||
* @param {object} data
|
|
||||||
*/
|
|
||||||
export function setDB(dbName, dbFile, data) {
|
|
||||||
const db = dbPath[dbName];
|
|
||||||
const dbFolder = path.join(dataPath, db);
|
|
||||||
try {
|
|
||||||
checkFolderExistAndCreate(dbFolder);
|
|
||||||
const dbPath = path.join(dbFolder, `${dbFile}.json`);
|
|
||||||
writeFileSync(dbPath, JSON.stringify(data, null, 2));
|
|
||||||
} catch (error) {
|
|
||||||
logger.debug(`读取数据库失败: ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} uid
|
* @param {string} uid
|
||||||
|
|
|
||||||
44
lib/db/core.js
Normal file
44
lib/db/core.js
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { readFileSync, writeFileSync } from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { checkFolderExistAndCreate } from '../../utils/file.js';
|
||||||
|
import { dataPath } from '../path.js';
|
||||||
|
export const dbPath = {
|
||||||
|
gacha: 'gacha',
|
||||||
|
panel: 'panel',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取数据库
|
||||||
|
* @param {keyof dbPath} dbName
|
||||||
|
* @param {string} dbFile
|
||||||
|
* @returns {object | Array<object> | null}
|
||||||
|
*/
|
||||||
|
export function getDB(dbName, dbFile) {
|
||||||
|
const db = dbPath[dbName];
|
||||||
|
const dbFolder = path.join(dataPath, db);
|
||||||
|
try {
|
||||||
|
const dbPath = path.join(dbFolder, `${dbFile}.json`);
|
||||||
|
return JSON.parse(readFileSync(dbPath, 'utf-8'));
|
||||||
|
} catch (error) {
|
||||||
|
logger.debug(`读取数据库失败: ${error.message}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写入数据库
|
||||||
|
* @param {string} dbName
|
||||||
|
* @param {string} dbFile
|
||||||
|
* @param {object} data
|
||||||
|
*/
|
||||||
|
export function setDB(dbName, dbFile, data) {
|
||||||
|
const db = dbPath[dbName];
|
||||||
|
const dbFolder = path.join(dataPath, db);
|
||||||
|
try {
|
||||||
|
checkFolderExistAndCreate(dbFolder);
|
||||||
|
const dbPath = path.join(dbFolder, `${dbFile}.json`);
|
||||||
|
writeFileSync(dbPath, JSON.stringify(data, null, 2));
|
||||||
|
} catch (error) {
|
||||||
|
logger.debug(`读取数据库失败: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
171
lib/download.js
171
lib/download.js
|
|
@ -1,72 +1,8 @@
|
||||||
import path from 'path';
|
import * as convert from './convert.js';
|
||||||
import fs from 'fs';
|
|
||||||
import {
|
import {
|
||||||
ZZZ_SQUARE_AVATAR,
|
downloadMysImage,
|
||||||
ZZZ_SQUARE_BANGBOO,
|
downloadResourceImage,
|
||||||
NEW_ZZZ_SQUARE_BANGBOO,
|
} from './download/download.js';
|
||||||
NEW_ZZZ_SQUARE_AVATAR,
|
|
||||||
} from './mysapi/api.js';
|
|
||||||
import { imageResourcesPath } from './path.js';
|
|
||||||
import { char, equip, weapon } from './convert.js';
|
|
||||||
import { getResourceRemotePath } from './assets.js';
|
|
||||||
import request from '../utils/request.js';
|
|
||||||
|
|
||||||
const ZZZ_SQUARE_AVATAR_PATH = path.join(imageResourcesPath, 'square_avatar');
|
|
||||||
const ZZZ_SMALL_SQUARE_AVATAR_PATH = path.join(
|
|
||||||
imageResourcesPath,
|
|
||||||
'role_general'
|
|
||||||
);
|
|
||||||
const ZZZ_SQUARE_BANGBOO_PATH = path.join(
|
|
||||||
imageResourcesPath,
|
|
||||||
'bangboo_square_avatar'
|
|
||||||
);
|
|
||||||
const ZZZ_WEAPON_PATH = path.join(imageResourcesPath, 'weapon');
|
|
||||||
const ZZZ_ROLE_PATH = path.join(imageResourcesPath, 'role');
|
|
||||||
const ZZZ_ROLE_CIRCLE_PATH = path.join(imageResourcesPath, 'role_circle');
|
|
||||||
const ZZZ_SUIT_3D_PATH = path.join(imageResourcesPath, 'suit_3d');
|
|
||||||
const ZZZ_SUIT_PATH = path.join(imageResourcesPath, 'suit');
|
|
||||||
// const ZZZ_GUIDES_PATH = path.join(imageResourcesPath, 'guides');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 下载文件
|
|
||||||
* @param {string} url 下载地址
|
|
||||||
* @param {string} savePath 保存路径
|
|
||||||
* @returns {Promise<string | null>} 保存路径
|
|
||||||
*/
|
|
||||||
export const downloadFile = async (url, savePath) => {
|
|
||||||
// 下载文件
|
|
||||||
try {
|
|
||||||
const download = await request(url, {}, 5);
|
|
||||||
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 null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查看文件是否存在,如果存在则返回路径,否则下载文件
|
|
||||||
* @param {string} url 下载地址
|
|
||||||
* @param {string} savePath 保存路径
|
|
||||||
* @returns {Promise<string | null>} 保存路径
|
|
||||||
*/
|
|
||||||
export const checkFile = async (url, savePath) => {
|
|
||||||
if (fs.existsSync(savePath)) {
|
|
||||||
const stats = fs.statSync(savePath);
|
|
||||||
if (stats.size > 0) {
|
|
||||||
return savePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const download = await downloadFile(url, savePath);
|
|
||||||
return download;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取角色头像(方形)
|
* 获取角色头像(方形)
|
||||||
|
|
@ -75,96 +11,94 @@ export const checkFile = async (url, savePath) => {
|
||||||
*/
|
*/
|
||||||
export const getSquareAvatar = async charID => {
|
export const getSquareAvatar = async charID => {
|
||||||
const filename = `role_square_avatar_${charID}.png`;
|
const filename = `role_square_avatar_${charID}.png`;
|
||||||
const avatarPath = path.join(ZZZ_SQUARE_AVATAR_PATH, filename);
|
const result = await downloadMysImage(
|
||||||
let url = `${ZZZ_SQUARE_AVATAR}/${filename}`;
|
'ZZZ_SQUARE_AVATAR',
|
||||||
let result = await checkFile(url, avatarPath);
|
'ZZZ_SQUARE_AVATAR_PATH',
|
||||||
if (!result) {
|
filename,
|
||||||
url = `${NEW_ZZZ_SQUARE_AVATAR}/${filename}`;
|
'NEW_ZZZ_SQUARE_AVATAR'
|
||||||
result = await checkFile(url, avatarPath);
|
);
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取角色头像(小方形)
|
* 获取角色头像(小方形)
|
||||||
* @param {string | number} charID
|
* @param {string | number} charID
|
||||||
* @returns Promise<string>
|
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
export const getSmallSquareAvatar = async charID => {
|
export const getSmallSquareAvatar = async charID => {
|
||||||
const sprite = char.IDToCharSprite(charID);
|
const sprite = convert.char.IDToCharSprite(charID);
|
||||||
if (!sprite) return null;
|
if (!sprite) return null;
|
||||||
const filename = `IconRoleGeneral${sprite}.png`;
|
const filename = `IconRoleGeneral${sprite}.png`;
|
||||||
const avatarPath = path.join(ZZZ_SMALL_SQUARE_AVATAR_PATH, filename);
|
const result = await downloadResourceImage(
|
||||||
const url = await getResourceRemotePath('role_general', filename);
|
'role_general',
|
||||||
const result = await checkFile(url, avatarPath);
|
'ZZZ_SMALL_SQUARE_AVATAR_PATH',
|
||||||
|
filename
|
||||||
|
);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取邦布头像(方形)
|
* 获取邦布头像(方形)
|
||||||
* @param {string | number} bangbooId
|
* @param {string | number} bangbooId
|
||||||
* @returns Promise<string>
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
export const getSquareBangboo = async bangbooId => {
|
export const getSquareBangboo = async bangbooId => {
|
||||||
const filename = `bangboo_rectangle_avatar_${bangbooId}.png`;
|
const filename = `bangboo_rectangle_avatar_${bangbooId}.png`;
|
||||||
const bangbooPath = path.join(ZZZ_SQUARE_BANGBOO_PATH, filename);
|
const result = await downloadMysImage(
|
||||||
let url = `${ZZZ_SQUARE_BANGBOO}/${filename}`;
|
'ZZZ_SQUARE_BANGBOO',
|
||||||
let result = await checkFile(url, bangbooPath);
|
'ZZZ_SQUARE_BANGBOO_PATH',
|
||||||
if (!result) {
|
filename,
|
||||||
url = `${NEW_ZZZ_SQUARE_BANGBOO}/${filename}`;
|
'NEW_ZZZ_SQUARE_BANGBOO'
|
||||||
result = await checkFile(url, bangbooPath);
|
);
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取武器图片
|
* 获取武器图片
|
||||||
* @param {string} id
|
* @param {string} id
|
||||||
* @returns Promise<string>
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
export const getWeaponImage = async id => {
|
export const getWeaponImage = async id => {
|
||||||
const name = weapon.IDToWeaponFileName(id);
|
const name = convert.weapon.IDToWeaponFileName(id);
|
||||||
let filename = `${name}_High.png`;
|
if (!name) return null;
|
||||||
const weaponPath = path.join(ZZZ_WEAPON_PATH, filename);
|
const filename = `${name}_High.png`;
|
||||||
const url = await getResourceRemotePath('weapon', filename);
|
const replaceFilename = `${name}_High.png`;
|
||||||
let result = await checkFile(url, weaponPath);
|
const result = await downloadResourceImage(
|
||||||
if (!result) {
|
'weapon',
|
||||||
filename = `${name}.png`;
|
'ZZZ_WEAPON_PATH',
|
||||||
const weaponPath = path.join(ZZZ_WEAPON_PATH, filename);
|
filename,
|
||||||
const url = await getResourceRemotePath('weapon', filename);
|
replaceFilename
|
||||||
result = await checkFile(url, weaponPath);
|
);
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取角色图片
|
* 获取角色图片
|
||||||
* @param {string | number} id
|
* @param {string | number} id
|
||||||
* @returns Promise<string>
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
export const getRoleImage = async id => {
|
export const getRoleImage = async id => {
|
||||||
const sprite = char.IDToCharSprite(id);
|
const sprite = convert.char.IDToCharSprite(id);
|
||||||
if (!sprite) return null;
|
if (!sprite) return null;
|
||||||
const filename = `IconRole${sprite}.png`;
|
const filename = `IconRole${sprite}.png`;
|
||||||
const rolePath = path.join(ZZZ_ROLE_PATH, filename);
|
const result = await downloadResourceImage('role', 'ZZZ_ROLE_PATH', filename);
|
||||||
const url = await getResourceRemotePath('role', filename);
|
|
||||||
const result = await checkFile(url, rolePath);
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取角色圆形图片
|
* 获取角色圆形图片
|
||||||
* @param {string | number} id
|
* @param {string | number} id
|
||||||
* @returns Promise<string>
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
export const getRoleCircleImage = async id => {
|
export const getRoleCircleImage = async id => {
|
||||||
const sprite = char.IDToCharSprite(id);
|
const sprite = convert.char.IDToCharSprite(id);
|
||||||
if (!sprite) return null;
|
if (!sprite) return null;
|
||||||
const filename = `IconRoleCircle${sprite}.png`;
|
const filename = `IconRoleCircle${sprite}.png`;
|
||||||
const roleCirclePath = path.join(ZZZ_ROLE_CIRCLE_PATH, filename);
|
const result = await downloadResourceImage(
|
||||||
const url = await getResourceRemotePath('role_circle', filename);
|
'role_circle',
|
||||||
const result = await checkFile(url, roleCirclePath);
|
'ZZZ_ROLE_CIRCLE_PATH',
|
||||||
|
filename
|
||||||
|
);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -174,11 +108,10 @@ export const getRoleCircleImage = async id => {
|
||||||
* @returns Promise<string>
|
* @returns Promise<string>
|
||||||
*/
|
*/
|
||||||
export const getSuitImage = async suitId => {
|
export const getSuitImage = async suitId => {
|
||||||
const suitName = equip.equipIdToSprite(suitId);
|
const suitName = convert.equip.equipIdToSprite(suitId);
|
||||||
|
if (!suitName) return null;
|
||||||
const filename = `${suitName}.png`;
|
const filename = `${suitName}.png`;
|
||||||
const suitPath = path.join(ZZZ_SUIT_PATH, filename);
|
const result = await downloadResourceImage('suit', 'ZZZ_SUIT_PATH', filename);
|
||||||
const url = await getResourceRemotePath('suit', filename);
|
|
||||||
const result = await checkFile(url, suitPath);
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -188,10 +121,12 @@ export const getSuitImage = async suitId => {
|
||||||
* @returns Promise<string>
|
* @returns Promise<string>
|
||||||
*/
|
*/
|
||||||
export const getSuit3DImage = async suitId => {
|
export const getSuit3DImage = async suitId => {
|
||||||
const suitName = equip.equipIdToSprite(suitId);
|
const suitName = convert.equip.equipIdToSprite(suitId);
|
||||||
const filename = `${suitName}_3d.png`;
|
const filename = `${suitName}_3d.png`;
|
||||||
const suitPath = path.join(ZZZ_SUIT_3D_PATH, filename);
|
const result = await downloadResourceImage(
|
||||||
const url = await getResourceRemotePath('suit_3d', filename);
|
'suit_3d',
|
||||||
const result = await checkFile(url, suitPath);
|
'ZZZ_SUIT_3D_PATH',
|
||||||
|
filename
|
||||||
|
);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
18
lib/download/const.js
Normal file
18
lib/download/const.js
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import path from 'path';
|
||||||
|
import { imageResourcesPath } from '../path.js';
|
||||||
|
|
||||||
|
export const ZZZ_SQUARE_AVATAR_PATH = path.join(
|
||||||
|
imageResourcesPath,
|
||||||
|
'square_avatar'
|
||||||
|
),
|
||||||
|
ZZZ_SMALL_SQUARE_AVATAR_PATH = path.join(imageResourcesPath, 'role_general'),
|
||||||
|
ZZZ_SQUARE_BANGBOO_PATH = path.join(
|
||||||
|
imageResourcesPath,
|
||||||
|
'bangboo_square_avatar'
|
||||||
|
),
|
||||||
|
ZZZ_WEAPON_PATH = path.join(imageResourcesPath, 'weapon'),
|
||||||
|
ZZZ_ROLE_PATH = path.join(imageResourcesPath, 'role'),
|
||||||
|
ZZZ_ROLE_CIRCLE_PATH = path.join(imageResourcesPath, 'role_circle'),
|
||||||
|
ZZZ_SUIT_3D_PATH = path.join(imageResourcesPath, 'suit_3d'),
|
||||||
|
ZZZ_SUIT_PATH = path.join(imageResourcesPath, 'suit');
|
||||||
|
// const ZZZ_GUIDES_PATH = path.join(imageResourcesPath, 'guides');
|
||||||
44
lib/download/core.js
Normal file
44
lib/download/core.js
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import request from '../../utils/request.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件
|
||||||
|
* @param {string} url 下载地址
|
||||||
|
* @param {string} savePath 保存路径
|
||||||
|
* @returns {Promise<string | null>} 保存路径
|
||||||
|
*/
|
||||||
|
export const downloadFile = async (url, savePath) => {
|
||||||
|
// 下载文件
|
||||||
|
try {
|
||||||
|
const download = await request(url, {}, 5);
|
||||||
|
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 null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看文件是否存在,如果存在则返回路径,否则下载文件
|
||||||
|
* @param {string} url 下载地址
|
||||||
|
* @param {string} savePath 保存路径
|
||||||
|
* @returns {Promise<string | null>} 保存路径
|
||||||
|
*/
|
||||||
|
export const checkFile = async (url, savePath) => {
|
||||||
|
if (fs.existsSync(savePath)) {
|
||||||
|
const stats = fs.statSync(savePath);
|
||||||
|
if (stats.size > 0) {
|
||||||
|
return savePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const download = await downloadFile(url, savePath);
|
||||||
|
return download;
|
||||||
|
};
|
||||||
57
lib/download/download.js
Normal file
57
lib/download/download.js
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import path from 'path';
|
||||||
|
import { checkFile } from './core.js';
|
||||||
|
import { getResourceRemotePath } from '../assets.js';
|
||||||
|
import * as MysURL from '../mysapi/api.js';
|
||||||
|
import * as LocalURI from './const.js';
|
||||||
|
/**
|
||||||
|
* 下载米游社图片
|
||||||
|
* @param {keyof MysURL} base 远程地址
|
||||||
|
* @param {keyof LocalURI} localBase 本地地址
|
||||||
|
* @param {string} filename 文件名
|
||||||
|
* @param {keyof MysURL} newBase 新远程地址
|
||||||
|
*/
|
||||||
|
export const downloadMysImage = async (
|
||||||
|
base,
|
||||||
|
localBase,
|
||||||
|
filename,
|
||||||
|
newBase = ''
|
||||||
|
) => {
|
||||||
|
base = MysURL[base];
|
||||||
|
localBase = LocalURI[localBase];
|
||||||
|
if (!!newBase) {
|
||||||
|
newBase = MysURL[newBase];
|
||||||
|
}
|
||||||
|
const finalPath = path.join(localBase, filename);
|
||||||
|
let url = `${base}/${filename}`;
|
||||||
|
let result = await checkFile(url, finalPath);
|
||||||
|
if (!result && !!newBase) {
|
||||||
|
url = `${newBase}/${filename}`;
|
||||||
|
result = await checkFile(url, finalPath);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载资源库图片
|
||||||
|
* @param {Parameters<getResourceRemotePath>[0]} remoteLabel 远程地址
|
||||||
|
* @param {keyof LocalURI} localBase 本地地址
|
||||||
|
* @param {string} filename 文件名
|
||||||
|
* @param {string} replaceFilename 替换文件名(如果资源不存在)
|
||||||
|
*/
|
||||||
|
export const downloadResourceImage = async (
|
||||||
|
remoteLabel,
|
||||||
|
localBase,
|
||||||
|
filename,
|
||||||
|
replaceFilename = ''
|
||||||
|
) => {
|
||||||
|
localBase = LocalURI[localBase];
|
||||||
|
const finalPath = path.join(localBase, filename);
|
||||||
|
const url = await getResourceRemotePath(remoteLabel, filename);
|
||||||
|
let result = await checkFile(url, finalPath);
|
||||||
|
if (!result && !!replaceFilename) {
|
||||||
|
const finalPath = path.join(localBase, replaceFilename);
|
||||||
|
const url = await getResourceRemotePath(remoteLabel, replaceFilename);
|
||||||
|
result = await checkFile(url, finalPath);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
37
lib/error.js
Normal file
37
lib/error.js
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
export class MysError extends Error {
|
||||||
|
/**
|
||||||
|
* 自定义错误类
|
||||||
|
* @param {string} code 错误码
|
||||||
|
*/
|
||||||
|
constructor(code, uid, result) {
|
||||||
|
let msg = '未知错误';
|
||||||
|
switch (code) {
|
||||||
|
case '10102':
|
||||||
|
if (result.message === 'Data is not public for the user') {
|
||||||
|
msg = `UID:${uid},米游社数据未公开`;
|
||||||
|
} else {
|
||||||
|
msg = `UID:${uid},请先去米游社绑定角色`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '10041':
|
||||||
|
case '5003':
|
||||||
|
msg = `UID:${uid},米游社账号异常,暂时无法查询,请发送 %绑定设备帮助 查看如何绑定设备`;
|
||||||
|
break;
|
||||||
|
case '10035':
|
||||||
|
case '1034':
|
||||||
|
msg = `UID:${uid},米游社查询遇到验证码`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (/(登录|login)/i.test(res.message)) {
|
||||||
|
msg = `UID:${uid},米游社cookie已失效`;
|
||||||
|
} else {
|
||||||
|
msg = `UID:${uid},米游社接口报错:${res.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super(msg);
|
||||||
|
this.code = Number(code);
|
||||||
|
this.uid = uid;
|
||||||
|
this.result = result;
|
||||||
|
this.name = 'MysError';
|
||||||
|
}
|
||||||
|
}
|
||||||
163
lib/gacha.js
163
lib/gacha.js
|
|
@ -1,139 +1,16 @@
|
||||||
import { SingleGachaLog, ZZZGachaLogResp } from '../model/gacha.js';
|
import { SingleGachaLog } from '../model/gacha.js';
|
||||||
import { sleep } from '../utils/time.js';
|
import { sleep } from '../utils/time.js';
|
||||||
import { rank } from './convert.js';
|
import { rank } from './convert.js';
|
||||||
import { getGachaLog, saveGachaLog } from './db.js';
|
import { getGachaLog, saveGachaLog } from './db.js';
|
||||||
import { ZZZ_GET_GACHA_LOG_API } from './mysapi/api.js';
|
import {
|
||||||
import request from '../utils/request.js';
|
gacha_type_meta_data,
|
||||||
|
FLOORS_MAP,
|
||||||
// 池子代码
|
HOMO_TAG,
|
||||||
export const gacha_type_meta_data = {
|
EMOJI,
|
||||||
音擎频段: ['3001'],
|
NORMAL_LIST,
|
||||||
独家频段: ['2001'],
|
} from './gacha/const.js';
|
||||||
常驻频段: ['1001'],
|
import { getLevelFromList } from './gacha/tool.js';
|
||||||
邦布频段: ['5001'],
|
import { getZZZGachaLogByAuthkey } from './gacha/core.js';
|
||||||
};
|
|
||||||
|
|
||||||
// 欧非阈值
|
|
||||||
const FLOORS_MAP = {
|
|
||||||
邦布频段: [50, 70],
|
|
||||||
音擎频段: [50, 70],
|
|
||||||
独家频段: [60, 80],
|
|
||||||
常驻频段: [60, 80],
|
|
||||||
};
|
|
||||||
|
|
||||||
// 欧非标签
|
|
||||||
const HOMO_TAG = ['非到极致', '运气不好', '平稳保底', '小欧一把', '欧狗在此'];
|
|
||||||
|
|
||||||
// 欧非表情
|
|
||||||
const EMOJI = [
|
|
||||||
[4, 8, 13],
|
|
||||||
[1, 10, 5],
|
|
||||||
[16, 15, 2],
|
|
||||||
[12, 3, 9],
|
|
||||||
[6, 14, 7],
|
|
||||||
];
|
|
||||||
|
|
||||||
// 常驻名称
|
|
||||||
const NORMAL_LIST = [
|
|
||||||
'「11号」',
|
|
||||||
'猫又',
|
|
||||||
'莱卡恩',
|
|
||||||
'丽娜',
|
|
||||||
'格莉丝',
|
|
||||||
'珂蕾妲',
|
|
||||||
'拘缚者',
|
|
||||||
'燃狱齿轮',
|
|
||||||
'嵌合编译器',
|
|
||||||
'钢铁肉垫',
|
|
||||||
'硫磺石',
|
|
||||||
'啜泣摇篮',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取抽卡链接
|
|
||||||
* @param {string} authKey 米游社认证密钥
|
|
||||||
* @param {string} gachaType 祈愿类型(池子代码)
|
|
||||||
* @param {string} initLogGachaBaseType
|
|
||||||
* @param {number} page 页数
|
|
||||||
* @param {string} endId 最后一个数据的 id
|
|
||||||
* @param {string} size 页面数据大小
|
|
||||||
* @returns {Promise<string>} 抽卡链接
|
|
||||||
*/
|
|
||||||
export const getZZZGachaLink = async (
|
|
||||||
authKey,
|
|
||||||
gachaType = '2001',
|
|
||||||
initLogGachaBaseType = '2',
|
|
||||||
page = 1,
|
|
||||||
endId = '0',
|
|
||||||
size = '20'
|
|
||||||
) => {
|
|
||||||
// 暂时直接写死服务器为国服
|
|
||||||
const serverId = 'prod_gf_cn';
|
|
||||||
const url = ZZZ_GET_GACHA_LOG_API;
|
|
||||||
const timestamp = Math.floor(Date.now() / 1000);
|
|
||||||
// 请求参数
|
|
||||||
const params = new URLSearchParams({
|
|
||||||
authkey_ver: '1',
|
|
||||||
sign_type: '2',
|
|
||||||
auth_appid: 'webview_gacha',
|
|
||||||
init_log_gacha_type: gachaType,
|
|
||||||
init_log_gacha_base_type: initLogGachaBaseType,
|
|
||||||
gacha_id: '2c1f5692fdfbb733a08733f9eb69d32aed1d37',
|
|
||||||
timestamp: timestamp.toString(),
|
|
||||||
lang: 'zh-cn',
|
|
||||||
device_type: 'mobile',
|
|
||||||
plat_type: 'ios',
|
|
||||||
region: serverId,
|
|
||||||
authkey: authKey,
|
|
||||||
game_biz: 'nap_cn',
|
|
||||||
gacha_type: gachaType,
|
|
||||||
real_gacha_type: initLogGachaBaseType,
|
|
||||||
page: page,
|
|
||||||
size: size,
|
|
||||||
end_id: endId,
|
|
||||||
});
|
|
||||||
// 完整链接
|
|
||||||
return `${url}?${params}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通过米游社认证密钥获取抽卡记录
|
|
||||||
* @param {string} authKey 米游社认证密钥
|
|
||||||
* @param {string} gachaType 祈愿类型(池子代码)
|
|
||||||
* @param {string} initLogGachaBaseType
|
|
||||||
* @param {number} page 页数
|
|
||||||
* @param {string} endId 最后一个数据的 id
|
|
||||||
* @returns {Promise<ZZZGachaLogResp>} 抽卡记录
|
|
||||||
*/
|
|
||||||
export const getZZZGachaLogByAuthkey = async (
|
|
||||||
authKey,
|
|
||||||
gachaType = '2001',
|
|
||||||
initLogGachaBaseType = '2',
|
|
||||||
page = 1,
|
|
||||||
endId = '0'
|
|
||||||
) => {
|
|
||||||
// 获取抽卡链接
|
|
||||||
const link = await getZZZGachaLink(
|
|
||||||
authKey,
|
|
||||||
gachaType,
|
|
||||||
initLogGachaBaseType,
|
|
||||||
page,
|
|
||||||
endId
|
|
||||||
);
|
|
||||||
// 发送请求
|
|
||||||
const response = await request(link, {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
retry: 3,
|
|
||||||
});
|
|
||||||
// 获取数据
|
|
||||||
const data = await response.json();
|
|
||||||
if (!data || !data?.data) return null;
|
|
||||||
|
|
||||||
return new ZZZGachaLogResp(data.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新抽卡数据
|
* 更新抽卡数据
|
||||||
|
|
@ -219,26 +96,6 @@ export const updateGachaLog = async (authKey, uid) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 欧非分析
|
|
||||||
* @param {number} ast
|
|
||||||
* @param {number[]} lst
|
|
||||||
*/
|
|
||||||
const getLevelFromList = (ast, lst) => {
|
|
||||||
if (ast === 0) {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
let level = 0;
|
|
||||||
for (let numIndex = 0; numIndex < lst.length; numIndex++) {
|
|
||||||
if (ast <= lst[numIndex]) {
|
|
||||||
level = 4 - numIndex;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return level;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 抽卡分析
|
* 抽卡分析
|
||||||
* @param {string} uid ZZZUID
|
* @param {string} uid ZZZUID
|
||||||
|
|
|
||||||
49
lib/gacha/const.js
Normal file
49
lib/gacha/const.js
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
// 池子代码
|
||||||
|
export const gacha_type_meta_data = {
|
||||||
|
音擎频段: ['3001'],
|
||||||
|
独家频段: ['2001'],
|
||||||
|
常驻频段: ['1001'],
|
||||||
|
邦布频段: ['5001'],
|
||||||
|
};
|
||||||
|
|
||||||
|
// 欧非阈值
|
||||||
|
export const FLOORS_MAP = {
|
||||||
|
邦布频段: [50, 70],
|
||||||
|
音擎频段: [50, 70],
|
||||||
|
独家频段: [60, 80],
|
||||||
|
常驻频段: [60, 80],
|
||||||
|
};
|
||||||
|
|
||||||
|
// 欧非标签
|
||||||
|
export const HOMO_TAG = [
|
||||||
|
'非到极致',
|
||||||
|
'运气不好',
|
||||||
|
'平稳保底',
|
||||||
|
'小欧一把',
|
||||||
|
'欧狗在此',
|
||||||
|
];
|
||||||
|
|
||||||
|
// 欧非表情
|
||||||
|
export const EMOJI = [
|
||||||
|
[4, 8, 13],
|
||||||
|
[1, 10, 5],
|
||||||
|
[16, 15, 2],
|
||||||
|
[12, 3, 9],
|
||||||
|
[6, 14, 7],
|
||||||
|
];
|
||||||
|
|
||||||
|
// 常驻名称
|
||||||
|
export const NORMAL_LIST = [
|
||||||
|
'「11号」',
|
||||||
|
'猫又',
|
||||||
|
'莱卡恩',
|
||||||
|
'丽娜',
|
||||||
|
'格莉丝',
|
||||||
|
'珂蕾妲',
|
||||||
|
'拘缚者',
|
||||||
|
'燃狱齿轮',
|
||||||
|
'嵌合编译器',
|
||||||
|
'钢铁肉垫',
|
||||||
|
'硫磺石',
|
||||||
|
'啜泣摇篮',
|
||||||
|
];
|
||||||
89
lib/gacha/core.js
Normal file
89
lib/gacha/core.js
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
import { ZZZ_GET_GACHA_LOG_API } from '../mysapi/api.js';
|
||||||
|
import request from '../../utils/request.js';
|
||||||
|
import { ZZZGachaLogResp } from '../../model/gacha.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取抽卡链接
|
||||||
|
* @param {string} authKey 米游社认证密钥
|
||||||
|
* @param {string} gachaType 祈愿类型(池子代码)
|
||||||
|
* @param {string} initLogGachaBaseType
|
||||||
|
* @param {number} page 页数
|
||||||
|
* @param {string} endId 最后一个数据的 id
|
||||||
|
* @param {string} size 页面数据大小
|
||||||
|
* @returns {Promise<string>} 抽卡链接
|
||||||
|
*/
|
||||||
|
export const getZZZGachaLink = async (
|
||||||
|
authKey,
|
||||||
|
gachaType = '2001',
|
||||||
|
initLogGachaBaseType = '2',
|
||||||
|
page = 1,
|
||||||
|
endId = '0',
|
||||||
|
size = '20'
|
||||||
|
) => {
|
||||||
|
// 暂时直接写死服务器为国服
|
||||||
|
const serverId = 'prod_gf_cn';
|
||||||
|
const url = ZZZ_GET_GACHA_LOG_API;
|
||||||
|
const timestamp = Math.floor(Date.now() / 1000);
|
||||||
|
// 请求参数
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
authkey_ver: '1',
|
||||||
|
sign_type: '2',
|
||||||
|
auth_appid: 'webview_gacha',
|
||||||
|
init_log_gacha_type: gachaType,
|
||||||
|
init_log_gacha_base_type: initLogGachaBaseType,
|
||||||
|
gacha_id: '2c1f5692fdfbb733a08733f9eb69d32aed1d37',
|
||||||
|
timestamp: timestamp.toString(),
|
||||||
|
lang: 'zh-cn',
|
||||||
|
device_type: 'mobile',
|
||||||
|
plat_type: 'ios',
|
||||||
|
region: serverId,
|
||||||
|
authkey: authKey,
|
||||||
|
game_biz: 'nap_cn',
|
||||||
|
gacha_type: gachaType,
|
||||||
|
real_gacha_type: initLogGachaBaseType,
|
||||||
|
page: page,
|
||||||
|
size: size,
|
||||||
|
end_id: endId,
|
||||||
|
});
|
||||||
|
// 完整链接
|
||||||
|
return `${url}?${params}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过米游社认证密钥获取抽卡记录
|
||||||
|
* @param {string} authKey 米游社认证密钥
|
||||||
|
* @param {string} gachaType 祈愿类型(池子代码)
|
||||||
|
* @param {string} initLogGachaBaseType
|
||||||
|
* @param {number} page 页数
|
||||||
|
* @param {string} endId 最后一个数据的 id
|
||||||
|
* @returns {Promise<ZZZGachaLogResp>} 抽卡记录
|
||||||
|
*/
|
||||||
|
export const getZZZGachaLogByAuthkey = async (
|
||||||
|
authKey,
|
||||||
|
gachaType = '2001',
|
||||||
|
initLogGachaBaseType = '2',
|
||||||
|
page = 1,
|
||||||
|
endId = '0'
|
||||||
|
) => {
|
||||||
|
// 获取抽卡链接
|
||||||
|
const link = await getZZZGachaLink(
|
||||||
|
authKey,
|
||||||
|
gachaType,
|
||||||
|
initLogGachaBaseType,
|
||||||
|
page,
|
||||||
|
endId
|
||||||
|
);
|
||||||
|
// 发送请求
|
||||||
|
const response = await request(link, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
retry: 3,
|
||||||
|
});
|
||||||
|
// 获取数据
|
||||||
|
const data = await response.json();
|
||||||
|
if (!data || !data?.data) return null;
|
||||||
|
|
||||||
|
return new ZZZGachaLogResp(data.data);
|
||||||
|
};
|
||||||
19
lib/gacha/tool.js
Normal file
19
lib/gacha/tool.js
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
/**
|
||||||
|
* 欧非分析
|
||||||
|
* @param {number} ast
|
||||||
|
* @param {number[]} lst
|
||||||
|
*/
|
||||||
|
export const getLevelFromList = (ast, lst) => {
|
||||||
|
if (ast === 0) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
let level = 0;
|
||||||
|
for (let numIndex = 0; numIndex < lst.length; numIndex++) {
|
||||||
|
if (ast <= lst[numIndex]) {
|
||||||
|
level = 4 - numIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return level;
|
||||||
|
};
|
||||||
|
|
@ -4,6 +4,7 @@ import crypto from 'crypto';
|
||||||
import ZZZApiTool from './mysapi/tool.js';
|
import ZZZApiTool from './mysapi/tool.js';
|
||||||
import { randomString } from '../utils/data.js';
|
import { randomString } from '../utils/data.js';
|
||||||
import MysApi from '../../genshin/model/mys/mysApi.js';
|
import MysApi from '../../genshin/model/mys/mysApi.js';
|
||||||
|
import { MysError } from './error.js';
|
||||||
|
|
||||||
// const DEVICE_ID = randomString(32).toUpperCase()
|
// const DEVICE_ID = randomString(32).toUpperCase()
|
||||||
// const DEVICE_NAME = randomString(_.random(1, 10));
|
// const DEVICE_NAME = randomString(_.random(1, 10));
|
||||||
|
|
@ -18,6 +19,7 @@ export default class MysZZZApi extends MysApi {
|
||||||
this.uid = uid;
|
this.uid = uid;
|
||||||
this.server = this.getServer(uid);
|
this.server = this.getServer(uid);
|
||||||
this.apiTool = new ZZZApiTool(uid, this.server);
|
this.apiTool = new ZZZApiTool(uid, this.server);
|
||||||
|
this.handler = option?.handler || {};
|
||||||
if (typeof this.cookie !== 'string' && this.cookie) {
|
if (typeof this.cookie !== 'string' && this.cookie) {
|
||||||
const ck = Object.values(this.cookie).find(item => {
|
const ck = Object.values(this.cookie).find(item => {
|
||||||
return item.ck && item.uid === uid;
|
return item.ck && item.uid === uid;
|
||||||
|
|
@ -239,81 +241,32 @@ export default class MysZZZApi extends MysApi {
|
||||||
* @param data 查询请求的数据
|
* @param data 查询请求的数据
|
||||||
* @returns {Promise<*|boolean>}
|
* @returns {Promise<*|boolean>}
|
||||||
*/
|
*/
|
||||||
async checkCode(e, res, type, data = {}) {
|
async checkCode(res, type, data = {}) {
|
||||||
if (!res || !e) {
|
if (!res) {
|
||||||
e.reply('米游社接口请求失败,暂时无法查询');
|
throw new Error('米游社接口返回数据为空');
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
this.e = e;
|
|
||||||
this.e.isZZZ = true;
|
|
||||||
res.retcode = Number(res.retcode);
|
res.retcode = Number(res.retcode);
|
||||||
switch (res.retcode) {
|
const code = String(res.retcode);
|
||||||
case 0:
|
if (code === '1034' || code === '10035') {
|
||||||
break;
|
// 如果有注册的mys.req.err,调用
|
||||||
case 10102:
|
if (!!this?.handler && this?.handler?.has('mys.req.err')) {
|
||||||
if (res.message === 'Data is not public for the user') {
|
logger.mark(
|
||||||
this.e.reply(`\nUID:${this.uid},米游社数据未公开`, false, {
|
`[米游社绝区零查询失败][UID:${this.uid}][qq:${this.userId}] 遇到验证码,尝试调用 Handler mys.req.err`
|
||||||
at: this.userId,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.e.reply(`UID:${this.uid},请先去米游社绑定角色`);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 10041:
|
|
||||||
case 5003:
|
|
||||||
this.e.reply(
|
|
||||||
`UID:${this.uid},米游社账号异常,暂时无法查询,请发送 %绑定设备帮助 查看如何绑定设备`
|
|
||||||
);
|
);
|
||||||
break;
|
res =
|
||||||
case 10035:
|
(await this.handler.call('mys.req.err', this.e, {
|
||||||
case 1034: {
|
mysApi: this,
|
||||||
let handler = this.e.runtime?.handler || {};
|
type,
|
||||||
|
res,
|
||||||
// 如果有注册的mys.req.err,调用
|
data,
|
||||||
if (handler.has('mys.req.err')) {
|
mysInfo: this,
|
||||||
logger.mark(
|
})) || res;
|
||||||
`[米游社zzz查询失败][UID:${this.uid}][qq:${this.userId}] 遇到验证码,尝试调用 Handler mys.req.err`
|
|
||||||
);
|
|
||||||
res =
|
|
||||||
(await handler.call('mys.req.err', this.e, {
|
|
||||||
mysApi: this,
|
|
||||||
type,
|
|
||||||
res,
|
|
||||||
data,
|
|
||||||
mysInfo: this,
|
|
||||||
})) || res;
|
|
||||||
}
|
|
||||||
if (!res || res?.retcode === 1034 || res?.retcode === 10035) {
|
|
||||||
logger.mark(
|
|
||||||
`[米游社zzz查询失败][UID:${this.uid}][qq:${this.userId}] 遇到验证码`
|
|
||||||
);
|
|
||||||
this.e.reply('米游社zzz查询遇到验证码,请稍后再试');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
if (/(登录|login)/i.test(res.message)) {
|
|
||||||
logger.mark(`[ck失效][UID:${this.uid}]`);
|
|
||||||
this.e.reply(`UID:${this.uid},米游社cookie已失效`);
|
|
||||||
} else {
|
|
||||||
this.e.reply(
|
|
||||||
`米游社接口报错,暂时无法查询:${res.message || 'error'}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (res.retcode !== 0) {
|
if (code === '0') {
|
||||||
logger.mark(
|
return res;
|
||||||
`[米游社zzz接口报错]${JSON.stringify(res)},UID:${this.uid}`
|
|
||||||
);
|
|
||||||
throw new Error({
|
|
||||||
type: 'mysApi',
|
|
||||||
uid: this.uid,
|
|
||||||
retcode: res.retcode,
|
|
||||||
result: res,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return res;
|
throw new MysError(code, this.uid, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -323,8 +276,8 @@ export default class MysZZZApi extends MysApi {
|
||||||
* @param {{deviceFp?: string; query: Record<string, any>; headers: object;}} data
|
* @param {{deviceFp?: string; query: Record<string, any>; headers: object;}} data
|
||||||
* @param {boolean} cached
|
* @param {boolean} cached
|
||||||
*/
|
*/
|
||||||
async getFinalData(e, type, data = {}, cached = false) {
|
async getFinalData(type, data = {}, cached = false) {
|
||||||
if (!data.headers) data.headers = {};
|
if (!data?.headers) data.headers = {};
|
||||||
if (data.deviceFp) {
|
if (data.deviceFp) {
|
||||||
data.headers['x-rpc-device_fp'] = data.deviceFp;
|
data.headers['x-rpc-device_fp'] = data.deviceFp;
|
||||||
}
|
}
|
||||||
|
|
@ -364,7 +317,7 @@ export default class MysZZZApi extends MysApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const result = await this.getData(type, data, cached);
|
const result = await this.getData(type, data, cached);
|
||||||
const _data = await this.checkCode(e, result, type, {});
|
const _data = await this.checkCode(result, type, {});
|
||||||
if (!_data || _data.retcode !== 0) return false;
|
if (!_data || _data.retcode !== 0) return false;
|
||||||
return _data.data;
|
return _data.data;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,9 @@ export class ZZZPlugin extends plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建米游社 API 对象
|
// 创建米游社 API 对象
|
||||||
const api = new MysZZZApi(uid, ck);
|
const api = new MysZZZApi(uid, ck, {
|
||||||
|
handler: this.e?.runtime?.handler || {},
|
||||||
|
});
|
||||||
const currentCK = Object.values(ck).find(item => {
|
const currentCK = Object.values(ck).find(item => {
|
||||||
return item.ck && item.uid === uid;
|
return item.ck && item.uid === uid;
|
||||||
});
|
});
|
||||||
|
|
@ -175,7 +177,11 @@ export class ZZZPlugin extends plugin {
|
||||||
// 获取 米游社 API
|
// 获取 米游社 API
|
||||||
const { api, uid } = await this.getAPI();
|
const { api, uid } = await this.getAPI();
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
let userData = await api.getFinalData(this.e, 'zzzUser');
|
let userData = await api.getFinalData('zzzUser').catch(e => {
|
||||||
|
this.reply(e.message);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
|
||||||
if (!userData) throw new Error('获取用户数据失败');
|
if (!userData) throw new Error('获取用户数据失败');
|
||||||
// 取第一个用户信息
|
// 取第一个用户信息
|
||||||
userData =
|
userData =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue