修正天赋记录错误时可能导致排行列表错误的问题

This commit is contained in:
Kokomi 2023-01-18 02:40:17 +08:00
parent fb5514c444
commit 1038f00c90
18 changed files with 137 additions and 60 deletions

View File

@ -1,26 +1,24 @@
# 2.1.7 # 2.1.8
* 更新瑶瑶、艾尔海森的信息,可通过`#瑶瑶天赋`、`#瑶瑶图鉴`等查看 * 增加`#启用排名``#禁用排名`,可在全局启用排名情况下,在特定群内禁用排名功能
* 增加艾尔海森 **@panganqi**、珐露珊的伤害计算 * `#日历`展示问题修复
* 一些已知问题修复
# 2.1.1~2.1.6 # 2.1.1~2.1.7
* 增加面板替换功能,可通过命令更换面板的圣遗物、武器、天赋命座等,用于伤害计算
* `#雷神面板换稻光换90级满命` / `#刻晴面板换雷神圣遗物` 等命令
* 更多命令参见 `#面板帮助`,请根据需求吟唱。后续会提供更细致的咒语详解
* 增加角色面板立绘图相关命令 **@cvs** * 增加角色面板立绘图相关命令 **@cvs**
* 支持`#上传刻晴面板图`上传 * 支持`#上传刻晴面板图`上传
* 新增`#刻晴面板图列表` * 新增`#刻晴面板图列表`
* 可通过指令查询当前可看的面板立绘 * 可通过指令查询当前可看的面板立绘
* 立绘支持`#原图`指令 * 立绘支持`#原图`指令
* 增加散兵的伤害计算 **@panganqi**
* 角色立绘支持随机,用于面板场景 * 角色立绘支持随机,用于面板场景
* 图像支持webp及png格式 * 图像支持webp及png格式
* 普通立绘:**resources/profile/normal-character/** * 普通立绘:**resources/profile/normal-character/**
* 彩蛋立绘(满命/ACE/三皇冠)**resources/profile/super-character/** * 彩蛋立绘(满命/ACE/三皇冠)**resources/profile/super-character/**
* 单张立绘请放置在普通&彩蛋目录下,以**角色全名**为**文件名**,例如**刻晴.webp** * 单张立绘请放置在普通&彩蛋目录下,以**角色全名**为**文件名**,例如**刻晴.webp**
* 如需多张随机,请在普通&彩蛋目录下,以**角色全名**为**目录**名,任意文件名为文件名,例如 **刻晴/1.png** * 如需多张随机,请在普通&彩蛋目录下,以**角色全名**为**目录**名,任意文件名为文件名,例如 **刻晴/1.png**
* 增加面板替换功能,可通过命令更换面板的圣遗物、武器、天赋命座等,用于伤害计算
* `#雷神面板换稻光换90级满命` / `#刻晴面板换雷神圣遗物` 等命令
* 更多命令参见 `#面板帮助`,请根据需求吟唱。后续会提供更细致的咒语详解
* 去除插件内自带的V2/V3兼容逻辑使用runtime进行V2/V3兼容如使用遇到问题请升级至最新版Yunzai * 去除插件内自带的V2/V3兼容逻辑使用runtime进行V2/V3兼容如使用遇到问题请升级至最新版Yunzai
* V3-Yunzai官方Yunzai最新版本 * V3-Yunzai官方Yunzai最新版本
* V2-Yunzai喵版V2-Yunzai2.2.3版本。其余分值维护的V2-Yunzai可合并2.2.3版本 * V2-Yunzai喵版V2-Yunzai2.2.3版本。其余分值维护的V2-Yunzai可合并2.2.3版本
@ -31,6 +29,8 @@
* 圣遗物主词条评分规则微调,可能会影响部分角色评分 * 圣遗物主词条评分规则微调,可能会影响部分角色评分
* 元素杯属性不符会触发主词缀评分惩罚 * 元素杯属性不符会触发主词缀评分惩罚
* 充能主词条不再触发主词缀评分惩罚 * 充能主词条不再触发主词缀评分惩罚
* 更新瑶瑶、艾尔海森的信息,可通过`#瑶瑶天赋`、`#瑶瑶图鉴`等查看
* 增加散兵、艾尔海森 **@panganqi**、珐露珊的伤害计算
* 一些已知问题修正与样式优化 * 一些已知问题修正与样式优化
# 2.1.0 # 2.1.0

View File

@ -3,7 +3,7 @@ import { Character } from '../models/index.js'
import { renderAvatar } from './character/AvatarCard.js' import { renderAvatar } from './character/AvatarCard.js'
import { uploadCharacterImg } from './character/ImgUpload.js' import { uploadCharacterImg } from './character/ImgUpload.js'
import { wife, wifeReg } from './character/AvatarWife.js' import { wife, wifeReg } from './character/AvatarWife.js'
import { getOriginalPicture } from './character/ProfileUtils.js' import { getOriginalPicture } from './profile/ProfileUtils.js'
let app = App.init({ let app = App.init({
id: 'character', id: 'character',

View File

@ -1,14 +1,14 @@
import { Common, App, Data } from '../components/index.js' import { Common, App, Data } from '../components/index.js'
import { Character } from '../models/index.js' import { Character } from '../models/index.js'
import { getTargetUid, getProfile, profileHelp } from './character/ProfileCommon.js' import { getTargetUid, getProfile, profileHelp } from './profile/ProfileCommon.js'
import { profileArtis, profileArtisList } from './character/ProfileArtis.js' import { profileArtis, profileArtisList } from './profile/ProfileArtis.js'
import { renderProfile } from './character/ProfileDetail.js' import { renderProfile } from './profile/ProfileDetail.js'
import { profileStat } from './character/ProfileStat.js' import { profileStat } from './profile/ProfileStat.js'
import { profileList } from './character/ProfileList.js' import { profileList } from './profile/ProfileList.js'
import { uploadCharacterImg, delProfileImg, profileImgList } from './character/ImgUpload.js' import { uploadCharacterImg, delProfileImg, profileImgList } from './character/ImgUpload.js'
import { enemyLv } from './character/ProfileUtils.js' import { enemyLv } from './profile/ProfileUtils.js'
import ProfileChange from './profile/ProfileChange.js' import ProfileChange from './profile/ProfileChange.js'
import { groupRank, resetRank, refreshRank } from './character/ProfileRank.js' import { groupRank, resetRank, refreshRank, manageRank } from './profile/ProfileRank.js'
let app = App.init({ let app = App.init({
id: 'profile', id: 'profile',
@ -38,6 +38,11 @@ app.reg('refresh-rank', refreshRank, {
name: '重置排名' name: '重置排名'
}) })
app.reg('manage-rank', manageRank, {
rule: /^#(开启|打开|启用|关闭|禁用)(群内|群|全部)*(排名|排行)$/,
name: '打开关闭'
})
app.reg('rank-list', groupRank, { app.reg('rank-list', groupRank, {
rule: /^#(群|群内)?.+(排名|排行)(榜)?$/, rule: /^#(群|群内)?.+(排名|排行)(榜)?$/,
name: '面板排名榜' name: '面板排名榜'

View File

@ -1,3 +1,6 @@
/**
* 面板数据替换相关逻辑
*/
import lodash from 'lodash' import lodash from 'lodash'
import { Profile, Data } from '../../components/index.js' import { Profile, Data } from '../../components/index.js'
import { Character, ProfileData, Weapon } from '../../models/index.js' import { Character, ProfileData, Weapon } from '../../models/index.js'
@ -18,7 +21,7 @@ lodash.forEach(keyMap, (val, key) => {
}) })
}) })
const keyReg = new RegExp(`^(\\d{9})?\\s*(.+?)\\s*(\\d{9})?\\s*((?:${lodash.keys(keyTitleMap).join('|')}|\\+)+)$`) const keyReg = new RegExp(`^(\\d{9})?\\s*(.+?)\\s*(\\d{9})?\\s*((?:${lodash.keys(keyTitleMap).join('|')}|\\+)+)$`)
// 默认武器
let defWeapon = { let defWeapon = {
bow: '西风猎弓', bow: '西风猎弓',
catalyst: '西风秘典', catalyst: '西风秘典',
@ -140,12 +143,17 @@ const ProfileChange = {
}, },
/** /**
* * 获取面板数据
* @param uid
* @param charid
* @param ds
* @returns {ProfileData|boolean}
*/ */
getProfile (uid, charid, ds) { getProfile (uid, charid, ds) {
if (!charid) { if (!charid) {
return false return false
} }
let source = Profile.get(uid, charid) let source = Profile.get(uid, charid)
let dc = ds.char || {} let dc = ds.char || {}
if (!source || !source.hasData) { if (!source || !source.hasData) {

View File

@ -36,8 +36,8 @@ export async function profileList (e) {
if (groupId) { if (groupId) {
rank = await ProfileRank.create({ groupId, uid, qq: e.user_id }) rank = await ProfileRank.create({ groupId, uid, qq: e.user_id })
} }
const groupRank = rank && (cfg?.diyCfg?.groupRank || false)
const rankCfg = await ProfileRank.getGroupCfg(groupId) const rankCfg = await ProfileRank.getGroupCfg(groupId)
const groupRank = rank && (cfg?.diyCfg?.groupRank || false) && rankCfg.status !== 1
await Profile.forEach(uid, async function (profile) { await Profile.forEach(uid, async function (profile) {
if (!profile.hasData) { if (!profile.hasData) {
return true return true

View File

@ -28,8 +28,13 @@ export async function groupRank (e) {
return false return false
} }
} }
let groupCfg = await ProfileRank.getGroupCfg(groupId)
if (!groupRank) { if (!groupRank) {
e.reply('群面板排名功能已禁用...') e.reply('群面板排名功能已禁用,主人可通过【#喵喵设置】启用...')
return true
}
if (groupCfg.status === 1) {
e.reply('本群已关闭群排名,主人可通过【#启用排名】启用...')
return true return true
} }
if (type === 'detail') { if (type === 'detail') {
@ -129,6 +134,24 @@ export async function refreshRank (e) {
e.reply(`本群排名已刷新,共刷新${count}个UID数据...`) e.reply(`本群排名已刷新,共刷新${count}个UID数据...`)
} }
export async function manageRank (e) {
let groupId = e.group_id
if (!groupId) {
return true
}
let isClose = /(关闭|禁用)/.test(e.msg)
if (!e.isMaster) {
e.reply(`只有管理员可${isClose ? '禁用' : '启用'}排名...`)
return true
}
await ProfileRank.setGroupStatus(groupId, isClose ? 1 : 0)
if (isClose) {
e.reply('当前群排名功能已禁用...')
} else {
e.reply('当前群排名功能已启用...\n如数据有问题可通过【#刷新排名】命令来刷新当前群内排名')
}
}
async function renderCharRankList ({ e, uids, char, mode, groupId }) { async function renderCharRankList ({ e, uids, char, mode, groupId }) {
let list = [] let list = []
for (let ds of uids) { for (let ds of uids) {

View File

@ -191,6 +191,11 @@ let Cal = {
return ret return ret
}, },
/**
* 获取角色数据
* @param dateList
* @returns {{charBirth: {}, charNum: number, charTalent: (*|{})}}
*/
getCharData (dateList) { getCharData (dateList) {
let charBirth = {} let charBirth = {}
let charTalent = {} let charTalent = {}
@ -235,6 +240,18 @@ let Cal = {
return { charBirth, charNum, charTalent } return { charBirth, charNum, charTalent }
}, },
/**
* 获取日历列表
* @param ds
* @param target
* @param startTime
* @param endTime
* @param totalRange
* @param now
* @param timeMap
* @param isAct
* @returns {boolean}
*/
getList (ds, target, { startTime, endTime, totalRange, now, timeMap = {} }, isAct = false) { getList (ds, target, { startTime, endTime, totalRange, now, timeMap = {} }, isAct = false) {
let type = isAct ? 'activity' : 'normal' let type = isAct ? 'activity' : 'normal'
let id = ds.ann_id let id = ds.ann_id
@ -297,7 +314,7 @@ let Cal = {
} else if (isAct) { } else if (isAct) {
label = sDate.format('MM-DD HH:mm') + ' ~ ' + eDate.format('MM-DD HH:mm') label = sDate.format('MM-DD HH:mm') + ' ~ ' + eDate.format('MM-DD HH:mm')
} }
if (sDate <= endTime && eDate >= startTime) {
target.push({ target.push({
...extra, ...extra,
id, id,
@ -313,6 +330,7 @@ let Cal = {
start: sDate.format('MM-DD HH:mm'), start: sDate.format('MM-DD HH:mm'),
end: eDate.format('MM-DD HH:mm') end: eDate.format('MM-DD HH:mm')
}) })
}
}, },
async get () { async get () {

View File

@ -61,11 +61,11 @@ let Data = {
delfile (file) { delfile (file) {
try { try {
fs.unlinkSync(`${_path}/${file}`) fs.unlinkSync(`${_path}/${file}`)
return true; return true
} catch (error) { } catch (error) {
logger.error(`文件删除失败:${error}`) logger.error(`文件删除失败:${error}`)
} }
return false; return false
}, },
async getCacheJSON (key) { async getCacheJSON (key) {
try { try {
@ -83,6 +83,22 @@ let Data = {
await redis.set(key, JSON.stringify(data), { EX }) await redis.set(key, JSON.stringify(data), { EX })
}, },
async redisGet (key, def = {}) {
try {
let txt = await redis.get(key)
if (txt) {
return JSON.parse(txt)
}
} catch (e) {
console.log(e)
}
return def
},
async redisSet (key, data, EX = 3600 * 24 * 90) {
await redis.set(key, JSON.stringify(data), { EX })
},
async importModule (file, root = '') { async importModule (file, root = '') {
root = getRoot(root) root = getRoot(root)
if (!/\.js$/.test(file)) { if (!/\.js$/.test(file)) {

View File

@ -1,6 +1,6 @@
import lodash from 'lodash' import lodash from 'lodash'
import moment from 'moment' import moment from 'moment'
import { Common } from '../components/index.js' import { Common, Data } from '../components/index.js'
export default class ProfileRank { export default class ProfileRank {
constructor (data) { constructor (data) {
@ -185,24 +185,31 @@ export default class ProfileRank {
5: '绑定CK或列表有16个角色数据且包含安柏&凯亚&丽莎' 5: '绑定CK或列表有16个角色数据且包含安柏&凯亚&丽莎'
} }
let rankLimit = Common.cfg('groupRankLimit') * 1 || 1 let rankLimit = Common.cfg('groupRankLimit') * 1 || 1
let ret = { let ret = await Data.redisGet(`miao:rank:${groupId}:cfg`, {
timestamp: (new Date()) * 1, timestamp: (new Date()) * 1,
status: 0 status: 0
} })
try { await Data.redisSet(`miao:rank:${groupId}:cfg`, ret, 3600 * 24 * 365)
let cfg = await redis.get(`miao:rank:${groupId}:cfg`)
if (!cfg) {
await redis.set(`miao:rank:${groupId}:cfg`, JSON.stringify(ret), { EX: 3600 * 24 * 365 })
} else {
ret = JSON.parse(cfg)
}
} catch (e) {
}
ret.limitTxt = rankLimitTxt[rankLimit] ret.limitTxt = rankLimitTxt[rankLimit]
ret.time = moment(new Date(ret.timestamp)).format('MM-DD HH:mm') ret.time = moment(new Date(ret.timestamp)).format('MM-DD HH:mm')
return ret return ret
} }
/**
* 设置群开关状态
* @param groupId
* @param status0开启1关闭
* @returns {Promise<void>}
*/
static async setGroupStatus (groupId, status = 0) {
let cfg = await Data.redisGet(`miao:rank:${groupId}:cfg`, {
timestamp: (new Date()) * 1,
status
})
cfg.status = status
await Data.redisSet(`miao:rank:${groupId}:cfg`, cfg, 3600 * 24 * 365)
}
static async setUidInfo ({ uid, qq, profiles, uidType = 'bind' }) { static async setUidInfo ({ uid, qq, profiles, uidType = 'bind' }) {
if (!uid) { if (!uid) {
return false return false

View File

@ -24,8 +24,8 @@ export default class ProfileReq extends Base {
if (!ext || isNaN(ext)) { if (!ext || isNaN(ext)) {
return false return false
} }
let cd = new Date() * 1 - ext let cd = (new Date() * 1) - ext
if (cd < 0) { if (cd < 0 && Math.abs(cd) < 100 * 60 * 1000) {
return Math.ceil(0 - cd / 1000) return Math.ceil(0 - cd / 1000)
} }
return false return false

View File

@ -10,8 +10,8 @@
"birth": "3-6", "birth": "3-6",
"astro": "木樨座", "astro": "木樨座",
"desc": "歌尘浪市真君膝下最年幼的弟子,温柔体贴的「小大人」。", "desc": "歌尘浪市真君膝下最年幼的弟子,温柔体贴的「小大人」。",
"cncv": "", "cncv": "刘颐诺",
"jpcv": "", "jpcv": "门胁舞以",
"costume": false, "costume": false,
"ver": 1, "ver": 1,
"baseAttr": { "baseAttr": {

View File

@ -204,7 +204,7 @@
"会投掷白玉萝卜,白玉萝卜会在命中角色或敌人时炸裂,对一定范围内的敌人造成草元素伤害,并为其中的角色恢复生命值,回复量受益于瑶瑶的生命值上限;若未命中敌人或角色,白玉萝卜将会留在命中的位置,并在触及角色或敌人时,或持续时间结束时炸裂。", "会投掷白玉萝卜,白玉萝卜会在命中角色或敌人时炸裂,对一定范围内的敌人造成草元素伤害,并为其中的角色恢复生命值,回复量受益于瑶瑶的生命值上限;若未命中敌人或角色,白玉萝卜将会留在命中的位置,并在触及角色或敌人时,或持续时间结束时炸裂。",
"月桂·抛掷型会依据附近的情况,选择投掷白玉萝卜的目标:", "月桂·抛掷型会依据附近的情况,选择投掷白玉萝卜的目标:",
"·附近的角色如果生命值均高于70%,会向附近的一名敌人投掷;", "·附近的角色如果生命值均高于70%,会向附近的一名敌人投掷;",
"·附近如果存在生命值低于或等于70%的角色,则会向附近场上生命值百分比最低的角色投掷。若附近不存在敌人,即使角色的生命值均高于70%,也会向角色投掷。", "·附近如果存在生命值低于或等于70%的角色,则会向附近场上生命值百分比最低的角色投掷。若附近不存在敌人,在角色的生命值均高于70%、低于100%时,也会向角色投掷;否则会向周围随意投掷。",
"同时至多存在2个月桂·抛掷型。", "同时至多存在2个月桂·抛掷型。",
"<i>「萝卜上市,郎中无事!啊,这不是说萝卜治百病的意思,不过,多吃萝卜确实有好处!」</i>" "<i>「萝卜上市,郎中无事!啊,这不是说萝卜治百病的意思,不过,多吃萝卜确实有好处!」</i>"
], ],
@ -328,7 +328,7 @@
"依照某位仙人的嘱咐,在紧急情况下,解放月桂的全部潜能,对周围的敌人造成草元素伤害,并进入(某种意义上)所向披靡的「桂子仙机」状态。", "依照某位仙人的嘱咐,在紧急情况下,解放月桂的全部潜能,对周围的敌人造成草元素伤害,并进入(某种意义上)所向披靡的「桂子仙机」状态。",
"<h3>桂子仙机</h3>", "<h3>桂子仙机</h3>",
"·产生的白玉萝卜将转为依据本技能详细属性造成伤害与治疗,炸裂时将为队伍中附近的所有角色恢复生命值,造成的草元素伤害转而视为元素爆发伤害;", "·产生的白玉萝卜将转为依据本技能详细属性造成伤害与治疗,炸裂时将为队伍中附近的所有角色恢复生命值,造成的草元素伤害转而视为元素爆发伤害;",
"·周期性召唤「月桂·弹跳型」直到数量达到上限。月桂·弹跳型与元素战技「云台团团降芦菔」召唤的月桂·抛掷型行为方式相同。同时至多存在3个月桂·弹跳型", "·周期性召唤「月桂·弹跳型」直到数量达到上限。月桂·弹跳型与元素战技「云台团团降芦菔」召唤的月桂·抛掷型行为方式相同。同时至多存在3个月桂·弹跳型",
"·瑶瑶的移动速度提升15%", "·瑶瑶的移动速度提升15%",
"·瑶瑶获得草元素抗性提升。", "·瑶瑶获得草元素抗性提升。",
"桂子仙机状态将在退场时结束。桂子仙机状态结束时,将移除剩余的月桂·弹跳型。", "桂子仙机状态将在退场时结束。桂子仙机状态结束时,将移除剩余的月桂·弹跳型。",

View File

@ -10,8 +10,8 @@
"birth": "2-11", "birth": "2-11",
"astro": "天隼座", "astro": "天隼座",
"desc": "须弥教令院现任书记官,有过人的智慧与才能。生活得自由自在,一般人基本找不到他。", "desc": "须弥教令院现任书记官,有过人的智慧与才能。生活得自由自在,一般人基本找不到他。",
"cncv": "", "cncv": "杨超然",
"jpcv": "", "jpcv": "梅原裕一郎",
"costume": false, "costume": false,
"ver": 1, "ver": 1,
"baseAttr": { "baseAttr": {