mirror of
https://github.com/ZZZure/ZZZ-Plugin.git
synced 2025-12-16 21:27:47 +00:00
typo: 代码注释
This commit is contained in:
parent
51b3908afd
commit
4c90ca5354
12 changed files with 457 additions and 139 deletions
|
|
@ -1,10 +1,12 @@
|
||||||
import { findLowestLatencyUrl } from '../utils/network.js';
|
import { findLowestLatencyUrl } from '../utils/network.js';
|
||||||
|
|
||||||
|
// 保存上次找的节点
|
||||||
let lastFindFastestUrl = {
|
let lastFindFastestUrl = {
|
||||||
url: null,
|
url: null,
|
||||||
time: 0,
|
time: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 节点列表
|
||||||
const URL_LIB = {
|
const URL_LIB = {
|
||||||
'[JPFRP]': 'http://jp-3.lcf.1l1.icu:17217',
|
'[JPFRP]': 'http://jp-3.lcf.1l1.icu:17217',
|
||||||
'[HKFRP]': 'http://hk-1.lcf.1l1.icu:10200',
|
'[HKFRP]': 'http://hk-1.lcf.1l1.icu:10200',
|
||||||
|
|
@ -15,35 +17,46 @@ const URL_LIB = {
|
||||||
'[Singapore]': 'https://sg.qxqx.cf',
|
'[Singapore]': 'https://sg.qxqx.cf',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 文件类型路径
|
||||||
const TYPE_PATH = {
|
const TYPE_PATH = {
|
||||||
wiki: 'wiki',
|
wiki: 'wiki',
|
||||||
resource: 'resource',
|
resource: 'resource',
|
||||||
guide: 'guide',
|
guide: 'guide',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 资源路径
|
||||||
const RESOURCE_PATH = {
|
const RESOURCE_PATH = {
|
||||||
role: 'role',
|
role: 'role',
|
||||||
role_circle: 'role_circle',
|
role_circle: 'role_circle',
|
||||||
weapon: 'weapon',
|
weapon: 'weapon',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 图鉴路径
|
||||||
const GUIDE_PATH = {
|
const GUIDE_PATH = {
|
||||||
flower: 'flower',
|
flower: 'flower',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最快节点
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
export const getFatestUrl = async () => {
|
export const getFatestUrl = async () => {
|
||||||
if (
|
if (
|
||||||
lastFindFastestUrl.url &&
|
lastFindFastestUrl.url &&
|
||||||
Date.now() - lastFindFastestUrl.time < 1000 * 60 * 5
|
Date.now() - lastFindFastestUrl.time < 1000 * 60 * 5
|
||||||
) {
|
) {
|
||||||
|
// 如果上次找到的节点在 5 分钟内,直接返回
|
||||||
return lastFindFastestUrl.url;
|
return lastFindFastestUrl.url;
|
||||||
}
|
}
|
||||||
|
// 获取最快节点
|
||||||
const urls = Object.values(URL_LIB);
|
const urls = Object.values(URL_LIB);
|
||||||
const url = findLowestLatencyUrl(urls);
|
const url = findLowestLatencyUrl(urls);
|
||||||
|
// 保存节点
|
||||||
lastFindFastestUrl = {
|
lastFindFastestUrl = {
|
||||||
url,
|
url,
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
};
|
};
|
||||||
|
// 返回节点
|
||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,21 @@ try {
|
||||||
* @param {string} mysid 米游社ID
|
* @param {string} mysid 米游社ID
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function getStoken(e, mysid = '') {
|
export const getStoken = (e, mysid = '') => {
|
||||||
let userId = e.user_id;
|
// 获取QQ号
|
||||||
let user = new User(e);
|
const userId = e.user_id;
|
||||||
let file = `${user.stokenPath}${userId}.yaml`;
|
// 实例化用户
|
||||||
|
const user = new User(e);
|
||||||
|
// 获取 sk 文件路径
|
||||||
|
const filePath = `${user.stokenPath}${userId}.yaml`;
|
||||||
try {
|
try {
|
||||||
let cks = fs.readFileSync(file, 'utf-8');
|
// 读取文件
|
||||||
cks = YAML.parse(cks);
|
const file = fs.readFileSync(filePath, 'utf-8');
|
||||||
|
// 解析文件
|
||||||
|
const cks = YAML.parse(file);
|
||||||
for (const ck in cks) {
|
for (const ck in cks) {
|
||||||
if (cks[ck]['stuid'] === mysid) {
|
if (cks?.[ck]?.['stuid'] && cks[ck]['stuid'] === mysid) {
|
||||||
|
// 如果 ck 存在并且 stuid 与 mysid 相同则返回(这两者都是字符串,不需要类型转换,因此不需要使用弱比较,强比较速度更快,没有隐式转换一步骤,注意代码规范与代码风格)
|
||||||
return cks[ck];
|
return cks[ck];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -31,20 +37,26 @@ export function getStoken(e, mysid = '') {
|
||||||
logger.debug(`[zzz:error]getStoken`, error);
|
logger.debug(`[zzz:error]getStoken`, error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 此方法依赖逍遥插件
|
* 此方法依赖逍遥插件
|
||||||
* @returns {Promise<void>}
|
* @param {*} e yunzai Event
|
||||||
|
* @param {*} _user
|
||||||
|
* @param {string} zzzUid
|
||||||
|
* @param {string} authAppid
|
||||||
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
export async function getAuthKey(e, _user, zzzUid, authAppid = 'csc') {
|
export const getAuthKey = async (e, _user, zzzUid, authAppid = 'csc') => {
|
||||||
if (!User) {
|
if (!User) {
|
||||||
throw new Error('未安装逍遥插件,无法自动刷新抽卡链接');
|
throw new Error('未安装逍遥插件,无法自动刷新抽卡链接');
|
||||||
}
|
}
|
||||||
|
// 获取 UID 数据
|
||||||
const uidData = _user.getUidData(zzzUid, 'zzz', e);
|
const uidData = _user.getUidData(zzzUid, 'zzz', e);
|
||||||
if (!uidData || uidData?.type != 'ck' || !uidData?.ltuid) {
|
if (!uidData || uidData?.type != 'ck' || !uidData?.ltuid) {
|
||||||
throw new Error(`当前UID${zzzUid}未绑定cookie,请切换UID后尝试`);
|
throw new Error(`当前UID${zzzUid}未绑定cookie,请切换UID后尝试`);
|
||||||
}
|
}
|
||||||
|
// 获取 sk
|
||||||
let ck = getStoken(e, uidData.ltuid);
|
let ck = getStoken(e, uidData.ltuid);
|
||||||
if (!ck) {
|
if (!ck) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
@ -56,7 +68,9 @@ export async function getAuthKey(e, _user, zzzUid, authAppid = 'csc') {
|
||||||
`当前UID${zzzUid}查询所使用的米游社ID${ck.stuid}与当前切换的米游社ID${uidData.ltuid}不匹配,请切换UID后尝试`
|
`当前UID${zzzUid}查询所使用的米游社ID${ck.stuid}与当前切换的米游社ID${uidData.ltuid}不匹配,请切换UID后尝试`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// 拼接 sk
|
||||||
ck = `stuid=${ck.stuid};stoken=${ck.stoken};mid=${ck.mid};`;
|
ck = `stuid=${ck.stuid};stoken=${ck.stoken};mid=${ck.mid};`;
|
||||||
|
// 实例化 API
|
||||||
const api = new MysZZZApi(zzzUid, ck);
|
const api = new MysZZZApi(zzzUid, ck);
|
||||||
let type = 'zzzAuthKey';
|
let type = 'zzzAuthKey';
|
||||||
switch (authAppid) {
|
switch (authAppid) {
|
||||||
|
|
@ -66,14 +80,16 @@ export async function getAuthKey(e, _user, zzzUid, authAppid = 'csc') {
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
logger.mark(type);
|
// 获取链接
|
||||||
const { url, headers, body } = api.getUrl(type);
|
const { url, headers, body } = api.getUrl(type);
|
||||||
logger.mark(url);
|
// 发送请求
|
||||||
let res = await fetch(url, {
|
let res = await fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers,
|
headers,
|
||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
|
// 获取数据
|
||||||
res = await res.json();
|
res = await res.json();
|
||||||
|
// 返回 authkey
|
||||||
return res?.data?.authkey;
|
return res?.data?.authkey;
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,17 +11,20 @@ import { char } from './convert.js';
|
||||||
* @param {boolean} origin 是否返回原始数据
|
* @param {boolean} origin 是否返回原始数据
|
||||||
* @returns {Promise<ZZZAvatarBasic[] | null>}
|
* @returns {Promise<ZZZAvatarBasic[] | null>}
|
||||||
*/
|
*/
|
||||||
export async function getAvatarBasicList(e, api, deviceFp, origin = false) {
|
export const getAvatarBasicList = async (e, api, deviceFp, origin = false) => {
|
||||||
|
// 获取米游社角色列表
|
||||||
const avatarBaseListData = await api.getFinalData(e, 'zzzAvatarList', {
|
const avatarBaseListData = await api.getFinalData(e, 'zzzAvatarList', {
|
||||||
deviceFp,
|
deviceFp,
|
||||||
});
|
});
|
||||||
if (!avatarBaseListData) return null;
|
if (!avatarBaseListData) return null;
|
||||||
|
// 是否返回原始数据
|
||||||
if (origin) return avatarBaseListData.avatar_list;
|
if (origin) return avatarBaseListData.avatar_list;
|
||||||
|
// 格式化数据
|
||||||
const result = avatarBaseListData.avatar_list.map(
|
const result = avatarBaseListData.avatar_list.map(
|
||||||
item => new ZZZAvatarBasic(item)
|
item => new ZZZAvatarBasic(item)
|
||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取角色详细信息列表
|
* 获取角色详细信息列表
|
||||||
|
|
@ -31,9 +34,11 @@ export async function getAvatarBasicList(e, api, deviceFp, origin = false) {
|
||||||
* @param {string} deviceFp
|
* @param {string} deviceFp
|
||||||
* @param {boolean} origin 是否返回原始数据
|
* @param {boolean} origin 是否返回原始数据
|
||||||
*/
|
*/
|
||||||
export async function getAvatarInfoList(e, api, deviceFp, origin = false) {
|
export const getAvatarInfoList = async (e, api, deviceFp, origin = false) => {
|
||||||
|
// 获取角色基础信息列表
|
||||||
const avatarBaseList = await getAvatarBasicList(e, api, deviceFp, origin);
|
const avatarBaseList = await getAvatarBasicList(e, api, deviceFp, origin);
|
||||||
if (!avatarBaseList) return null;
|
if (!avatarBaseList) return null;
|
||||||
|
// 获取角色详细信息
|
||||||
const avatarInfoList = await api.getFinalData(e, 'zzzAvatarInfo', {
|
const avatarInfoList = await api.getFinalData(e, 'zzzAvatarInfo', {
|
||||||
deviceFp,
|
deviceFp,
|
||||||
query: {
|
query: {
|
||||||
|
|
@ -41,12 +46,14 @@ export async function getAvatarInfoList(e, api, deviceFp, origin = false) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!avatarInfoList) return null;
|
if (!avatarInfoList) return null;
|
||||||
|
// 是否返回原始数据
|
||||||
if (origin) return avatarInfoList.avatar_list;
|
if (origin) return avatarInfoList.avatar_list;
|
||||||
|
// 格式化数据
|
||||||
const result = avatarInfoList.avatar_list.map(
|
const result = avatarInfoList.avatar_list.map(
|
||||||
item => new ZZZAvatarInfo(item)
|
item => new ZZZAvatarInfo(item)
|
||||||
);
|
);
|
||||||
return result;
|
return result;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 刷新面板
|
* 刷新面板
|
||||||
|
|
@ -56,38 +63,47 @@ export async function getAvatarInfoList(e, api, deviceFp, origin = false) {
|
||||||
* @param {string} deviceFp
|
* @param {string} deviceFp
|
||||||
* @returns {Promise<ZZZAvatarInfo[] | null>}
|
* @returns {Promise<ZZZAvatarInfo[] | null>}
|
||||||
*/
|
*/
|
||||||
export async function refreshPanel(e, api, uid, deviceFp) {
|
export const refreshPanel = async (e, api, uid, deviceFp) => {
|
||||||
|
// 获取已保存数据
|
||||||
const originData = getPanelData(uid);
|
const originData = getPanelData(uid);
|
||||||
|
// 获取新数据
|
||||||
const newData = await getAvatarInfoList(e, api, deviceFp, true);
|
const newData = await getAvatarInfoList(e, api, deviceFp, true);
|
||||||
if (!newData) return null;
|
if (!newData) return null;
|
||||||
|
// 初始化最终数据
|
||||||
const finalData = [...newData];
|
const finalData = [...newData];
|
||||||
|
// 如果有已保存的数据
|
||||||
if (originData) {
|
if (originData) {
|
||||||
|
// 合并数据
|
||||||
for (const item of originData) {
|
for (const item of originData) {
|
||||||
if (!finalData.find(i => i.id === item.id)) {
|
if (!finalData.find(i => i.id === item.id)) {
|
||||||
|
// 将已保存的数据添加到最终数据中(放在后面)
|
||||||
finalData.push(item);
|
finalData.push(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 保存数据
|
||||||
savePanelData(uid, finalData);
|
savePanelData(uid, finalData);
|
||||||
|
// 格式化数据
|
||||||
finalData.forEach(item => {
|
finalData.forEach(item => {
|
||||||
item.isNew = newData.find(i => i.id === item.id);
|
item.isNew = newData.find(i => i.id === item.id);
|
||||||
});
|
});
|
||||||
const formattedData = finalData.map(item => new ZZZAvatarInfo(item));
|
const formattedData = finalData.map(item => new ZZZAvatarInfo(item));
|
||||||
for (const item of formattedData) {
|
for (const item of formattedData) {
|
||||||
|
// 下载图片资源
|
||||||
await item.get_basic_assets();
|
await item.get_basic_assets();
|
||||||
}
|
}
|
||||||
return formattedData;
|
return formattedData;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*获取面板数据
|
*获取面板数据
|
||||||
* @param {string} uid
|
* @param {string} uid
|
||||||
* @returns {ZZZAvatarInfo[]}
|
* @returns {ZZZAvatarInfo[]}
|
||||||
*/
|
*/
|
||||||
export function getPanelList(uid) {
|
export const getPanelList = uid => {
|
||||||
const data = getPanelData(uid);
|
const data = getPanelData(uid);
|
||||||
return data.map(item => new ZZZAvatarInfo(item));
|
return data.map(item => new ZZZAvatarInfo(item));
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取某个角色的面板数据
|
* 获取某个角色的面板数据
|
||||||
|
|
@ -95,12 +111,15 @@ export function getPanelList(uid) {
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @returns {ZZZAvatarInfo | null}
|
* @returns {ZZZAvatarInfo | null}
|
||||||
*/
|
*/
|
||||||
export function getPanel(uid, name) {
|
export const getPanel = (uid, name) => {
|
||||||
const _data = getPanelData(uid);
|
const _data = getPanelData(uid);
|
||||||
|
// 获取所有面板数据
|
||||||
const data = _data.map(item => new ZZZAvatarInfo(item));
|
const data = _data.map(item => new ZZZAvatarInfo(item));
|
||||||
|
// 通过名称(包括别名)获取角色 ID
|
||||||
const id = char.atlasToID(name);
|
const id = char.atlasToID(name);
|
||||||
if (!id) return null;
|
if (!id) return null;
|
||||||
|
// 通过 ID 获取角色数据
|
||||||
const result = data.find(item => item.id === id);
|
const result = data.find(item => item.id === id);
|
||||||
if (!result) return null;
|
if (!result) return null;
|
||||||
return result;
|
return result;
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,18 @@ import { getStoken } from './authkey.js';
|
||||||
|
|
||||||
export const rulePrefix = '^((#|\\%)?(zzz|ZZZ|绝区零))';
|
export const rulePrefix = '^((#|\\%)?(zzz|ZZZ|绝区零))';
|
||||||
|
|
||||||
export async function getCk(e, s = false) {
|
/**
|
||||||
|
* 获取米游社用户的 cookie
|
||||||
|
* @param {Object} e yunzai事件
|
||||||
|
* @param {boolean} s 是否获取 stoken
|
||||||
|
* @returns {Promise<Object>}
|
||||||
|
*/
|
||||||
|
export const getCk = async (e, s = false) => {
|
||||||
e.isSr = true;
|
e.isSr = true;
|
||||||
let stoken = '';
|
let stoken = '';
|
||||||
let user = new User(e);
|
const user = new User(e);
|
||||||
if (s) {
|
if (s) {
|
||||||
stoken = await getStoken(e);
|
stoken = getStoken(e);
|
||||||
}
|
}
|
||||||
if (typeof user.getCk === 'function') {
|
if (typeof user.getCk === 'function') {
|
||||||
let ck = user.getCk();
|
let ck = user.getCk();
|
||||||
|
|
@ -19,7 +25,7 @@ export async function getCk(e, s = false) {
|
||||||
});
|
});
|
||||||
return ck;
|
return ck;
|
||||||
}
|
}
|
||||||
let mysUser = (await user.user()).getMysUser('zzz');
|
const mysUser = (await user.user()).getMysUser('zzz');
|
||||||
let ck;
|
let ck;
|
||||||
if (mysUser) {
|
if (mysUser) {
|
||||||
ck = {
|
ck = {
|
||||||
|
|
@ -33,4 +39,4 @@ export async function getCk(e, s = false) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return ck;
|
return ck;
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -25,19 +25,24 @@ const ZZZ_SUIT_PATH = path.join(imageResourcesPath, 'suit');
|
||||||
*/
|
*/
|
||||||
const downloadFile = async (url, savePath) => {
|
const downloadFile = async (url, savePath) => {
|
||||||
const _download = async (url, savePath, retry = 0) => {
|
const _download = async (url, savePath, retry = 0) => {
|
||||||
|
// 重试次数超过 5 次则返回 null
|
||||||
if (retry > 5) {
|
if (retry > 5) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
// 下载文件
|
||||||
try {
|
try {
|
||||||
const download = await fetch(url);
|
const download = await fetch(url);
|
||||||
const arrayBuffer = await download.arrayBuffer();
|
const arrayBuffer = await download.arrayBuffer();
|
||||||
const buffer = Buffer.from(arrayBuffer);
|
const buffer = Buffer.from(arrayBuffer);
|
||||||
|
// 保存文件
|
||||||
if (!fs.existsSync(path.dirname(savePath))) {
|
if (!fs.existsSync(path.dirname(savePath))) {
|
||||||
fs.mkdirSync(path.dirname(savePath), { recursive: true });
|
fs.mkdirSync(path.dirname(savePath), { recursive: true });
|
||||||
}
|
}
|
||||||
fs.writeFileSync(savePath, buffer);
|
fs.writeFileSync(savePath, buffer);
|
||||||
|
// 返回保存路径
|
||||||
return savePath;
|
return savePath;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// 下载失败,重试
|
||||||
return await _download(url, savePath, retry + 1);
|
return await _download(url, savePath, retry + 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -97,7 +102,7 @@ export const getWeaponImage = async id => {
|
||||||
*/
|
*/
|
||||||
export const getRoleImage = async id => {
|
export const getRoleImage = async id => {
|
||||||
const sprite = char.IDToCharSprite(id);
|
const sprite = char.IDToCharSprite(id);
|
||||||
if (sprite === null) 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 rolePath = path.join(ZZZ_ROLE_PATH, filename);
|
||||||
if (fs.existsSync(rolePath)) return rolePath;
|
if (fs.existsSync(rolePath)) return rolePath;
|
||||||
|
|
@ -114,7 +119,7 @@ export const getRoleImage = async id => {
|
||||||
*/
|
*/
|
||||||
export const getRoleCircleImage = async id => {
|
export const getRoleCircleImage = async id => {
|
||||||
const sprite = char.IDToCharSprite(id);
|
const sprite = char.IDToCharSprite(id);
|
||||||
if (sprite === null) 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 roleCirclePath = path.join(ZZZ_ROLE_CIRCLE_PATH, filename);
|
||||||
if (fs.existsSync(roleCirclePath)) return roleCirclePath;
|
if (fs.existsSync(roleCirclePath)) return roleCirclePath;
|
||||||
|
|
|
||||||
210
lib/gacha.js
210
lib/gacha.js
|
|
@ -4,6 +4,7 @@ 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 { ZZZ_GET_GACHA_LOG_API } from './mysapi/api.js';
|
||||||
|
|
||||||
|
// 池子代码
|
||||||
export const gacha_type_meta_data = {
|
export const gacha_type_meta_data = {
|
||||||
音擎频段: ['3001'],
|
音擎频段: ['3001'],
|
||||||
独家频段: ['2001'],
|
独家频段: ['2001'],
|
||||||
|
|
@ -11,13 +12,50 @@ export const gacha_type_meta_data = {
|
||||||
邦布频段: ['5001'],
|
邦布频段: ['5001'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 欧非阈值
|
||||||
|
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} authKey 米游社认证密钥
|
||||||
|
* @param {string} gachaType 祈愿类型(池子代码)
|
||||||
* @param {string} initLogGachaBaseType
|
* @param {string} initLogGachaBaseType
|
||||||
* @param {number} page
|
* @param {number} page 页数
|
||||||
* @param {string} endId
|
* @param {string} endId 最后一个数据的 id
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>} 抽卡链接
|
||||||
*/
|
*/
|
||||||
export const getZZZGachaLink = async (
|
export const getZZZGachaLink = async (
|
||||||
authKey,
|
authKey,
|
||||||
|
|
@ -26,10 +64,11 @@ export const getZZZGachaLink = async (
|
||||||
page = 1,
|
page = 1,
|
||||||
endId = '0'
|
endId = '0'
|
||||||
) => {
|
) => {
|
||||||
|
// 暂时直接写死服务器为国服
|
||||||
const serverId = 'prod_gf_cn';
|
const serverId = 'prod_gf_cn';
|
||||||
const url = ZZZ_GET_GACHA_LOG_API;
|
const url = ZZZ_GET_GACHA_LOG_API;
|
||||||
const timestamp = Math.floor(Date.now() / 1000);
|
const timestamp = Math.floor(Date.now() / 1000);
|
||||||
|
// 请求参数
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
authkey_ver: '1',
|
authkey_ver: '1',
|
||||||
sign_type: '2',
|
sign_type: '2',
|
||||||
|
|
@ -50,26 +89,27 @@ export const getZZZGachaLink = async (
|
||||||
size: '20',
|
size: '20',
|
||||||
end_id: endId,
|
end_id: endId,
|
||||||
});
|
});
|
||||||
|
// 完整链接
|
||||||
return `${url}?${params}`;
|
return `${url}?${params}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* 通过米游社认证密钥获取抽卡记录
|
||||||
* @param {string} authKey
|
* @param {string} authKey 米游社认证密钥
|
||||||
* @param {*} gachaType
|
* @param {string} gachaType 祈愿类型(池子代码)
|
||||||
* @param {*} initLogGachaBaseType
|
* @param {string} initLogGachaBaseType
|
||||||
* @param {number} page
|
* @param {number} page 页数
|
||||||
* @param {string} endId
|
* @param {string} endId 最后一个数据的 id
|
||||||
* @returns {Promise<ZZZGachaLogResp>}
|
* @returns {Promise<ZZZGachaLogResp>} 抽卡记录
|
||||||
*/
|
*/
|
||||||
export async function getZZZGachaLogByAuthkey(
|
export const getZZZGachaLogByAuthkey = async (
|
||||||
authKey,
|
authKey,
|
||||||
gachaType = '2001',
|
gachaType = '2001',
|
||||||
initLogGachaBaseType = '2',
|
initLogGachaBaseType = '2',
|
||||||
page = 1,
|
page = 1,
|
||||||
endId = '0'
|
endId = '0'
|
||||||
) {
|
) => {
|
||||||
|
// 获取抽卡链接
|
||||||
const link = await getZZZGachaLink(
|
const link = await getZZZGachaLink(
|
||||||
authKey,
|
authKey,
|
||||||
gachaType,
|
gachaType,
|
||||||
|
|
@ -77,25 +117,25 @@ export async function getZZZGachaLogByAuthkey(
|
||||||
page,
|
page,
|
||||||
endId
|
endId
|
||||||
);
|
);
|
||||||
|
// 发送请求
|
||||||
const response = await fetch(link, {
|
const response = await fetch(link, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
// 获取数据
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (!data || !data?.data) return null;
|
if (!data || !data?.data) return null;
|
||||||
|
|
||||||
return new ZZZGachaLogResp(data.data);
|
return new ZZZGachaLogResp(data.data);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* 更新抽卡数据
|
||||||
* @param {string} authKey
|
* @param {string} authKey 米游社认证密钥
|
||||||
* @param {string} uid
|
* @param {string} uid ZZZUID
|
||||||
* @returns {Promise<{
|
* @returns {Promise<{
|
||||||
* data: {
|
* data: {
|
||||||
* [x: string]: SingleGachaLog[];
|
* [x: string]: SingleGachaLog[];
|
||||||
|
|
@ -103,26 +143,39 @@ export async function getZZZGachaLogByAuthkey(
|
||||||
* count: {
|
* count: {
|
||||||
* [x: string]: number;
|
* [x: string]: number;
|
||||||
* }
|
* }
|
||||||
* }>}
|
* }>} 更新后的抽卡数据
|
||||||
*/
|
*/
|
||||||
export async function updateGachaLog(authKey, uid) {
|
export const updateGachaLog = async (authKey, uid) => {
|
||||||
|
// 获取之前的抽卡数据
|
||||||
let previousLog = getGachaLog(uid);
|
let previousLog = getGachaLog(uid);
|
||||||
if (!previousLog) {
|
if (!previousLog) {
|
||||||
|
// 如果没有数据,初始化为空对象
|
||||||
previousLog = {};
|
previousLog = {};
|
||||||
}
|
}
|
||||||
|
// 新的抽卡数据
|
||||||
let newCount = {};
|
let newCount = {};
|
||||||
|
// 遍历所有池子
|
||||||
for (const name in gacha_type_meta_data) {
|
for (const name in gacha_type_meta_data) {
|
||||||
if (!previousLog[name]) {
|
if (!previousLog[name]) {
|
||||||
|
// 如果没有数据,初始化为空数组
|
||||||
previousLog[name] = [];
|
previousLog[name] = [];
|
||||||
}
|
}
|
||||||
|
// 初始化新数据计数(当前池子)
|
||||||
newCount[name] = 0;
|
newCount[name] = 0;
|
||||||
|
// 转换为 SingleGachaLog 对象
|
||||||
previousLog[name] = previousLog[name].map(i => new SingleGachaLog(i));
|
previousLog[name] = previousLog[name].map(i => new SingleGachaLog(i));
|
||||||
|
// 获取上一次保存的数据
|
||||||
const lastSaved = previousLog[name]?.[0];
|
const lastSaved = previousLog[name]?.[0];
|
||||||
|
// 初始化页数和最后一个数据的 id
|
||||||
let page = 1;
|
let page = 1;
|
||||||
let endId = '0';
|
let endId = '0';
|
||||||
|
// 新数据
|
||||||
const newData = [];
|
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(
|
||||||
authKey,
|
authKey,
|
||||||
type,
|
type,
|
||||||
|
|
@ -133,58 +186,42 @@ export async function updateGachaLog(authKey, uid) {
|
||||||
if (!log || !log?.list || log?.list?.length === 0) {
|
if (!log || !log?.list || log?.list?.length === 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// 遍历数据 (从最新的开始)
|
||||||
for (const item of log.list) {
|
for (const item of log.list) {
|
||||||
if (lastSaved && lastSaved.equals(item)) {
|
if (lastSaved && lastSaved.equals(item)) {
|
||||||
|
// 如果数据相同,说明已经获取完毕
|
||||||
break queryLabel;
|
break queryLabel;
|
||||||
}
|
}
|
||||||
|
// 添加到新数据中
|
||||||
newData.push(item);
|
newData.push(item);
|
||||||
|
// 当前池子新数据计数加一
|
||||||
newCount[name]++;
|
newCount[name]++;
|
||||||
}
|
}
|
||||||
|
// 更新页数和最后一个数据的 id
|
||||||
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]];
|
previousLog[name] = [...newData, ...previousLog[name]];
|
||||||
}
|
}
|
||||||
|
// 保存数据到文件
|
||||||
saveGachaLog(uid, previousLog);
|
saveGachaLog(uid, previousLog);
|
||||||
|
// 返回数据
|
||||||
return {
|
return {
|
||||||
data: previousLog,
|
data: previousLog,
|
||||||
count: newCount,
|
count: newCount,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const HOMO_TAG = ['非到极致', '运气不好', '平稳保底', '小欧一把', '欧狗在此'];
|
|
||||||
const EMOJI = [
|
|
||||||
[4, 8, 13],
|
|
||||||
[1, 10, 5],
|
|
||||||
[16, 15, 2],
|
|
||||||
[12, 3, 9],
|
|
||||||
[6, 14, 7],
|
|
||||||
];
|
|
||||||
const NORMAL_LIST = [
|
|
||||||
'「11号」',
|
|
||||||
'猫又',
|
|
||||||
'莱卡恩',
|
|
||||||
'丽娜',
|
|
||||||
'格莉丝',
|
|
||||||
'珂蕾妲',
|
|
||||||
'拘缚者',
|
|
||||||
'燃狱齿轮',
|
|
||||||
'嵌合编译器',
|
|
||||||
'钢铁肉垫',
|
|
||||||
'硫磺石',
|
|
||||||
'啜泣摇篮',
|
|
||||||
];
|
|
||||||
|
|
||||||
const FLOORS_MAP = {
|
|
||||||
邦布频段: [50, 70],
|
|
||||||
音擎频段: [50, 70],
|
|
||||||
独家频段: [60, 80],
|
|
||||||
常驻频段: [60, 80],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function getLevelFromList(ast, lst) {
|
/**
|
||||||
|
* 欧非分析
|
||||||
|
* @param {number} ast
|
||||||
|
* @param {number[]} lst
|
||||||
|
*/
|
||||||
|
const getLevelFromList = (ast, lst) => {
|
||||||
if (ast === 0) {
|
if (ast === 0) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
@ -197,40 +234,77 @@ function getLevelFromList(ast, lst) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return level;
|
return level;
|
||||||
}
|
};
|
||||||
|
|
||||||
export async function anaylizeGachaLog(uid) {
|
/**
|
||||||
|
* 抽卡分析
|
||||||
|
* @param {string} uid ZZZUID
|
||||||
|
* @returns {Promise<{
|
||||||
|
* name: string;
|
||||||
|
* timeRange: string;
|
||||||
|
* list: SingleGachaLog[];
|
||||||
|
* lastFive: number | string;
|
||||||
|
* fiveStars: number;
|
||||||
|
* upCount: number;
|
||||||
|
* totalCount: number;
|
||||||
|
* avgFive: string;
|
||||||
|
* avgUp: string;
|
||||||
|
* level: number;
|
||||||
|
* tag: string;
|
||||||
|
* emoji: number;
|
||||||
|
* }[]>} 分析结果
|
||||||
|
*/
|
||||||
|
export const anaylizeGachaLog = async uid => {
|
||||||
|
// 读取已保存的数据
|
||||||
const savedData = getGachaLog(uid);
|
const savedData = getGachaLog(uid);
|
||||||
if (!savedData) {
|
if (!savedData) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
// 初始化结果
|
||||||
const result = [];
|
const result = [];
|
||||||
|
// 遍历所有池子
|
||||||
for (const name in savedData) {
|
for (const name in savedData) {
|
||||||
|
// 转换为 SingleGachaLog 对象
|
||||||
const data = savedData[name].map(item => new SingleGachaLog(item));
|
const data = savedData[name].map(item => new SingleGachaLog(item));
|
||||||
|
// 获取最早和最晚的数据
|
||||||
const earliest = data[data.length - 1];
|
const earliest = data[data.length - 1];
|
||||||
const latest = data[0];
|
const latest = data[0];
|
||||||
|
// 初始化五星列表
|
||||||
const list = [];
|
const list = [];
|
||||||
|
// 初始化最后五星位置
|
||||||
let lastFive = null;
|
let lastFive = null;
|
||||||
|
// 初始化前一个索引
|
||||||
let preIndex = 0;
|
let preIndex = 0;
|
||||||
|
// 遍历数据
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (const item of data) {
|
for (const item of data) {
|
||||||
|
// 是否为UP
|
||||||
let isUp = true;
|
let isUp = true;
|
||||||
|
// 如果是五星
|
||||||
if (item.rank_type === '4') {
|
if (item.rank_type === '4') {
|
||||||
|
// 下载图片资源
|
||||||
await item.get_assets();
|
await item.get_assets();
|
||||||
|
// 判断是否为常驻
|
||||||
if (NORMAL_LIST.includes(item.name)) {
|
if (NORMAL_LIST.includes(item.name)) {
|
||||||
isUp = false;
|
isUp = false;
|
||||||
}
|
}
|
||||||
|
// 如果是第一个五星
|
||||||
if (lastFive === null) {
|
if (lastFive === null) {
|
||||||
|
// 记录位置
|
||||||
lastFive = i;
|
lastFive = i;
|
||||||
}
|
}
|
||||||
|
// 如果不是第一个五星
|
||||||
if (list.length > 0) {
|
if (list.length > 0) {
|
||||||
|
// 计算前一个五星到当前五星的次数(即前一个五星出卡所用的抽数)
|
||||||
list[list.length - 1]['totalCount'] = i - preIndex;
|
list[list.length - 1]['totalCount'] = i - preIndex;
|
||||||
|
// 根据次数设置颜色
|
||||||
if (i - preIndex <= FLOORS_MAP[name][0]) {
|
if (i - preIndex <= FLOORS_MAP[name][0]) {
|
||||||
list[list.length - 1]['color'] = 'rgb(63, 255, 0)';
|
list[list.length - 1]['color'] = 'rgb(63, 255, 0)';
|
||||||
} else if (i - preIndex >= FLOORS_MAP[name][1]) {
|
} else if (i - preIndex >= FLOORS_MAP[name][1]) {
|
||||||
list[list.length - 1]['color'] = 'rgb(255, 20, 20)';
|
list[list.length - 1]['color'] = 'rgb(255, 20, 20)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 添加到列表中
|
||||||
list.push({
|
list.push({
|
||||||
...item,
|
...item,
|
||||||
rank_type_label: rank.getRankChar(item.rank_type),
|
rank_type_label: rank.getRankChar(item.rank_type),
|
||||||
|
|
@ -240,8 +314,11 @@ export async function anaylizeGachaLog(uid) {
|
||||||
});
|
});
|
||||||
preIndex = i;
|
preIndex = i;
|
||||||
}
|
}
|
||||||
|
// 如果是最后一个数据
|
||||||
if (i === data.length - 1 && list.length > 0) {
|
if (i === data.length - 1 && list.length > 0) {
|
||||||
|
// 计算前一个五星到当前五星的次数(即前一个五星出卡所用的抽数)
|
||||||
list[list.length - 1]['totalCount'] = i - preIndex + 1;
|
list[list.length - 1]['totalCount'] = i - preIndex + 1;
|
||||||
|
// 根据次数设置颜色
|
||||||
if (i - preIndex + 1 <= FLOORS_MAP[name][0]) {
|
if (i - preIndex + 1 <= FLOORS_MAP[name][0]) {
|
||||||
list[list.length - 1]['color'] = 'rgb(63, 255, 0)';
|
list[list.length - 1]['color'] = 'rgb(63, 255, 0)';
|
||||||
} else if (i - preIndex + 1 >= FLOORS_MAP[name][1]) {
|
} else if (i - preIndex + 1 >= FLOORS_MAP[name][1]) {
|
||||||
|
|
@ -250,23 +327,37 @@ export async function anaylizeGachaLog(uid) {
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
// 计算五星数量和UP数量
|
||||||
const upCount = list.filter(i => i.isUp).length;
|
const upCount = list.filter(i => i.isUp).length;
|
||||||
|
// 计算总次数
|
||||||
const totalCount = data.length;
|
const totalCount = data.length;
|
||||||
|
// 计算五星数量
|
||||||
const fiveStars = list.length;
|
const fiveStars = list.length;
|
||||||
|
// 初始化时间范围
|
||||||
let timeRange = '还没有抽卡';
|
let timeRange = '还没有抽卡';
|
||||||
|
// 初始化平均五星次数
|
||||||
let avgFive = '-';
|
let avgFive = '-';
|
||||||
|
// 初始化平均UP次数
|
||||||
let avgUp = '-';
|
let avgUp = '-';
|
||||||
|
// 初始化欧非等级
|
||||||
let level = 2;
|
let level = 2;
|
||||||
|
// 如果有数据
|
||||||
if (data.length > 0) {
|
if (data.length > 0) {
|
||||||
|
// 设置时间范围
|
||||||
timeRange = `${latest.time} ~ ${earliest.time}`;
|
timeRange = `${latest.time} ~ ${earliest.time}`;
|
||||||
|
// 计算平均五星次数
|
||||||
if (fiveStars > 0)
|
if (fiveStars > 0)
|
||||||
avgFive = ((totalCount - lastFive) / fiveStars).toFixed(1);
|
avgFive = ((totalCount - lastFive) / fiveStars).toFixed(1);
|
||||||
|
// 计算平均UP次数
|
||||||
if (upCount > 0) avgUp = ((totalCount - lastFive) / upCount).toFixed(1);
|
if (upCount > 0) avgUp = ((totalCount - lastFive) / upCount).toFixed(1);
|
||||||
}
|
}
|
||||||
|
// 如果没有最后五星
|
||||||
if (!lastFive && fiveStars === 0) {
|
if (!lastFive && fiveStars === 0) {
|
||||||
|
// 设置最后五星为 '-'
|
||||||
if (totalCount > 0) lastFive = totalCount;
|
if (totalCount > 0) lastFive = totalCount;
|
||||||
else lastFive = '-';
|
else lastFive = '-';
|
||||||
}
|
}
|
||||||
|
// 根据不同池子计算欧非等级
|
||||||
if (avgUp !== '-') {
|
if (avgUp !== '-') {
|
||||||
if ('音擎频段' === name) {
|
if ('音擎频段' === name) {
|
||||||
level = getLevelFromList(avgUp, [62, 75, 88, 99, 111]);
|
level = getLevelFromList(avgUp, [62, 75, 88, 99, 111]);
|
||||||
|
|
@ -281,10 +372,13 @@ export async function anaylizeGachaLog(uid) {
|
||||||
level = getLevelFromList(avgFive, [53, 60, 68, 73, 75]);
|
level = getLevelFromList(avgFive, [53, 60, 68, 73, 75]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 设置欧非标签
|
||||||
const tag = HOMO_TAG[level];
|
const tag = HOMO_TAG[level];
|
||||||
|
// 设置欧非表情
|
||||||
const emojis = EMOJI[level];
|
const emojis = EMOJI[level];
|
||||||
// 随机选取一个
|
// 随机选取一个
|
||||||
const emoji = emojis[Math.floor(Math.random() * emojis.length)];
|
const emoji = emojis[Math.floor(Math.random() * emojis.length)];
|
||||||
|
// 写入数据
|
||||||
result.push({
|
result.push({
|
||||||
name,
|
name,
|
||||||
timeRange,
|
timeRange,
|
||||||
|
|
@ -301,4 +395,4 @@ export async function anaylizeGachaLog(uid) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@ import md5 from 'md5';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import ZZZApiTool from './mysapi/tool.js';
|
import ZZZApiTool from './mysapi/tool.js';
|
||||||
|
import { randomString } from '../utils/data.js';
|
||||||
import MysApi from '../../genshin/model/mys/mysApi.js';
|
import MysApi from '../../genshin/model/mys/mysApi.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));
|
||||||
|
|
||||||
const game_region = [
|
const game_region = [
|
||||||
'prod_gf_cn',
|
'prod_gf_cn',
|
||||||
'prod_gf_us',
|
'prod_gf_us',
|
||||||
|
|
@ -13,9 +15,14 @@ const game_region = [
|
||||||
'prod_gf_jp',
|
'prod_gf_jp',
|
||||||
'prod_gf_sg',
|
'prod_gf_sg',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 米游社ZZZAPI(继承自MysApi)
|
||||||
|
*/
|
||||||
export default class MysZZZApi extends MysApi {
|
export default class MysZZZApi extends MysApi {
|
||||||
constructor(uid, cookie, option = {}) {
|
constructor(uid, cookie, option = {}) {
|
||||||
super(uid, cookie, option, true);
|
super(uid, cookie, option, true);
|
||||||
|
// 初始化 uid、server、apiTool
|
||||||
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);
|
||||||
|
|
@ -30,6 +37,10 @@ export default class MysZZZApi extends MysApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取服务器
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
getServer() {
|
getServer() {
|
||||||
const _uid = this.uid?.toString();
|
const _uid = this.uid?.toString();
|
||||||
if (_uid.length < 10) {
|
if (_uid.length < 10) {
|
||||||
|
|
@ -47,10 +58,19 @@ export default class MysZZZApi extends MysApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求网址
|
||||||
|
* @param {string} type
|
||||||
|
* @param {object} data
|
||||||
|
* @returns {object|boolean}
|
||||||
|
*/
|
||||||
getUrl(type, data = {}) {
|
getUrl(type, data = {}) {
|
||||||
|
// 设置设备ID
|
||||||
data.deviceId = this._device;
|
data.deviceId = this._device;
|
||||||
let urlMap = this.apiTool.getUrlMap(data);
|
// 获取请求地址
|
||||||
|
const urlMap = this.apiTool.getUrlMap(data);
|
||||||
if (!urlMap[type]) return false;
|
if (!urlMap[type]) return false;
|
||||||
|
// 获取请求参数(即APITool中默认的请求参数,此参数理应是不可获取的,详细请参照 lib/mysapi/tool.js`)
|
||||||
let {
|
let {
|
||||||
url,
|
url,
|
||||||
query = '',
|
query = '',
|
||||||
|
|
@ -58,7 +78,9 @@ export default class MysZZZApi extends MysApi {
|
||||||
noDs = false,
|
noDs = false,
|
||||||
dsSalt = '',
|
dsSalt = '',
|
||||||
} = urlMap[type];
|
} = urlMap[type];
|
||||||
|
// 如果有query,拼接到url上
|
||||||
if (query) url += `?${query}`;
|
if (query) url += `?${query}`;
|
||||||
|
// 如果传入了 query 参数,将 query 参数拼接到 url 上
|
||||||
if (data.query) {
|
if (data.query) {
|
||||||
let str = '';
|
let str = '';
|
||||||
for (let key in data.query) {
|
for (let key in data.query) {
|
||||||
|
|
@ -77,19 +99,22 @@ export default class MysZZZApi extends MysApi {
|
||||||
url += `?${str}`;
|
url += `?${str}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 写入 body
|
||||||
if (body) body = JSON.stringify(body);
|
if (body) body = JSON.stringify(body);
|
||||||
|
// 获取请求头
|
||||||
let headers = this.getHeaders(query, body);
|
let headers = this.getHeaders(query, body);
|
||||||
if (data.deviceFp) {
|
if (data.deviceFp) {
|
||||||
headers['x-rpc-device_fp'] = data.deviceFp;
|
headers['x-rpc-device_fp'] = data.deviceFp;
|
||||||
// 兼容喵崽
|
// 兼容喵崽
|
||||||
this._device_fp = { data: { device_fp: data.deviceFp } };
|
this._device_fp = { data: { device_fp: data.deviceFp } };
|
||||||
}
|
}
|
||||||
|
// 写入 cookie
|
||||||
headers.cookie = this.cookie;
|
headers.cookie = this.cookie;
|
||||||
|
// 写入设备ID
|
||||||
if (this._device) {
|
if (this._device) {
|
||||||
headers['x-rpc-device_id'] = this._device;
|
headers['x-rpc-device_id'] = this._device;
|
||||||
}
|
}
|
||||||
|
// 写入DS
|
||||||
switch (dsSalt) {
|
switch (dsSalt) {
|
||||||
case 'web': {
|
case 'web': {
|
||||||
headers.DS = this.getDS2();
|
headers.DS = this.getDS2();
|
||||||
|
|
@ -106,6 +131,7 @@ export default class MysZZZApi extends MysApi {
|
||||||
} else {
|
} else {
|
||||||
headers.DS = this.getDs(query, body);
|
headers.DS = this.getDs(query, body);
|
||||||
}
|
}
|
||||||
|
// 如果不需要 DS,删除 DS
|
||||||
if (noDs) {
|
if (noDs) {
|
||||||
delete headers.DS;
|
delete headers.DS;
|
||||||
if (this._device) {
|
if (this._device) {
|
||||||
|
|
@ -115,9 +141,16 @@ export default class MysZZZApi extends MysApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.debug(`[mysapi]请求url:${url}`);
|
logger.debug(`[mysapi]请求url:${url}`);
|
||||||
|
// 返回请求参数
|
||||||
return { url, headers, body };
|
return { url, headers, body };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取DS
|
||||||
|
* @param {string} q
|
||||||
|
* @param {string} b
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
getDs(q = '', b = '') {
|
getDs(q = '', b = '') {
|
||||||
let n = '';
|
let n = '';
|
||||||
if (['prod_gf_cn'].includes(this.server)) {
|
if (['prod_gf_cn'].includes(this.server)) {
|
||||||
|
|
@ -125,19 +158,29 @@ export default class MysZZZApi extends MysApi {
|
||||||
} else {
|
} else {
|
||||||
n = 'okr4obncj8bw5a65hbnn5oo6ixjc3l9w';
|
n = 'okr4obncj8bw5a65hbnn5oo6ixjc3l9w';
|
||||||
}
|
}
|
||||||
let t = Math.round(new Date().getTime() / 1000);
|
const t = Math.round(new Date().getTime() / 1000);
|
||||||
let r = Math.floor(Math.random() * 900000 + 100000);
|
const r = Math.floor(Math.random() * 900000 + 100000);
|
||||||
let DS = md5(`salt=${n}&t=${t}&r=${r}&b=${b}&q=${q}`);
|
const DS = md5(`salt=${n}&t=${t}&r=${r}&b=${b}&q=${q}`);
|
||||||
return `${t},${r},${DS}`;
|
return `${t},${r},${DS}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取DS2
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
getDS2() {
|
getDS2() {
|
||||||
let t = Math.round(new Date().getTime() / 1000);
|
const t = Math.round(new Date().getTime() / 1000);
|
||||||
let r = randomString(6);
|
const r = randomString(6);
|
||||||
let sign = md5(`salt=BIPaooxbWZW02fGHZL1If26mYCljPgst&t=${t}&r=${r}`);
|
const sign = md5(`salt=BIPaooxbWZW02fGHZL1If26mYCljPgst&t=${t}&r=${r}`);
|
||||||
return `${t},${r},${sign}`;
|
return `${t},${r},${sign}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取请求头
|
||||||
|
* @param {string} query
|
||||||
|
* @param {string} body
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
getHeaders(query = '', body = '') {
|
getHeaders(query = '', body = '') {
|
||||||
const cn = {
|
const cn = {
|
||||||
app_version: '2.63.1',
|
app_version: '2.63.1',
|
||||||
|
|
@ -191,7 +234,7 @@ export default class MysZZZApi extends MysApi {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.e = e;
|
this.e = e;
|
||||||
this.e.isSr = true;
|
this.e.isZZZ = true;
|
||||||
res.retcode = Number(res.retcode);
|
res.retcode = Number(res.retcode);
|
||||||
switch (res.retcode) {
|
switch (res.retcode) {
|
||||||
case 0:
|
case 0:
|
||||||
|
|
@ -255,20 +298,3 @@ export default class MysZZZApi extends MysApi {
|
||||||
return _data.data;
|
return _data.data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function randomString(length) {
|
|
||||||
let randomStr = '';
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
randomStr += _.sample('abcdefghijklmnopqrstuvwxyz0123456789');
|
|
||||||
}
|
|
||||||
return randomStr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generateSeed(length = 16) {
|
|
||||||
const characters = '0123456789abcdef';
|
|
||||||
let result = '';
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
result += characters[Math.floor(Math.random() * characters.length)];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -6,49 +6,62 @@ import settings from '../lib/settings.js';
|
||||||
|
|
||||||
export class ZZZPlugin extends plugin {
|
export class ZZZPlugin extends plugin {
|
||||||
/**
|
/**
|
||||||
*
|
* 获取用户 UID(如果需要同时获取API,可以直接调用 getAPI)
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string | boolean>}
|
||||||
*/
|
*/
|
||||||
async getUID() {
|
async getUID() {
|
||||||
|
// 默认为当前用户
|
||||||
let user = this.e;
|
let user = this.e;
|
||||||
|
// 获取配置
|
||||||
const query = settings.getConfig('config').query;
|
const query = settings.getConfig('config').query;
|
||||||
const allow = _.get(query, 'others', true);
|
const allow = _.get(query, 'others', true);
|
||||||
|
// 如果 at 存在且允许查看其他用户
|
||||||
if (this.e.at && allow) {
|
if (this.e.at && allow) {
|
||||||
|
// 将当前用户的 user_id 设置为 at 对象的 user_id
|
||||||
this.e.user_id = this.e.at;
|
this.e.user_id = this.e.at;
|
||||||
|
// 将当前用户设置为 at 对象
|
||||||
user = this.e.at;
|
user = this.e.at;
|
||||||
}
|
}
|
||||||
|
// 获取用户信息(米游社),因此这里会导致查询一次米游社的信息
|
||||||
this.User = await NoteUser.create(user);
|
this.User = await NoteUser.create(user);
|
||||||
// let uid = this.e.msg.match(/\d+/)?.[0];
|
// 获取用户 UID
|
||||||
const uid = this.User?.getUid('zzz');
|
const uid = this.User?.getUid('zzz');
|
||||||
|
// 如果 UID 不存在,说明没有绑定 cookie
|
||||||
if (!uid) {
|
if (!uid) {
|
||||||
await this.reply('uid为空,米游社查询请先绑定cookie,其他查询请携带uid');
|
await this.reply('uid为空,米游社查询请先绑定cookie,其他查询请携带uid');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// 返回 UID
|
||||||
return uid;
|
return uid;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*
|
* 获取米游社 API
|
||||||
* @returns {Promise<{api: MysZZZApi, uid: string, deviceFp: string}>}
|
* @returns {Promise<{api: MysZZZApi, uid: string, deviceFp: string}>}
|
||||||
*/
|
*/
|
||||||
async getAPI() {
|
async getAPI() {
|
||||||
let uid = await this.getUID();
|
// 直接调用获取 UID
|
||||||
|
const uid = await this.getUID();
|
||||||
if (!uid) return false;
|
if (!uid) return false;
|
||||||
|
// 获取用户的 cookie
|
||||||
const ck = await getCk(this.e);
|
const ck = await getCk(this.e);
|
||||||
|
// 如果 cookie 不存在或者 cookie 为空,说明没有绑定 cookie
|
||||||
if (!ck || Object.keys(ck).filter(k => ck[k].ck).length === 0) {
|
if (!ck || Object.keys(ck).filter(k => ck[k].ck).length === 0) {
|
||||||
await this.reply('尚未绑定cookie,请先绑定cookie');
|
await this.reply('尚未绑定cookie,请先绑定cookie');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建米游社 API 对象
|
||||||
const api = new MysZZZApi(uid, ck);
|
const api = new MysZZZApi(uid, ck);
|
||||||
|
// 获取设备指纹
|
||||||
let deviceFp = await redis.get(`ZZZ:DEVICE_FP:${uid}`);
|
let deviceFp = await redis.get(`ZZZ:DEVICE_FP:${uid}`);
|
||||||
if (!deviceFp) {
|
if (!deviceFp) {
|
||||||
let sdk = api.getUrl('getFp');
|
const sdk = api.getUrl('getFp');
|
||||||
let res = await fetch(sdk.url, {
|
const res = await fetch(sdk.url, {
|
||||||
headers: sdk.headers,
|
headers: sdk.headers,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: sdk.body,
|
body: sdk.body,
|
||||||
});
|
});
|
||||||
let fpRes = await res.json();
|
const fpRes = await res.json();
|
||||||
deviceFp = fpRes?.data?.device_fp;
|
deviceFp = fpRes?.data?.device_fp;
|
||||||
if (deviceFp) {
|
if (deviceFp) {
|
||||||
await redis.set(`ZZZ:DEVICE_FP:${uid}`, deviceFp, {
|
await redis.set(`ZZZ:DEVICE_FP:${uid}`, deviceFp, {
|
||||||
|
|
@ -56,19 +69,25 @@ export class ZZZPlugin extends plugin {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 返回数据(API、UID、设备指纹)
|
||||||
return { api, uid, deviceFp };
|
return { api, uid, deviceFp };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* 获取玩家信息(当调用此方法时,会获取用户的玩家信息,并将其保存到`e.playerCard`中,方便渲染用户信息(此部分请查阅`lib/render.js`中两个模块的作用))
|
||||||
* @returns {Promise<boolean | object>}
|
* @returns {Promise<boolean | object>}
|
||||||
*/
|
*/
|
||||||
async getPlayerInfo() {
|
async getPlayerInfo() {
|
||||||
|
// 获取 米游社 API
|
||||||
const { api } = await this.getAPI();
|
const { api } = await this.getAPI();
|
||||||
if (!api) return false;
|
if (!api) return false;
|
||||||
|
// 获取用户信息
|
||||||
let userData = await api.getFinalData(this.e, 'zzzUser');
|
let userData = await api.getFinalData(this.e, 'zzzUser');
|
||||||
if (!userData) return false;
|
if (!userData) return false;
|
||||||
|
// 取第一个用户信息
|
||||||
userData = userData?.list[0];
|
userData = userData?.list[0];
|
||||||
|
|
||||||
|
// 获取用户头像
|
||||||
let avatar = this.e.bot.avatar;
|
let avatar = this.e.bot.avatar;
|
||||||
if (this.e?.user_id) {
|
if (this.e?.user_id) {
|
||||||
avatar = `https://q1.qlogo.cn/g?b=qq&s=0&nk=${this.e.user_id}`;
|
avatar = `https://q1.qlogo.cn/g?b=qq&s=0&nk=${this.e.user_id}`;
|
||||||
|
|
@ -77,10 +96,12 @@ export class ZZZPlugin extends plugin {
|
||||||
} else if (this.e.friend?.getAvatarUrl) {
|
} else if (this.e.friend?.getAvatarUrl) {
|
||||||
avatar = await this.e.friend.getAvatarUrl();
|
avatar = await this.e.friend.getAvatarUrl();
|
||||||
}
|
}
|
||||||
|
// 写入数据
|
||||||
this.e.playerCard = {
|
this.e.playerCard = {
|
||||||
avatar: avatar,
|
avatar: avatar,
|
||||||
player: userData,
|
player: userData,
|
||||||
};
|
};
|
||||||
|
// 返回数据
|
||||||
return userData;
|
return userData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import version from './version.js';
|
||||||
* @param {object} cfg 配置
|
* @param {object} cfg 配置
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function render(e, renderPath, renderData = {}, cfg = {}) {
|
const render = (e, renderPath, renderData = {}, cfg = {}) => {
|
||||||
// 判断是否存在e.runtime
|
// 判断是否存在e.runtime
|
||||||
if (!e.runtime) {
|
if (!e.runtime) {
|
||||||
console.log('未找到e.runtime,请升级至最新版Yunzai');
|
console.log('未找到e.runtime,请升级至最新版Yunzai');
|
||||||
|
|
@ -30,31 +30,55 @@ function render(e, renderPath, renderData = {}, cfg = {}) {
|
||||||
|
|
||||||
// 调用e.runtime.render方法渲染
|
// 调用e.runtime.render方法渲染
|
||||||
return e.runtime.render(pluginName, renderPath, renderData, {
|
return e.runtime.render(pluginName, renderPath, renderData, {
|
||||||
|
// 合并传入的配置
|
||||||
...cfg,
|
...cfg,
|
||||||
beforeRender({ data }) {
|
beforeRender({ data }) {
|
||||||
|
// 资源路径
|
||||||
const resPath = data.pluResPath;
|
const resPath = data.pluResPath;
|
||||||
|
// 布局路径
|
||||||
const layoutPath = data.pluResPath + 'common/layout/';
|
const layoutPath = data.pluResPath + 'common/layout/';
|
||||||
|
// 当前的渲染路径
|
||||||
const renderPathFull = data.pluResPath + renderPath.split('/')[0] + '/';
|
const renderPathFull = data.pluResPath + renderPath.split('/')[0] + '/';
|
||||||
|
// 合并数据
|
||||||
return {
|
return {
|
||||||
|
// 玩家信息
|
||||||
player: e?.playerCard?.player,
|
player: e?.playerCard?.player,
|
||||||
|
// 玩家头像
|
||||||
avatar: e?.playerCard?.avatar,
|
avatar: e?.playerCard?.avatar,
|
||||||
|
// 传入的数据
|
||||||
...data,
|
...data,
|
||||||
|
// 资源路径
|
||||||
_res_path: resPath,
|
_res_path: resPath,
|
||||||
|
// 布局路径
|
||||||
_layout_path: layoutPath,
|
_layout_path: layoutPath,
|
||||||
|
// 默认布局路径
|
||||||
defaultLayout: path.join(layoutPathFull, 'index.html'),
|
defaultLayout: path.join(layoutPathFull, 'index.html'),
|
||||||
|
// 系统配置
|
||||||
sys: {
|
sys: {
|
||||||
|
// 缩放比例
|
||||||
scale: pct,
|
scale: pct,
|
||||||
|
// 资源路径
|
||||||
resourcesPath: resPath,
|
resourcesPath: resPath,
|
||||||
|
// 当前渲染的路径
|
||||||
currentPath: renderPathFull,
|
currentPath: renderPathFull,
|
||||||
|
/**
|
||||||
|
* 下面两个模块的作用在于,你可以在你的布局文件中使用这两个模块,就可以显示用户信息和特殊标题,使用方法如下:
|
||||||
|
* 1. 展示玩家信息:首先你要在查询的时候调用`this.getPlayerInfo()`,这样,玩家数据就会保存在`e.playerCard`中,然后你就可以在布局文件中使用`{{include sys.playerInfo}}`来展示玩家信息。
|
||||||
|
* 2. 展示特殊标题:你可以在布局文件中使用`<% include(sys.specialTitle, {en: 'PROPERTY' , cn: '属性' , count: 6 }) %>`来展示特殊标题,其中`count`为可选参数,默认为9。
|
||||||
|
*/
|
||||||
|
// 玩家信息模块
|
||||||
playerInfo: path.join(layoutPathFull, 'playerinfo.html'),
|
playerInfo: path.join(layoutPathFull, 'playerinfo.html'),
|
||||||
|
// 特殊标题模块
|
||||||
specialTitle: path.join(layoutPathFull, 'specialtitle.html'),
|
specialTitle: path.join(layoutPathFull, 'specialtitle.html'),
|
||||||
|
// 版权信息
|
||||||
copyright: `Created By ${version.name}<span class="version">${version.yunzai}</span> & ${pluginName}<span class="version">${version.version}</span>`,
|
copyright: `Created By ${version.name}<span class="version">${version.yunzai}</span> & ${pluginName}<span class="version">${version.version}</span>`,
|
||||||
|
// 版权信息(简化版)
|
||||||
createdby: `Created By <span class="highlight">${pluginName}</span> & Powered By <span class="highlight">ZZZure</span>`,
|
createdby: `Created By <span class="highlight">${pluginName}</span> & Powered By <span class="highlight">ZZZure</span>`,
|
||||||
},
|
},
|
||||||
quality: 100,
|
quality: 100,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
export default render;
|
export default render;
|
||||||
|
|
|
||||||
|
|
@ -59,12 +59,17 @@ class Setting {
|
||||||
|
|
||||||
// 配置对象分析 用于锅巴插件界面设置
|
// 配置对象分析 用于锅巴插件界面设置
|
||||||
analysis(config) {
|
analysis(config) {
|
||||||
for (let key of Object.keys(config)) {
|
for (const key of Object.keys(config)) {
|
||||||
this.setConfig(key, config[key]);
|
this.setConfig(key, config[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取对应模块数据文件
|
/**
|
||||||
|
* 获取对应模块数据文件
|
||||||
|
* @param {string} filepath
|
||||||
|
* @param {string} filename
|
||||||
|
* @returns {object | false}
|
||||||
|
*/
|
||||||
getData(filepath, filename) {
|
getData(filepath, filename) {
|
||||||
filename = `${filename}.yaml`;
|
filename = `${filename}.yaml`;
|
||||||
filepath = path.join(this.dataPath, filepath);
|
filepath = path.join(this.dataPath, filepath);
|
||||||
|
|
@ -79,7 +84,13 @@ class Setting {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 写入对应模块数据文件
|
/**
|
||||||
|
* 写入对应模块数据文件
|
||||||
|
* @param {string} filepath
|
||||||
|
* @param {string} filename
|
||||||
|
* @param {object} data
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
setData(filepath, filename, data) {
|
setData(filepath, filename, data) {
|
||||||
filename = `${filename}.yaml`;
|
filename = `${filename}.yaml`;
|
||||||
filepath = path.join(this.dataPath, filepath);
|
filepath = path.join(this.dataPath, filepath);
|
||||||
|
|
@ -93,24 +104,38 @@ class Setting {
|
||||||
YAML.stringify(data),
|
YAML.stringify(data),
|
||||||
'utf8'
|
'utf8'
|
||||||
);
|
);
|
||||||
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[${pluginName}] [${filename}] 写入失败 ${error}`);
|
logger.error(`[${pluginName}] [${filename}] 写入失败 ${error}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取对应模块默认配置
|
/**
|
||||||
|
* 获取对应模块默认配置
|
||||||
|
* @param {'atlas'|'config'|'gacha'|'panel'} app
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
getdefSet(app) {
|
getdefSet(app) {
|
||||||
return this.getYaml(app, 'defSet');
|
return this.getYaml(app, 'defSet');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取对应模块用户配置
|
/**
|
||||||
|
* 获取对应模块用户配置(配置文件名)
|
||||||
|
* @param {'atlas'|'config'|'gacha'|'panel'} app
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
getConfig(app) {
|
getConfig(app) {
|
||||||
return { ...this.getdefSet(app), ...this.getYaml(app, 'config') };
|
return { ...this.getdefSet(app), ...this.getYaml(app, 'config') };
|
||||||
// return this.mergeConfigObjectArray({...this.getdefSet(app)},{...this.getYaml(app, 'config')});
|
// return this.mergeConfigObjectArray({...this.getdefSet(app)},{...this.getYaml(app, 'config')});
|
||||||
}
|
}
|
||||||
|
|
||||||
//合并两个对象 相同的数组对象 主要用于yml的列表属性合并 并去重 先备份一下方法
|
/**
|
||||||
|
* 合并两个对象 相同的数组对象 主要用于yml的列表属性合并 并去重 先备份一下方法
|
||||||
|
* @param {object} obj1
|
||||||
|
* @param {object} obj2
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
mergeConfigObjectArray(obj1, obj2) {
|
mergeConfigObjectArray(obj1, obj2) {
|
||||||
for (const key in obj2) {
|
for (const key in obj2) {
|
||||||
if (Array.isArray(obj2[key]) && Array.isArray(obj1[key])) {
|
if (Array.isArray(obj2[key]) && Array.isArray(obj1[key])) {
|
||||||
|
|
@ -126,9 +151,14 @@ class Setting {
|
||||||
return obj1;
|
return obj1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置对应模块用户配置
|
/**
|
||||||
setConfig(app, Object) {
|
* 设置对应模块用户配置
|
||||||
return this.setYaml(app, 'config', { ...this.getdefSet(app), ...Object });
|
* @param {'atlas'|'config'|'gacha'|'panel'} app
|
||||||
|
* @param {object} obj
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
setConfig(app, obj) {
|
||||||
|
return this.setYaml(app, 'config', { ...this.getdefSet(app), ...obj });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将对象写入YAML文件
|
// 将对象写入YAML文件
|
||||||
|
|
|
||||||
|
|
@ -3,42 +3,70 @@ import lodash from 'lodash';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { pluginPath } from './path.js';
|
import { pluginPath } from './path.js';
|
||||||
|
|
||||||
|
// 更新日志文件位置
|
||||||
const _logPath = path.join(pluginPath, 'CHANGELOG.md');
|
const _logPath = path.join(pluginPath, 'CHANGELOG.md');
|
||||||
|
|
||||||
|
// 存放日志
|
||||||
let logs = {};
|
let logs = {};
|
||||||
|
|
||||||
|
// 存放更新日志
|
||||||
let changelogs = [];
|
let changelogs = [];
|
||||||
|
|
||||||
|
// 当前版本
|
||||||
let currentVersion;
|
let currentVersion;
|
||||||
|
|
||||||
|
// 版本数量
|
||||||
let versionCount = 4;
|
let versionCount = 4;
|
||||||
|
|
||||||
|
// 读取 package.json(此处为读取Yunzai-Bot的package.json)
|
||||||
let packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
let packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 Markdown 行转换为 HTML
|
||||||
|
* @param {*} line
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
const getLine = function (line) {
|
const getLine = function (line) {
|
||||||
|
// 去除行首空格和换行符
|
||||||
line = line.replace(/(^\s*\*|\r)/g, '');
|
line = line.replace(/(^\s*\*|\r)/g, '');
|
||||||
|
// 替换行内代码块
|
||||||
line = line.replace(/\s*`([^`]+`)/g, '<span class="cmd">$1');
|
line = line.replace(/\s*`([^`]+`)/g, '<span class="cmd">$1');
|
||||||
line = line.replace(/`\s*/g, '</span>');
|
line = line.replace(/`\s*/g, '</span>');
|
||||||
|
// 替换行内加粗
|
||||||
line = line.replace(/\s*\*\*([^\*]+\*\*)/g, '<span class="strong">$1');
|
line = line.replace(/\s*\*\*([^\*]+\*\*)/g, '<span class="strong">$1');
|
||||||
line = line.replace(/\*\*\s*/g, '</span>');
|
line = line.replace(/\*\*\s*/g, '</span>');
|
||||||
|
// 替换行内表示更新内容
|
||||||
line = line.replace(/ⁿᵉʷ/g, '<span class="new"></span>');
|
line = line.replace(/ⁿᵉʷ/g, '<span class="new"></span>');
|
||||||
|
// 返回转换后的行内容(HTML)
|
||||||
return line;
|
return line;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 尝试读取更新日志文件
|
||||||
try {
|
try {
|
||||||
if (fs.existsSync(_logPath)) {
|
if (fs.existsSync(_logPath)) {
|
||||||
|
// 如果文件存在,读取文件内容
|
||||||
logs = fs.readFileSync(_logPath, 'utf8') || '';
|
logs = fs.readFileSync(_logPath, 'utf8') || '';
|
||||||
|
// 将文件内容按行分割
|
||||||
logs = logs.split('\n');
|
logs = logs.split('\n');
|
||||||
|
|
||||||
|
// 遍历每一行,提取版本号和更新内容
|
||||||
let temp = {};
|
let temp = {};
|
||||||
let lastLine = {};
|
let lastLine = {};
|
||||||
lodash.forEach(logs, line => {
|
lodash.forEach(logs, line => {
|
||||||
|
// 如果版本数量小于0,返回false
|
||||||
if (versionCount <= -1) {
|
if (versionCount <= -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let versionRet = /^#\s*([0-9a-zA-Z\\.~\s]+?)\s*$/.exec(line);
|
// 匹配版本号
|
||||||
|
const versionRet = /^#\s*([0-9a-zA-Z\\.~\s]+?)\s*$/.exec(line);
|
||||||
if (versionRet && versionRet[1]) {
|
if (versionRet && versionRet[1]) {
|
||||||
let v = versionRet[1].trim();
|
// 如果匹配到版本号,提取版本号
|
||||||
|
const v = versionRet[1].trim();
|
||||||
if (!currentVersion) {
|
if (!currentVersion) {
|
||||||
|
// 如果当前版本号不存在,将当前版本号设置为匹配到的版本号
|
||||||
currentVersion = v;
|
currentVersion = v;
|
||||||
} else {
|
} else {
|
||||||
|
// 写入更新日志
|
||||||
changelogs.push(temp);
|
changelogs.push(temp);
|
||||||
if (/0\s*$/.test(v) && versionCount > 0) {
|
if (/0\s*$/.test(v) && versionCount > 0) {
|
||||||
versionCount = 0;
|
versionCount = 0;
|
||||||
|
|
@ -46,15 +74,16 @@ try {
|
||||||
versionCount--;
|
versionCount--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
temp = {
|
temp = {
|
||||||
version: v,
|
version: v,
|
||||||
logs: [],
|
logs: [],
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
// 如果行为空,不继续执行
|
||||||
if (!line.trim()) {
|
if (!line.trim()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 如果行以 * 开头,表示更新内容
|
||||||
if (/^\*/.test(line)) {
|
if (/^\*/.test(line)) {
|
||||||
lastLine = {
|
lastLine = {
|
||||||
title: getLine(line),
|
title: getLine(line),
|
||||||
|
|
@ -71,18 +100,27 @@ try {
|
||||||
// void error
|
// void error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 读取Yunzai-Bot的版本号
|
||||||
const yunzaiVersion = packageJson.version;
|
const yunzaiVersion = packageJson.version;
|
||||||
|
|
||||||
|
// 判断是否为Yunzai-Bot v3
|
||||||
const isV3 = yunzaiVersion[0] === '3';
|
const isV3 = yunzaiVersion[0] === '3';
|
||||||
|
|
||||||
|
// 是否为喵(默认为否)
|
||||||
let isMiao = false;
|
let isMiao = false;
|
||||||
|
// bot名称
|
||||||
let name = 'Yunzai-Bot';
|
let name = 'Yunzai-Bot';
|
||||||
if (packageJson.name === 'miao-yunzai') {
|
if (packageJson.name === 'miao-yunzai') {
|
||||||
|
// 如果是喵,将 isMiao 设置为 true,name 设置为 Miao-Yunzai
|
||||||
isMiao = true;
|
isMiao = true;
|
||||||
name = 'Miao-Yunzai';
|
name = 'Miao-Yunzai';
|
||||||
} else if (packageJson.name === 'trss-yunzai') {
|
} else if (packageJson.name === 'trss-yunzai') {
|
||||||
|
// 如果是 TRSS-Yunzai,将 isMiao 设置为 true,name 设置为 TRSS-Yunzai
|
||||||
isMiao = true;
|
isMiao = true;
|
||||||
name = 'TRSS-Yunzai';
|
name = 'TRSS-Yunzai';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 导出版本信息
|
||||||
const version = {
|
const version = {
|
||||||
isV3,
|
isV3,
|
||||||
isMiao,
|
isMiao,
|
||||||
|
|
|
||||||
26
utils/data.js
Normal file
26
utils/data.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
/**
|
||||||
|
* 生成随机字符串
|
||||||
|
* @param {number} length 长度
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export const randomString = length => {
|
||||||
|
let randomStr = '';
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
randomStr += _.sample('abcdefghijklmnopqrstuvwxyz0123456789');
|
||||||
|
}
|
||||||
|
return randomStr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成随机种子
|
||||||
|
* @param {number} length 长度
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export const generateSeed = (length = 16) => {
|
||||||
|
const characters = '0123456789abcdef';
|
||||||
|
let result = '';
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
result += characters[Math.floor(Math.random() * characters.length)];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue