初步适配Yunzai V3

This commit is contained in:
yoimiya-kokomi 2022-07-24 04:32:10 +08:00
parent 026f4230b5
commit 89876ab31e
350 changed files with 1679 additions and 1786 deletions

View File

@ -1,3 +1,7 @@
# 1.9.0
* 初步适配Yunzai V3
# 1.8.9
* 增加提纳里、柯莱、多莉的资料及角色图像
@ -12,138 +16,138 @@
# 1.8.1~1.8.7
* 新增`#上传深渊数据`命令
* 上传自己角色的深渊挑战数据及角色列表,并展示在本期深渊中伤害与承伤排名
* 上传数据用于`#角色持有率 #深渊出场率`等统计,可使统计更加及时准确
* 数据统计及服务来自SG团队胡桃API
* 上传自己角色的深渊挑战数据及角色列表,并展示在本期深渊中伤害与承伤排名
* 上传数据用于`#角色持有率 #深渊出场率`等统计,可使统计更加及时准确
* 数据统计及服务来自SG团队胡桃API
* 增加`#深渊使用率`命令数据源自SG团队胡桃API
* `#刻晴` 角色卡片功能升级
* `#老婆设置刻晴,心海`不再检查是否具有角色或展示在米游社展柜
* `#刻晴` 角色卡片优先使用面板数据进行展示,无面板数据时使用米游社数据
* 在未能获取到角色数据时也会展示角色卡片
* `#老婆设置刻晴,心海`不再检查是否具有角色或展示在米游社展柜
* `#刻晴` 角色卡片优先使用面板数据进行展示,无面板数据时使用米游社数据
* 在未能获取到角色数据时也会展示角色卡片
* 支持戳一戳返回喵喵版角色卡片
* 需要使用喵喵分支Yunzai以支持此能力如需切换可在Yunzai根目录输入下方命令后更新重启
* `git remote set-url origin https://gitee.com/yoimiya-kokomi/Yunzai-Bot`
* 可通过`#喵喵设置` 关闭戳一戳
* 需要使用喵喵分支Yunzai以支持此能力如需切换可在Yunzai根目录输入下方命令后更新重启
* `git remote set-url origin https://gitee.com/yoimiya-kokomi/Yunzai-Bot`
* 可通过`#喵喵设置` 关闭戳一戳
* 支持定义新角色及别名
* 新增角色 派蒙、瑶瑶、白术、伐难、应达、散兵、女士、萍儿 的角色配置及图片
* 自定义角色可使用`#派蒙` `#派蒙图片`触发图片查看,`#女儿设置派蒙`进行设置。后续会支持更多场景
* 如需扩展可在喵喵config/character.js中定义
* 新增角色 派蒙、瑶瑶、白术、伐难、应达、散兵、女士、萍儿 的角色配置及图片
* 自定义角色可使用`#派蒙` `#派蒙图片`触发图片查看,`#女儿设置派蒙`进行设置。后续会支持更多场景
* 如需扩展可在喵喵config/character.js中定义
* `#喵喵帮助`增加对自定义配置文件的支持
* 已有配置文件可更名为help-cfg.js防止后续更新冲突后续会支持更多配置项
* 在默认配置中增加部分新的帮助命令
* 已有配置文件可更名为help-cfg.js防止后续更新冲突后续会支持更多配置项
* 在默认配置中增加部分新的帮助命令
* 增加`#添加刻晴图像`命令,感谢 **@叶**
* 可通过命令上传添加指定角色图片,上传至 **resources/character-img/刻晴/upload**
* 请将图像与命令一同发送后续会支持at图像及命令后发送图像
* 可通过命令上传添加指定角色图片,上传至 **resources/character-img/刻晴/upload**
* 请将图像与命令一同发送后续会支持at图像及命令后发送图像
* `#喵喵日历`现在可通过`#日历 #日历列表`触发
# 1.8.0
* `#角色面板`、`#圣遗物列表` 使用新的圣遗物评分逻辑计算评分
* 新的圣遗物评分规针对不同角色进行了细化,对不同角色的评分进行了拉齐
* 不同角色基于不同词条权重进行计算。感谢 **@糖炒栗子 @秋声 @49631073**等的权重梳理
* 新的圣遗物评分规针对不同角色进行了细化,对不同角色的评分进行了拉齐
* 不同角色基于不同词条权重进行计算。感谢 **@糖炒栗子 @秋声 @49631073**等的权重梳理
* 增加`#雷神圣遗物`命令
* 展示指定角色圣遗物及评分计算详情
* 展示新版圣遗物评分逻辑与计算规则
* 展示指定角色圣遗物及评分计算详情
* 展示新版圣遗物评分逻辑与计算规则
* 增加`#原图`命令,可获取喵喵角色卡片原图,感谢 **@牧星长** 提供功能
* 对由`#老婆 #刻晴`发出的角色卡片图回复`#原图`可获取对应图像
* 对由`#老婆 #刻晴`发出的角色卡片图回复`#原图`可获取对应图像
* `#角色面板`现在支持B服角色数据获取
* 数据来自喵喵API目前开放调用无需Token仅限喵喵插件用户使用
* 已知问题角色天赋的皇冠及命座加成效果显示可能有问题后期fix
* 数据来自喵喵API目前开放调用无需Token仅限喵喵插件用户使用
* 已知问题角色天赋的皇冠及命座加成效果显示可能有问题后期fix
* `#录入角色面板` 功能恢复
* 可对已有面板数据的角色手工输入更改面板属性,用于伤害测算
* 例如`#录入雷神面板 暴击80暴伤250`
* 暂不支持设置武器、圣遗物、命座、天赋。后续会增加支持
* 可对已有面板数据的角色手工输入更改面板属性,用于伤害测算
* 例如`#录入雷神面板 暴击80暴伤250`
* 暂不支持设置武器、圣遗物、命座、天赋。后续会增加支持
* 部分页面样式调整及功能优化
* `#角色持有率` 等增加提示说明
* `#圣遗物列表` 展示个数提升至28且根据新版圣遗物评分规则进行词条高亮
* `#喵喵更新` 的自动重启功能适配node app方式启动的Yunzai-Bot感谢 **@SirlyDreamer**
* `#角色持有率` 等增加提示说明
* `#圣遗物列表` 展示个数提升至28且根据新版圣遗物评分规则进行词条高亮
* `#喵喵更新` 的自动重启功能适配node app方式启动的Yunzai-Bot感谢 **@SirlyDreamer**
* 角色图像增加小清新开关,默认关闭
* 对增量包内的角色图像进行分级,较为清凉的图像独立管理
* 勇士们可使用 `#喵喵设置小清新开启` 启用
* 对增量包内的角色图像进行分级,较为清凉的图像独立管理
* 勇士们可使用 `#喵喵设置小清新开启` 启用
* 伤害计算增加扩散、感电的计算逻辑,感谢 **@49631073**的逻辑梳理
* `#角色面板` 伤害计算增加部分角色,目前支持
* 长柄武器:雷神、胡桃、魈、钟离、香菱
* 法器:神子、心海、可莉、凝光、芭芭拉、莫娜
* 弓:甘雨、宵宫、公子,九条,迪奥娜、安柏、皇女、温迪、夜兰
* 单手剑:绫人、绫华、刻晴、阿贝多、行秋、班尼特、七七、凯亚、琴、万叶ⁿᵉʷ、久岐忍ⁿᵉʷ
* 双手剑:一斗、优菈、迪卢克、诺艾尔、重云
* 长柄武器:雷神、胡桃、魈、钟离、香菱
* 法器:神子、心海、可莉、凝光、芭芭拉、莫娜
* 弓:甘雨、宵宫、公子,九条,迪奥娜、安柏、皇女、温迪、夜兰
* 单手剑:绫人、绫华、刻晴、阿贝多、行秋、班尼特、七七、凯亚、琴、万叶ⁿᵉʷ、久岐忍ⁿᵉʷ
* 双手剑:一斗、优菈、迪卢克、诺艾尔、重云
# 1.7.0
* `#更新面板` 功能升级
* 该功能可直接使用不再需要token
* 在查询新用户时会自动使用自动使用的CD 12小时
* 支持国际服UID目前暂不支持2及5开头的UID
* 服务来自enka api部分网络可能无法请求请科学处理后续会增加转发服务。
* 由于服务逻辑与之前数据不一致,部分角色的属性及伤害计算可能会不准确,如有发现请反馈给喵喵
* 该功能可直接使用不再需要token
* 在查询新用户时会自动使用自动使用的CD 12小时
* 支持国际服UID目前暂不支持2及5开头的UID
* 服务来自enka api部分网络可能无法请求请科学处理后续会增加转发服务。
* 由于服务逻辑与之前数据不一致,部分角色的属性及伤害计算可能会不准确,如有发现请反馈给喵喵
* `#面板`、`#更新面板`、`#角色面板`、`#角色伤害`、`#圣遗物列表`不再需要绑定cookie支持查他人
* 使用`#面板`命令可查看已获取面板数据的角色列表
* 默认查询自己UID同时也可通过命令+uid方式指定查询对象
* 由于整体逻辑变化喵喵1.6.0之前更新的面板数据无法查看,需要重新更新数据
* 使用`#面板`命令可查看已获取面板数据的角色列表
* 默认查询自己UID同时也可通过命令+uid方式指定查询对象
* 由于整体逻辑变化喵喵1.6.0之前更新的面板数据无法查看,需要重新更新数据
* 增加`#喵喵面板设置`命令,可更精细的设置是否允许好友/临时对话/群使用面板功能
* 由`#录入xx面板` 录入的数据暂时屏蔽
* `#角色面板`、`#喵喵日历` 部分细节样式调整
* `#角色面板` 伤害计算增加部分角色,目前支持
* 长柄武器:雷神、胡桃、魈、钟离、香菱
* 法器:神子、心海、可莉、凝光、芭芭拉、莫娜ⁿᵉʷ
* 弓:甘雨、宵宫、公子,九条,迪奥娜、安柏、皇女ⁿᵉʷ、温迪ⁿᵉʷ、夜兰ⁿᵉʷ
* 单手剑:绫人、绫华、刻晴、阿贝多、行秋、班尼特、七七、凯亚、琴ⁿᵉʷ
* 双手剑:一斗、优菈、迪卢克、诺艾尔、重云
* 长柄武器:雷神、胡桃、魈、钟离、香菱
* 法器:神子、心海、可莉、凝光、芭芭拉、莫娜ⁿᵉʷ
* 弓:甘雨、宵宫、公子,九条,迪奥娜、安柏、皇女ⁿᵉʷ、温迪ⁿᵉʷ、夜兰ⁿᵉʷ
* 单手剑:绫人、绫华、刻晴、阿贝多、行秋、班尼特、七七、凯亚、琴ⁿᵉʷ
* 双手剑:一斗、优菈、迪卢克、诺艾尔、重云
# 1.6.0
* `#喵喵设置` 支持设置 面板查询 的功能开关
* `#喵喵版本` 使用图片展示更新信息
* `#喵喵日历` 升级
* 增加 `#喵喵日历列表`命令,以列表形式展示活动信息
* 增加从活动详情信息中解析活动日期的逻辑,使一些活动日期更加准确
* 增加 `#喵喵日历列表`命令,以列表形式展示活动信息
* 增加从活动详情信息中解析活动日期的逻辑,使一些活动日期更加准确
* 增加鹿野院平藏的角色信息,可通过`#平藏天赋`、`#平藏命座`查看信息
* 其他升级调整
* `#深渊出场率`、`#角色持有率` 等页面功能及样式微调
* `#角色面板` 伤害计算增加双手剑计算逻辑,增加物伤计算逻辑
* 页面版权信息展示Yunzai及喵喵版本号
* `#深渊出场率`、`#角色持有率` 等页面功能及样式微调
* `#角色面板` 伤害计算增加双手剑计算逻辑,增加物伤计算逻辑
* 页面版权信息展示Yunzai及喵喵版本号
* `#角色面板` 伤害计算增加部分角色,目前支持
* 长柄武器:雷神、胡桃、魈、钟离、香菱
* 法器:神子、心海、可莉ⁿᵉʷ、凝光ⁿᵉʷ、芭芭拉ⁿᵉʷ
* 弓:甘雨、宵宫、公子,九条ⁿᵉʷ,迪奥娜ⁿᵉʷ、安柏ⁿᵉʷ
* 单手剑:绫人、绫华、刻晴、阿贝多、行秋、班尼特、七七ⁿᵉʷ、凯亚ⁿᵉʷ
* 双手剑:一斗ⁿᵉʷ、优菈ⁿᵉʷ、迪卢克ⁿᵉʷ、诺艾尔ⁿᵉʷ、重云ⁿᵉʷ
* 长柄武器:雷神、胡桃、魈、钟离、香菱
* 法器:神子、心海、可莉ⁿᵉʷ、凝光ⁿᵉʷ、芭芭拉ⁿᵉʷ
* 弓:甘雨、宵宫、公子,九条ⁿᵉʷ,迪奥娜ⁿᵉʷ、安柏ⁿᵉʷ
* 单手剑:绫人、绫华、刻晴、阿贝多、行秋、班尼特、七七ⁿᵉʷ、凯亚ⁿᵉʷ
* 双手剑:一斗ⁿᵉʷ、优菈ⁿᵉʷ、迪卢克ⁿᵉʷ、诺艾尔ⁿᵉʷ、重云ⁿᵉʷ
# 1.5.0
* 增加`#喵喵日历` 功能
* 【请注意】此功能需要安装moment库请在Yunzai安装目录下运行`npm install moment`后再进行升级
* 展示当前进行中及即将开始的活动,包括深境螺旋
* 【请注意】此功能需要安装moment库请在Yunzai安装目录下运行`npm install moment`后再进行升级
* 展示当前进行中及即将开始的活动,包括深境螺旋
* `#角色面板` 伤害计算目前支持
* 长柄武器:雷神、胡桃、魈、钟离、香菱ⁿᵉʷ
* 法器:神子、心海
* 弓:甘雨、宵宫、公子
* 单手剑:绫人、绫华、刻晴、阿贝多ⁿᵉʷ、行秋ⁿᵉʷ、班尼特ⁿᵉʷ
* 长柄武器:雷神、胡桃、魈、钟离、香菱ⁿᵉʷ
* 法器:神子、心海
* 弓:甘雨、宵宫、公子
* 单手剑:绫人、绫华、刻晴、阿贝多ⁿᵉʷ、行秋ⁿᵉʷ、班尼特ⁿᵉʷ
* 底层升级抽象了部分公共组件为tpl模板以提高复用度css改为less处理
# 1.4.0
* 增加 `#深渊配队` 功能
* 根据当前账号的角色练度及本期深渊出场数据,推荐较匹配的配队方案
* 深渊出场数据来自胡桃API为Snap Genshin用户自主上传的深渊挑战记录感谢SG团队
* 配队方案仅供参考
* 根据当前账号的角色练度及本期深渊出场数据,推荐较匹配的配队方案
* 深渊出场数据来自胡桃API为Snap Genshin用户自主上传的深渊挑战记录感谢SG团队
* 配队方案仅供参考
* `#角色面板` 伤害计算新增部分角色
* 目前支持:雷神、胡桃、魈、神子、甘雨、宵宫、公子、绫人、绫华、心海、钟离
* 目前支持:雷神、胡桃、魈、神子、甘雨、宵宫、公子、绫人、绫华、心海、钟离
* `#角色面板` 一些功能升级与调整
* 支持对治疗量、护盾量的计算与展示
* 修复冰融化、少女4等buff等buff遗漏或错误导致的伤害计算偏差
* 支持对治疗量、护盾量的计算与展示
* 修复冰融化、少女4等buff等buff遗漏或错误导致的伤害计算偏差
* `#老婆` 功能支持对jpeg格式的图片格式识别
# 1.3.0
* 增加 `#雷神伤害` 功能
* 可计算圣遗物副词条置换带来的伤害变化,可用于圣遗物副词条侧重方向的参考
* 可以查看指定角色伤害计算的Buff列表
* 可计算圣遗物副词条置换带来的伤害变化,可用于圣遗物副词条侧重方向的参考
* 可以查看指定角色伤害计算的Buff列表
* `#角色面板` 伤害计算新增部分角色
* 目前支持:雷神、胡桃、魈、神子、甘雨、宵宫、公子、绫人、绫华
* 目前支持:雷神、胡桃、魈、神子、甘雨、宵宫、公子、绫人、绫华
* `#角色面板` 功能升级
* 优化无角色面板数据时的引导
* 优化返回的图像格式及分辨率,平衡响应速度及显示效果
* 优化无角色面板数据时的引导
* 优化返回的图像格式及分辨率,平衡响应速度及显示效果
* 增加 `#圣遗物列表` 功能,对已经获取面板的所有角色圣遗物进行评分,并展示高评分的圣遗物列表
* 增加 `#角色面板列表` / `#角色面板帮助` 命令
* 增加 `#更新胡桃面板` 命令获取单个角色面板数据每天可更新5次
@ -152,15 +156,15 @@
# 1.2.0
* `#角色面板` 增加伤害计算功能
* 目前支持角色:雷神、胡桃、魈、神子、甘雨
* 可通过 `#怪物等级85` 命令设定怪物等级,以获得更准确的计算结果
* 计算伤害为满Buff情况后续会出更详细的Buff及计算展示
* 目前支持角色:雷神、胡桃、魈、神子、甘雨
* 可通过 `#怪物等级85` 命令设定怪物等级,以获得更准确的计算结果
* 计算伤害为满Buff情况后续会出更详细的Buff及计算展示
* `#获取游戏角色详情`命令在服务侧增加基于UID的天频度限制
* 增加 `#喵喵更新` 功能
* 感谢 @碎月 @清秋 的代码支持
* 若更新成功会重启Yunzai需要Yunzai以 npm run start 模式启动
* 尚未经充分测试,请有一定容错能力的勇士尝试
* 增加`#喵喵版本`命令查询版本信息
* 感谢 @碎月 @清秋 的代码支持
* 若更新成功会重启Yunzai需要Yunzai以 npm run start 模式启动
* 尚未经充分测试,请有一定容错能力的勇士尝试
* 增加`#喵喵版本`命令查询版本信息
# 1.1.0

44
adapter/index.js Normal file
View File

@ -0,0 +1,44 @@
import plugin from '../../../lib/plugins/plugin.js'
import * as Miao from '../apps/index.js'
import { render } from './render.js'
import { checkAuth, getMysApi } from './mys.js'
export class miao extends plugin {
constructor () {
super({
name: 'miao-plugin',
desc: '喵喵插件',
event: 'message',
priority: 50,
rule: [{
reg: '.+',
fnc: 'dispatch'
}]
})
}
async dispatch (e) {
let msg = e.raw_message
e.checkAuth = async function (cfg) {
return await checkAuth(e, cfg)
}
e.getMysApi = async function (cfg) {
return await getMysApi(e, cfg)
}
msg = '#' + msg.replace('#', '')
for (let fn in Miao.rule) {
let cfg = Miao.rule[fn]
if (Miao[fn] && new RegExp(cfg.reg).test(msg)) {
let ret = await Miao[fn](e, {
render
})
if (ret === true) {
console.log('ret true')
return true
}
}
}
return false
}
}

114
adapter/mys.js Normal file
View File

@ -0,0 +1,114 @@
import MysInfo from '../../genshin/model/mys/mysInfo.js'
import lodash from 'lodash'
class User {
constructor (cfg) {
this.id = cfg.id
this.uid = cfg.uid
this.cookie = ''
}
// 保存用户配置
async setCfg (path, value) {
console.log(this.id)
let userCfg = await redis.get(`genshin:user-cfg:${this.id}`)
userCfg = userCfg ? JSON.parse(userCfg) : {}
lodash.set(userCfg, path, value)
await redis.set(`genshin:user-cfg:${this.id}`, JSON.stringify(userCfg))
}
/* 获取用户配置 */
async getCfg (path, defaultValue) {
let userCfg = await redis.get(`genshin:user-cfg:${this.id}`)
userCfg = userCfg ? JSON.parse(userCfg) : {}
return lodash.get(userCfg, path, defaultValue)
}
async getMysUser () {
return {
uid: this.uid
}
}
}
class Mys {
constructor (e, uid, MysApi) {
this.selfUser = new User({ id: e.user_id, uid })
this.targetUser = {
uid
}
this.e = e
this.MysApi = MysApi
e.targetUser = this.targetUser
e.selfUser = this.selfUser
}
async getData (api, data) {
if (!this.MysApi) {
return false
}
let ret = await MysInfo.get(this.e, api, data)
if (!ret) {
return false
}
return ret.data || ret
}
// 获取角色信息
async getCharacter () {
return await this.getData('character')
}
// 获取角色详情
async getAvatar (id) {
return await this.getData('detail', { avatar_id: id })
}
// 首页宝箱信息
async getIndex () {
return await this.getData('index')
}
// 获取深渊信息
async getSpiralAbyss (type = 1) {
return await this.getData('spiralAbyss', { schedule_type: type })
}
async getDetail (id) {
return await this.getData('detail', { avatar_id: id })
}
async getCompute (data) {
return await this.getData('compute', data)
}
async getAvatarSkill (id) {
return await this.getData('avatarSkill', { avatar_id: id })
}
get isSelfCookie () {
return true
}
}
export async function getMysApi (e, cfg) {
let { auth = 'all' } = cfg
let uid = await MysInfo.getUid(e)
if (!uid) return false
/* 检查user ck */
let isCookieUser = await MysInfo.checkUidBing(uid)
if (auth === 'cookie' && !isCookieUser) {
e.reply('尚未绑定Cookie...')
return false
}
let MysApi = await MysInfo.init(e, 'roleIndex')
if (!MysApi) {
return false
}
return new Mys(e, uid, MysApi)
}
export async function checkAuth (e, cfg) {
return new User({ id: e.user_id })
}

21
adapter/render.js Normal file
View File

@ -0,0 +1,21 @@
import lodash from 'lodash'
import Data from '../components/Data.js'
import puppeteer from '../../../lib/puppeteer/puppeteer.js'
const plugin = 'miao-plugin'
const _path = process.cwd()
export async function render (app = '', tpl = '', data = {}, imgType = 'jpeg') {
// 在data中保存plugin信息
data._plugin = plugin
if (lodash.isUndefined(data._res_path)) {
data._res_path = `../../../../../plugins/${plugin}/resources/`
}
Data.createDir(_path + '/data/', `html/${plugin}/${app}/${tpl}`)
data.saveId = data.saveId || data.save_id || tpl
data.tplFile = `./plugins/${plugin}/resources/${app}/${tpl}.html`
data.pluResPath = data._res_path
return await puppeteer.screenshot(`miao-plugin/${app}/${tpl}`, data)
}

View File

@ -1,242 +1,235 @@
import { segment } from "oicq";
import fs from "fs";
import lodash from "lodash";
import { createRequire } from "module";
import { exec } from "child_process";
import { Cfg } from "../components/index.js";
import Common from "../components/Common.js";
const require = createRequire(import.meta.url);
import fs from 'fs'
import lodash from 'lodash'
import { exec } from 'child_process'
import { Cfg } from '../components/index.js'
import Common from '../components/Common.js'
let cfgMap = {
"角色": "char.char",
"面板": "char.profile-data",
"老婆": "char.wife",
"戳一戳": "char.poke",
"小清新": "char.se",
"查他人": "char.queryOther",
"图鉴": "wiki.wiki",
"图片": "wiki.pic",
"深渊": "wiki.abyss",
"渲染": "sys.scale",
"帮助": "sys.help",
};
let sysCfgReg = `^#喵喵设置\s*(${lodash.keys(cfgMap).join("|")})?\s*(.*)$`;
角色: 'char.char',
面板: 'char.profile-data',
老婆: 'char.wife',
戳一戳: 'char.poke',
小清新: 'char.se',
查他人: 'char.queryOther',
图鉴: 'wiki.wiki',
图片: 'wiki.pic',
深渊: 'wiki.abyss',
渲染: 'sys.scale',
帮助: 'sys.help'
}
let sysCfgReg = `^#喵喵设置\\s*(${lodash.keys(cfgMap).join('|')})?\\s*(.*)$`
export const rule = {
updateRes: {
hashMark: true,
reg: "^#喵喵(更新图像|图像更新)$",
describe: "【#管理】更新素材",
reg: '^#喵喵(更新图像|图像更新)$',
describe: '【#管理】更新素材'
},
updateMiaoPlugin: {
hashMark: true,
reg: "^#喵喵(强制)?更新",
describe: "【#管理】喵喵更新",
reg: '^#喵喵(强制)?更新',
describe: '【#管理】喵喵更新'
},
sysCfg: {
hashMark: true,
reg: sysCfgReg,
describe: "【#管理】系统设置"
describe: '【#管理】系统设置'
},
profileCfg: {
hashMark: true,
reg: "^#喵喵面板(?:设置)?.*",
describe: "【#管理】面板设置"
reg: '^#喵喵面板(?:设置)?.*',
describe: '【#管理】面板设置'
}
};
}
const _path = process.cwd();
const resPath = `${_path}/plugins/miao-plugin/resources/`;
const plusPath = `${resPath}/miao-res-plus/`;
const _path = process.cwd()
const resPath = `${_path}/plugins/miao-plugin/resources/`
const plusPath = `${resPath}/miao-res-plus/`
const checkAuth = async function (e) {
return await e.checkAuth({
auth: "master",
auth: 'master',
replyMsg: `只有主人才能命令喵喵哦~
(*/ω*)`
});
})
}
export async function sysCfg(e, { render }) {
export async function sysCfg (e, { render }) {
if (!await checkAuth(e)) {
return true;
return true
}
let cfgReg = new RegExp(sysCfgReg);
let regRet = cfgReg.exec(e.msg);
let cfgReg = new RegExp(sysCfgReg)
let regRet = cfgReg.exec(e.msg)
if (!regRet) {
return true;
return true
}
if (regRet[1]) {
// 设置模式
let val = regRet[2] || "";
let val = regRet[2] || ''
let cfgKey = cfgMap[regRet[1]];
if (cfgKey === "sys.scale") {
val = Math.min(200, Math.max(50, val * 1 || 100));
let cfgKey = cfgMap[regRet[1]]
if (cfgKey === 'sys.scale') {
val = Math.min(200, Math.max(50, val * 1 || 100))
} else {
val = !/关闭/.test(val);
val = !/关闭/.test(val)
}
if (cfgKey) {
Cfg.set(cfgKey, val);
Cfg.set(cfgKey, val)
}
}
let cfg = {
chars: getStatus("char.char"),
profile: getStatus("char.profile-data"),
wife: getStatus("char.wife"),
poke: getStatus("char.poke"),
se: getStatus("char.se", false),
other: getStatus("char.queryOther"),
wiki: getStatus("wiki.wiki"),
pic: getStatus("wiki.pic"),
abyss: getStatus("wiki.abyss"),
chars: getStatus('char.char'),
profile: getStatus('char.profile-data'),
wife: getStatus('char.wife'),
poke: getStatus('char.poke'),
se: getStatus('char.se', false),
other: getStatus('char.queryOther'),
wiki: getStatus('wiki.wiki'),
pic: getStatus('wiki.pic'),
abyss: getStatus('wiki.abyss'),
imgPlus: fs.existsSync(plusPath),
help: getStatus("sys.help", false),
scale: Cfg.get("sys.scale", 100)
help: getStatus('sys.help', false),
scale: Cfg.get('sys.scale', 100)
}
//渲染图像
return await Common.render("admin/index", {
...cfg,
}, { e, render, scale: 1.4 });
// 渲染图像
return await Common.render('admin/index', {
...cfg
}, { e, render, scale: 1.4 })
}
const getStatus = function (rote, def = true) {
if (Cfg.get(rote, def)) {
return `<div class="cfg-status" >已开启</div>`;
return '<div class="cfg-status" >已开启</div>'
} else {
return `<div class="cfg-status status-off">已关闭</div>`;
return '<div class="cfg-status status-off">已关闭</div>'
}
}
export async function updateRes(e) {
export async function updateRes (e) {
if (!await checkAuth(e)) {
return true;
return true
}
let command = "";
let command = ''
if (fs.existsSync(`${resPath}/miao-res-plus/`)) {
e.reply("开始尝试更新,请耐心等待~");
command = `git pull`;
e.reply('开始尝试更新,请耐心等待~')
command = 'git pull'
exec(command, { cwd: `${resPath}/miao-res-plus/` }, function (error, stdout, stderr) {
console.log(stdout);
console.log(stdout)
if (/(Already up[ -]to[ -]date|已经是最新的)/.test(stdout)) {
e.reply("目前所有图片都已经是最新了~");
return true;
e.reply('目前所有图片都已经是最新了~')
return true
}
let numRet = /(\d*) files changed,/.exec(stdout);
let numRet = /(\d*) files changed,/.exec(stdout)
if (numRet && numRet[1]) {
e.reply(`报告主人,更新成功,此次更新了${numRet[1]}个图片~`);
return true;
e.reply(`报告主人,更新成功,此次更新了${numRet[1]}个图片~`)
return true
}
if (error) {
e.reply("更新失败!\nError code: " + error.code + "\n" + error.stack + "\n 请稍后重试。");
e.reply('更新失败!\nError code: ' + error.code + '\n' + error.stack + '\n 请稍后重试。')
} else {
e.reply("图片加量包更新成功~");
e.reply('图片加量包更新成功~')
}
});
})
} else {
command = `git clone https://gitee.com/yoimiya-kokomi/miao-res-plus.git "${resPath}/miao-res-plus/"`;
e.reply("开始尝试安装图片加量包,可能会需要一段时间,请耐心等待~");
command = `git clone https://gitee.com/yoimiya-kokomi/miao-res-plus.git "${resPath}/miao-res-plus/"`
e.reply('开始尝试安装图片加量包,可能会需要一段时间,请耐心等待~')
exec(command, function (error, stdout, stderr) {
if (error) {
e.reply("角色图片加量包安装失败!\nError code: " + error.code + "\n" + error.stack + "\n 请稍后重试。");
e.reply('角色图片加量包安装失败!\nError code: ' + error.code + '\n' + error.stack + '\n 请稍后重试。')
} else {
e.reply("角色图片加量包安装成功!您后续也可以通过 #喵喵更新图像 命令来更新图像");
e.reply('角色图片加量包安装成功!您后续也可以通过 #喵喵更新图像 命令来更新图像')
}
});
})
}
return true;
return true
}
let timer;
let timer
export async function updateMiaoPlugin(e) {
export async function updateMiaoPlugin (e) {
if (!await checkAuth(e)) {
return true;
return true
}
let isForce = e.msg.includes("强制");
let command = "git pull";
let isForce = e.msg.includes('强制')
let command = 'git pull'
if (isForce) {
command = "git checkout . && git pull";
e.reply("正在执行强制更新操作,请稍等");
command = 'git checkout . && git pull'
e.reply('正在执行强制更新操作,请稍等')
} else {
e.reply("正在执行更新操作,请稍等");
e.reply('正在执行更新操作,请稍等')
}
exec(command, { cwd: `${_path}/plugins/miao-plugin/` }, function (error, stdout, stderr) {
if (/(Already up[ -]to[ -]date|已经是最新的)/.test(stdout)) {
e.reply("目前已经是最新版喵喵了~");
return true;
e.reply('目前已经是最新版喵喵了~')
return true
}
if (error) {
e.reply("喵喵更新失败!\nError code: " + error.code + "\n" + error.stack + "\n 请稍后重试。");
return true;
e.reply('喵喵更新失败!\nError code: ' + error.code + '\n' + error.stack + '\n 请稍后重试。')
return true
}
e.reply("喵喵更新成功正在尝试重新启动Yunzai以应用更新...");
timer && clearTimeout(timer);
redis.set("miao:restart-msg", JSON.stringify({
msg: "重启成功,新版喵喵已经生效",
e.reply('喵喵更新成功正在尝试重新启动Yunzai以应用更新...')
timer && clearTimeout(timer)
redis.set('miao:restart-msg', JSON.stringify({
msg: '重启成功,新版喵喵已经生效',
qq: e.user_id
}), { EX: 30 });
}), { EX: 30 })
timer = setTimeout(function () {
let command = `npm run start`;
if (process.argv[1].includes("pm2")) {
command = `npm run restart`;
let command = 'npm run start'
if (process.argv[1].includes('pm2')) {
command = 'npm run restart'
}
exec(command, function (error, stdout, stderr) {
if (error) {
e.reply("自动重启失败,请手动重启以应用新版喵喵。\nError code: " + error.code + "\n" + error.stack + "\n");
Bot.logger.error('重启失败\n${error.stack}');
return true;
e.reply('自动重启失败,请手动重启以应用新版喵喵。\nError code: ' + error.code + '\n' + error.stack + '\n')
Bot.logger.error(`重启失败\n${error.stack}`)
return true
} else if (stdout) {
Bot.logger.mark("重启成功运行已转为后台查看日志请用命令npm run log");
Bot.logger.mark("停止后台运行命令npm stop");
process.exit();
Bot.logger.mark('重启成功运行已转为后台查看日志请用命令npm run log')
Bot.logger.mark('停止后台运行命令npm stop')
process.exit()
}
})
}, 1000);
});
return true;
}, 1000)
})
return true
}
export async function profileCfg(e, { render }) {
export async function profileCfg (e, { render }) {
if (!await checkAuth(e)) {
return true;
return true
}
let keyMap = {
"好友": "friend",
"群": "group",
"陌生人": "stranger"
好友: 'friend',
: 'group',
陌生人: 'stranger'
}
let regRet = /喵喵面板(?:设置)?\s*(好友|群|群聊|陌生人)?\s*(\d*)\s*(开启|关闭|删除)?\s*$/.exec(e.msg);
let regRet = /喵喵面板(?:设置)?\s*(好友|群|群聊|陌生人)?\s*(\d*)\s*(开启|关闭|删除)?\s*$/.exec(e.msg)
if (!regRet) {
return;
return
}
let [, target, groupId, actionType] = regRet;
if (target === "群聊") {
target = "群";
let [, target, groupId, actionType] = regRet
if (target === '群聊') {
target = '群'
}
if (target) {
if (groupId && (target === "群" || !target)) {
if (actionType === "删除") {
Cfg.del(`profile.groups.群${groupId}`);
if (groupId && (target === '群' || !target)) {
if (actionType === '删除') {
Cfg.del(`profile.groups.群${groupId}`)
} else {
Cfg.set(`profile.groups.群${groupId}.status`, actionType !== "关闭");
Cfg.set(`profile.groups.群${groupId}.status`, actionType !== '关闭')
}
} else {
Cfg.set(`profile.${keyMap[target]}.status`, actionType !== "关闭");
Cfg.set(`profile.${keyMap[target]}.status`, actionType !== '关闭')
}
}
@ -246,13 +239,12 @@ export async function profileCfg(e, { render }) {
lodash.forEach(['friend', 'group', 'stranger'], (key) => {
cfg[key] = getStatus(`profile.${key}.status`, true)
});
})
let groups = Cfg.get('profile-data.groups', {});
let groups = Cfg.get('profile-data.groups', {})
lodash.forEach(lodash.keys(groups), (group, idx) => {
if (lodash.isUndefined(groups[group])) {
return;
return
}
cfg.groups.push({
group,
@ -261,9 +253,8 @@ export async function profileCfg(e, { render }) {
})
})
//渲染图像
return await Common.render("admin/profile", {
...cfg,
}, { e, render, scale: 1.4 });
// 渲染图像
return await Common.render('admin/profile', {
...cfg
}, { e, render, scale: 1.4 })
}

View File

@ -1,133 +1,119 @@
import lodash from "lodash";
import { Common, Cfg } from "../components/index.js";
import { renderAvatar } from "./character/avatar-card.js";
import { getTargetUid, getProfile, profileHelp, getProfileAll } from "./character/profile-common.js";
import { Common, Cfg } from '../components/index.js'
import { renderAvatar } from './character/avatar-card.js'
import { getTargetUid, getProfile, profileHelp, getProfileAll } from './character/profile-common.js'
import { profileArtis } from "./character/profile-artis.js"
import { renderProfile } from "./character/profile-detail.js";
import { profileArtis } from './character/profile-artis.js'
import { renderProfile } from './character/profile-detail.js'
export { enemyLv, getOriginalPicture } from "./character/utils.js";
import { Character } from '../components/models.js'
export { enemyLv, getOriginalPicture } from './character/utils.js'
// 角色图像上传
export { uploadCharacterImg } from "./character/character-img-upload.js";
export { uploadCharacterImg } from './character/character-img-upload.js'
//
export { getProfileAll, getProfile, profileHelp };
export { getProfileAll, getProfile, profileHelp }
// 圣遗物列表
export { profileArtisList } from "./character/profile-artis.js";
export { profileArtisList } from './character/profile-artis.js'
// 老婆
export { wife, pokeWife, wifeReg } from "./character/avatar-wife.js";
import { Character } from "../components/models.js";
export { wife, pokeWife, wifeReg } from './character/avatar-wife.js'
// 查看当前角色
export async function character(e, { render, User }) {
let msg = e.msg;
export async function character (e, { render }) {
let msg = e.raw_message || e.msg
if (!msg) {
return;
return
}
let mode = 'card';
let uidRet = /[0-9]{9}/.exec(msg);
let mode = 'card'
let uidRet = /[0-9]{9}/.exec(msg)
if (uidRet) {
e.uid = uidRet[0];
msg = msg.replace(uidRet[0], "");
e.uid = uidRet[0]
msg = msg.replace(uidRet[0], '')
}
let name = msg.replace(/#|老婆|老公/g, "").trim();
msg = msg.replace("面版", "面板")
let dmgRet = /伤害(\d?)$/.exec(name), dmgIdx = 0;
let name = msg.replace(/#|老婆|老公/g, '').trim()
msg = msg.replace('面版', '面板')
let dmgRet = /伤害(\d?)$/.exec(name);
let dmgIdx = 0
if (/(详情|详细|面板|面版)\s*$/.test(msg) && !/更新|录入|输入/.test(msg)) {
mode = 'profile';
name = name.replace(/(详情|详细|面板)/, "").trim();
mode = 'profile'
name = name.replace(/(详情|详细|面板)/, '').trim()
} else if (dmgRet) {
mode = 'dmg';
name = name.replace(/伤害[0-5]?/, "").trim();
mode = 'dmg'
name = name.replace(/伤害[0-5]?/, '').trim()
if (dmgRet[1]) {
dmgIdx = dmgRet[1] * 1;
dmgIdx = dmgRet[1] * 1
}
} else if (/(详情|详细|面板)更新$/.test(msg) || (/更新/.test(msg) && /(详情|详细|面板)$/.test(msg))) {
mode = "refresh";
name = name.replace(/详情|详细|面板|更新/g, "").trim();
mode = 'refresh'
name = name.replace(/详情|详细|面板|更新/g, '').trim()
} else if (/(录入|输入)/.test(msg) && /(详情|详细|面板)/.test(msg)) {
mode = "input";
let nameRet = /(?:录入|输入)(.+)(?:面板|详细|详情|数据)+/.exec(name);
mode = 'input'
let nameRet = /(?:录入|输入)(.+)(?:面板|详细|详情|数据)+/.exec(name)
if (nameRet) {
name = nameRet[1];
e.inputData = msg.replace(nameRet[0], "");
name = nameRet[1]
e.inputData = msg.replace(nameRet[0], '')
}
name = name.replace(/录入|输入|详情|详细|面板|数据|[0-9]|\.|\+/g, "").trim()
name = name.replace(/录入|输入|详情|详细|面板|数据|[0-9]|\.|\+/g, '').trim()
} else if (/圣遗物/.test(msg)) {
mode = "artis";
name = name.replace("圣遗物", "").trim();
mode = 'artis'
name = name.replace('圣遗物', '').trim()
}
if (mode === "card" && Common.isDisable(e, "char.char")) {
return;
if (mode === 'card' && Common.isDisable(e, 'char.char')) {
return
}
if (mode !== "card" && !e.isMaster) {
if (Common.isDisable(e, "char.profile")) {
if (mode !== 'card' && !e.isMaster) {
if (Common.isDisable(e, 'char.profile')) {
// 面板开关关闭
return;
return
}
if (e.isPrivate) {
if ((e.sub_type === "friend" && Cfg.get("profile.friend.status") === false) ||
(e.sub_type === "group" && Cfg.get("profile.stranger.status") === false)) {
return;
if ((e.sub_type === 'friend' && Cfg.get('profile.friend.status') === false) ||
(e.sub_type === 'group' && Cfg.get('profile.stranger.status') === false)) {
return
}
} else if (e.isGroup) {
let groupCfg = Cfg.get(`profile.groups.群${e.group_id}.status`);
if (groupCfg === false || (groupCfg !== true && Cfg.get(`profile.group.status`) === false)) {
return;
let groupCfg = Cfg.get(`profile.groups.群${e.group_id}.status`)
if (groupCfg === false || (groupCfg !== true && Cfg.get('profile.group.status') === false)) {
return
}
}
}
let char = Character.get(name.trim());
let char = Character.get(name.trim())
if (!char) {
return false;
return false
}
if (mode === "card") {
return renderAvatar(e, char.name, render);
if (mode === 'card') {
return renderAvatar(e, char.name, render)
}
let uid = await getTargetUid(e);
let uid = await getTargetUid(e)
if (!uid) {
return true;
return true
}
e.uid = uid;
e.avatar = char.id;
e.uid = uid
e.avatar = char.id
if (char.isCustom) {
e.reply("自定义角色暂不支持此功能");
return true;
e.reply('自定义角色暂不支持此功能')
return true
}
if (mode === "profile" || mode === "dmg") {
return renderProfile(e, char, render, mode, { dmgIdx });
} else if (mode === "refresh" || mode === "input") {
await getProfile(e, mode);
return true;
} else if (mode === "artis") {
return profileArtis(e, { render });
if (mode === 'profile' || mode === 'dmg') {
return renderProfile(e, char, render, mode, { dmgIdx })
} else if (mode === 'refresh' || mode === 'input') {
await getProfile(e, mode)
return true
} else if (mode === 'artis') {
return profileArtis(e, { render })
}
return true;
return true
}

View File

@ -1,240 +1,227 @@
import { Artifact, Character } from "../../components/models.js";
import { Cfg, Data, Common, Profile } from "../../components/index.js";
import lodash from "lodash";
import { segment } from "oicq";
import { Artifact, Character } from '../../components/models.js'
import { Cfg, Data, Common, Profile } from '../../components/index.js'
import lodash from 'lodash'
import { segment } from 'oicq'
//角色昵称
let nameID = "";
let genshin = {};
await init();
// 角色昵称
export async function init(isUpdate = false) {
let _path = "file://" + process.cwd();
let version = isUpdate ? new Date().getTime() : 0;
genshin = await import(_path + `/config/genshin/roleId.js?version=${version}`);
nameID = "";
}
export async function renderAvatar(e, avatar, render, renderType = "card") {
let abbr = Character.getAbbr()
export async function renderAvatar (e, avatar, render, renderType = 'card') {
// 如果传递的是名字,则获取
if (typeof (avatar) === "string") {
let char = Character.get(avatar);
if (typeof (avatar) === 'string') {
let char = Character.get(avatar)
if (!char) {
return false;
return false
}
let MysApi = await e.getMysApi({
auth: "all",
targetType: Cfg.get("char.queryOther", true) ? "all" : "self",
cookieType: "all",
actionName: "查询信息"
});
auth: 'all',
targetType: Cfg.get('char.queryOther', true) ? 'all' : 'self',
cookieType: 'all',
actionName: '查询信息'
})
if (!MysApi) return true;
if (!MysApi) return true
let uid = e.targetUser.uid;
let uid = MysApi.targetUser.uid
if (char.isCustom) {
avatar = { id: char.id, name: char.name, detail: false }
} else {
let profile = await Profile.get(uid, char.id, true);
let profile = await Profile.get(uid, char.id, true)
if (profile) {
// 优先使用Profile数据
avatar = profile;
avatar = profile
} else {
// 使用Mys数据兜底
let charData = await MysApi.getCharacter();
if (!charData) return true;
let charData = await MysApi.getCharacter()
if (!charData) return true
let avatars = charData.avatars;
let length = avatars.length;
char.checkAvatars(avatars);
avatars = lodash.keyBy(avatars, "id");
avatar = avatars[char.id] || { id: char.id, name: char.name, detail: false };
let avatars = charData.avatars
char.checkAvatars(avatars)
avatars = lodash.keyBy(avatars, 'id')
avatar = avatars[char.id] || { id: char.id, name: char.name, detail: false }
}
}
}
return await renderCard(e, avatar, render, renderType);
return await renderCard(e, avatar, render, renderType)
}
// 渲染角色卡片
async function renderCard(e, avatar, render, renderType = "card") {
let char = Character.get(avatar);
async function renderCard (e, avatar, render, renderType = 'card') {
let char = Character.get(avatar)
if (!char) {
return false;
return false
}
let uid = e.uid || (e.targetUser && e.targetUser.uid);
let uid = e.uid || (e.targetUser && e.targetUser.uid)
let crownNum = 0, talent = {};
let crownNum = 0;
let talent = {}
if (!char.isCustom) {
talent = await getTalent(e, avatar);
talent = await getTalent(e, avatar)
// 计算皇冠个数
crownNum = lodash.filter(lodash.map(talent, (d) => d.original), (d) => d >= 10).length;
crownNum = lodash.filter(lodash.map(talent, (d) => d.original), (d) => d >= 10).length
}
let bg = char.getCardImg(Cfg.get("char.se", false));
if (renderType === "photo") {
e.reply(segment.image(process.cwd() + "/plugins/miao-plugin/resources/" + bg.img));
let bg = char.getCardImg(Cfg.get('char.se', false))
if (renderType === 'photo') {
e.reply(segment.image(process.cwd() + '/plugins/miao-plugin/resources/' + bg.img))
} else {
//渲染图像
let msgRes = await Common.render("character/card", {
// 渲染图像
let msgRes = await Common.render('character/card', {
save_id: uid,
uid,
talent,
crownNum,
talentMap: { a: "普攻", e: "战技", q: "爆发" },
talentMap: { a: '普攻', e: '战技', q: '爆发' },
bg,
custom: char.isCustom,
...getCharacterData(avatar),
ds: char.getData("name,id,title,desc"),
}, { e, render, scale: 1.6 });
ds: char.getData('name,id,title,desc')
}, { e, render, scale: 1.6 })
if (msgRes && msgRes.message_id) {
// 如果消息发送成功就将message_id和图片路径存起来1小时过期
await redis.set(`miao:original-picture:${msgRes.message_id}`, bg.img, { EX: 3600 });
await redis.set(`miao:original-picture:${msgRes.message_id}`, bg.img, { EX: 3600 })
}
return true;
return true
}
return true;
return true
}
//获取角色技能数据
async function getTalent(e, avatars) {
let talent = {}, cons = 0, char = Character.get(avatars.id), mode = "level";
// 获取角色技能数据
async function getTalent (e, avatars) {
let talent = {};
let cons = 0;
let char = Character.get(avatars.id);
let mode = 'level'
if (char.isCustom) {
return {}
}
if (avatars.dataSource && avatars.talent) {
// profile模式
talent = avatars.talent || {};
cons = avatars.cons || 0;
talent = avatars.talent || {}
cons = avatars.cons || 0
} else {
let MysApi = await e.getMysApi({
auth: "all",
targetType: Cfg.get("char.queryOther", true) ? "all" : "self",
cookieType: "all",
actionName: "查询信息"
});
if (!MysApi && !MysApi.isSelfCookie) return {};
let skillRes = await MysApi.getAvatar(avatars.id);
auth: 'all',
targetType: Cfg.get('char.queryOther', true) ? 'all' : 'self',
cookieType: 'all',
actionName: '查询信息'
})
if (!MysApi && !MysApi.isSelfCookie) return {}
let skillRes = await MysApi.getAvatar(avatars.id)
cons = avatars.actived_constellation_num
mode = "original";
mode = 'original'
if (skillRes && skillRes.skill_list) {
let skill_list = lodash.orderBy(skillRes.skill_list, ["id"], ["asc"]);
for (let val of skill_list) {
if (val.name.includes("普通攻击")) {
talent.a = val;
continue;
let skillList = lodash.orderBy(skillRes.skill_list, ['id'], ['asc'])
for (let val of skillList) {
if (val.name.includes('普通攻击')) {
talent.a = val
continue
}
if (val.max_level >= 10 && !talent.e) {
talent.e = val;
continue;
talent.e = val
continue
}
if (val.max_level >= 10 && !talent.q) {
talent.q = val;
talent.q = val
}
}
}
}
return char.getAvatarTalent(talent, cons, mode);
return char.getAvatarTalent(talent, cons, mode)
}
/*
* 获取角色数据
* */
function getCharacterData(avatars) {
let list = [];
let set = {};
let artiEffect = [];
let w = avatars.weapon || {};
function getCharacterData (avatars) {
let list = []
let set = {}
let artiEffect = []
let w = avatars.weapon || {}
let weapon = {
type: "weapon",
name: w.name || "",
showName: genshin.abbr[w.name] || w.name || "",
type: 'weapon',
name: w.name || '',
showName: abbr[w.name] || w.name || '',
level: w.level || 1,
affix: w.affix || w.affix_level || 0
}
let artis = avatars.artis || avatars.reliquaries;
let artis = avatars.artis || avatars.reliquaries
if (artis) {
lodash.forEach(artis, (val) => {
let setCfg = Artifact.getSetByArti(val.name);
let setCfg = Artifact.getSetByArti(val.name)
if (!setCfg) {
return;
return
}
let setName = setCfg.name;
let setName = setCfg.name
if (set[setName]) {
set[setName]++;
set[setName]++
if (set[setName] === 2) {
artiEffect.push("2件套" + setCfg.effect['2'])
artiEffect.push('2件套' + setCfg.effect['2'])
}
if (set[setName] === 4) {
artiEffect.push("4件套" + setCfg.name);
artiEffect.push('4件套' + setCfg.name)
}
} else {
set[setName] = 1;
set[setName] = 1
}
})
if (artiEffect.length === 0) {
artiEffect = ["无套装效果"];
artiEffect = ['无套装效果']
}
}
if (avatars.id == "10000005") {
avatars.name = "空";
} else if (avatars.id == "10000007") {
avatars.name = "荧";
if (avatars.id == '10000005') {
avatars.name = '空'
} else if (avatars.id == '10000007') {
avatars.name = '荧'
}
let reliquaries = list[0];
let reliquaries = list[0]
return {
name: avatars.name,
showName: genshin.abbr[avatars.name] ? genshin.abbr[avatars.name] : avatars.name,
showName: abbr[avatars.name] ? abbr[avatars.name] : avatars.name,
level: Data.def(avatars.lv, avatars.level),
fetter: avatars.fetter,
cons: Data.def(avatars.cons, avatars.actived_constellation_num),
weapon,
artiEffect,
reliquaries
};
}
}
export async function getAvatarList(e, type, MysApi) {
let data = await MysApi.getCharacter();
if (!data) return false;
export async function getAvatarList (e, type, MysApi) {
let data = await MysApi.getCharacter()
if (!data) return false
let avatars = data.avatars;
let avatars = data.avatars
if (avatars.length <= 0) {
return false;
return false
}
let list = [];
let list = []
for (let val of avatars) {
if (type !== false) {
if (!genshin.wifeData[type].includes(Number(val.id))) {
continue;
if (!Character.checkWifeType(val.id, type)) {
continue
}
}
if (val.rarity > 5) {
val.rarity = 5;
val.rarity = 5
}
list.push(val);
list.push(val)
}
if (list.length <= 0) {
return false;
return false
}
let sortKey = "level,fetter,weapon_level,rarity,weapon_rarity,cons,weapon_affix_level";
list = lodash.orderBy(list, sortKey, lodash.repeat("desc,", sortKey.length).split(","));
return list;
}
export function checkWifeType(id, type) {
return Chargenshin.wifeData[type].includes(Number(id));
let sortKey = 'level,fetter,weapon_level,rarity,weapon_rarity,cons,weapon_affix_level'
list = lodash.orderBy(list, sortKey, lodash.repeat('desc,', sortKey.length).split(','))
return list
}

View File

@ -1,189 +1,190 @@
//#老婆
import lodash from "lodash";
import { Cfg } from "../../components/index.js";
import { Character } from "../../components/models.js";
import { checkWifeType, getAvatarList, renderAvatar } from "./avatar-card.js";
// #老婆
import lodash from 'lodash'
import { Cfg } from '../../components/index.js'
import { Character } from '../../components/models.js'
import { getAvatarList, renderAvatar } from './avatar-card.js'
const relationMap = {
wife: {
keyword: "老婆,媳妇,妻子,娘子".split(","),
keyword: '老婆,媳妇,妻子,娘子'.split(','),
type: 0
},
husband: {
keyword: "老公,丈夫,夫君,郎君".split(","),
keyword: '老公,丈夫,夫君,郎君'.split(','),
type: 1
},
gf: {
keyword: "女朋友,女友,女神".split(","),
keyword: '女朋友,女友,女神'.split(','),
type: 0
},
bf: {
keyword: "男朋友,男友,男神".split(","),
keyword: '男朋友,男友,男神'.split(','),
type: 1
},
daughter: {
keyword: "女儿".split(","),
keyword: '女儿'.split(','),
type: 2
},
son: {
keyword: "儿子".split(","),
keyword: '儿子'.split(','),
type: 3
}
}
const relation = lodash.flatMap(relationMap, (d) => d.keyword);
export const wifeReg = `^#?\\s*(${relation.join("|")})\\s*(设置|选择|指定|列表|查询|列表|是|是谁|照片|相片|图片|写真|图像)?\\s*([^\\d]*)\\s*(\\d*)$`;
const relation = lodash.flatMap(relationMap, (d) => d.keyword)
export const wifeReg = `^#?\\s*(${relation.join('|')})\\s*(设置|选择|指定|列表|查询|列表|是|是谁|照片|相片|图片|写真|图像)?\\s*([^\\d]*)\\s*(\\d*)$`
export async function wife(e, { render, User }) {
let msg = e.msg || "";
if (!msg && !e.isPoke) return false;
export async function wife (e, { render, User }) {
let msg = e.msg || ''
if (!msg && !e.isPoke) return false
if (e.isPoke) {
if (Cfg.isDisable(e, "char.poke")) {
return false;
if (Cfg.isDisable(e, 'char.poke')) {
return false
}
} else if (Cfg.isDisable(e, "char.wife")) {
return false;
} else if (Cfg.isDisable(e, 'char.wife')) {
return false
}
let msgRet = (new RegExp(wifeReg)).exec(msg);
let msgRet = (new RegExp(wifeReg)).exec(msg)
if (e.isPoke) {
msgRet = [];
msgRet = []
} else if (!msgRet) {
return false;
return false
}
let target = msgRet[1],
action = msgRet[2] || "卡片",
actionParam = msgRet[3] || "";
let target = msgRet[1]
let action = msgRet[2] || '卡片'
let actionParam = msgRet[3] || ''
if (!"设置,选择,挑选,指定".split(",").includes(action) && actionParam) {
return false;
if (!'设置,选择,挑选,指定'.split(',').includes(action) && actionParam) {
return false
}
let targetCfg = lodash.find(relationMap, (cfg, key) => {
cfg.key = key;
return cfg.keyword.includes(target);
});
if (!targetCfg && !e.isPoke) return true;
cfg.key = key
return cfg.keyword.includes(target)
})
if (!targetCfg && !e.isPoke) return true
let avatarList = [], avatar = {}, wifeList = [];
let avatarList = [];
let avatar = {};
let wifeList = []
let MysApi = await e.getMysApi({
auth: "all",
targetType: Cfg.get("char.queryOther", true) ? "all" : "self",
cookieType: "all",
actionName: "查询信息"
});
auth: 'all',
targetType: Cfg.get('char.queryOther', true) ? 'all' : 'self',
cookieType: 'all',
actionName: '查询信息'
})
if (!MysApi || !MysApi.selfUser) {
return true;
return true
}
let selfUser = MysApi.selfUser;
let selfMysUser = await selfUser.getMysUser();
let isSelf = true;
let selfUser = MysApi.selfUser
let selfMysUser = await selfUser.getMysUser()
let isSelf = true
if (!selfMysUser || selfMysUser.uid !== MysApi.targetUser.uid) {
isSelf = false;
isSelf = false
}
switch (action) {
case "卡片":
case "照片":
case "相片":
case "图片":
case "写真":
case '卡片':
case '照片':
case '相片':
case '图片':
case '写真':
// 展示老婆卡片
// 如果选择过,则进行展示
let renderType = action === "卡片" ? "card" : "photo";
let renderType = action === '卡片' ? 'card' : 'photo'
if (!e.isPoke) {
wifeList = await selfUser.getCfg(`wife.${targetCfg.key}`, []);
wifeList = await selfUser.getCfg(`wife.${targetCfg.key}`, [])
// 存在设置
if (wifeList && wifeList.length > 0 && isSelf && !e.isPoke) {
if (wifeList[0] === "随机") {
if (wifeList[0] === '随机') {
// 如果选择为全部,则从列表中随机选择一个
avatarList = await getAvatarList(e, targetCfg.type, MysApi);
let avatar = lodash.sample(avatarList);
return renderAvatar(e, avatar, render, renderType);
avatarList = await getAvatarList(e, targetCfg.type, MysApi)
let avatar = lodash.sample(avatarList)
return renderAvatar(e, avatar, render, renderType)
} else {
// 如果指定过,则展示指定角色
return renderAvatar(e, lodash.sample(wifeList), render, renderType);
return renderAvatar(e, lodash.sample(wifeList), render, renderType)
}
}
}
// 如果未指定过则从列表中排序并随机选择前5个
if (e.isPoke) {
avatarList = await getAvatarList(e, false, MysApi);
avatarList = await getAvatarList(e, false, MysApi)
if (avatarList && avatarList.length > 0) {
avatar = lodash.sample(avatarList);
return await renderAvatar(e, avatar, render, renderType);
avatar = lodash.sample(avatarList)
return await renderAvatar(e, avatar, render, renderType)
}
} else {
avatarList = await getAvatarList(e, targetCfg.type, MysApi);
avatarList = await getAvatarList(e, targetCfg.type, MysApi)
if (avatarList && avatarList.length > 0) {
avatar = lodash.sample(avatarList.slice(0, 5));
return await renderAvatar(e, avatar, render, renderType);
avatar = lodash.sample(avatarList.slice(0, 5))
return await renderAvatar(e, avatar, render, renderType)
}
}
e.reply(`在当前米游社公开展示的角色中未能找到适合展示的角色..`);
return true;
break;
case "设置":
case "选择":
case "挑选":
case "指定":
e.reply('在当前米游社公开展示的角色中未能找到适合展示的角色..')
return true
break
case '设置':
case '选择':
case '挑选':
case '指定':
if (!isSelf) {
e.reply("只能指定自己的哦~");
return true;
e.reply('只能指定自己的哦~')
return true
}
// 选择老婆
actionParam = actionParam.replace(/(|、|;|)/g, ",");
wifeList = actionParam.split(",");
let addRet = [];
if (lodash.intersection(["全部", "任意", "随机", "全都要"], wifeList).length > 0) {
addRet = ['随机'];
actionParam = actionParam.replace(/(|、|;|)/g, ',')
wifeList = actionParam.split(',')
let addRet = []
if (lodash.intersection(['全部', '任意', '随机', '全都要'], wifeList).length > 0) {
addRet = ['随机']
} else {
wifeList = lodash.map(wifeList, (name) => {
let char = Character.get(name);
let char = Character.get(name)
if (char && char.checkWifeType(targetCfg.type)) {
return char.name;
return char.name
}
});
wifeList = lodash.filter(lodash.uniq(wifeList), (d) => !!d);
})
wifeList = lodash.filter(lodash.uniq(wifeList), (d) => !!d)
/*
avatarList = await getAvatarList(e, targetCfg.type, MysApi);
avatarList = lodash.map(avatarList, (avatar) => avatar.name);
avatarList = lodash.filter(avatarList, (d) => !!d);
addRet = lodash.intersection(avatarList, wifeList);
*/
addRet = wifeList;
addRet = wifeList
if (addRet.length === 0) {
e.reply(`在可选的${targetCfg.keyword[0]}列表中未能找到 ${actionParam} ~`);
return true;
e.reply(`在可选的${targetCfg.keyword[0]}列表中未能找到 ${actionParam} ~`)
return true
}
}
await selfUser.setCfg(`wife.${targetCfg.key}`, addRet);
e.reply(`${targetCfg.keyword[0]}已经设置:${addRet.join("")}`);
return true;
break;
case "列表":
case "是":
case "是谁":
await selfUser.setCfg(`wife.${targetCfg.key}`, addRet)
e.reply(`${targetCfg.keyword[0]}已经设置:${addRet.join('')}`)
return true
break
case '列表':
case '是':
case '是谁':
// 查看当前选择老婆
if (!isSelf) {
e.reply("只能查看自己的哦~");
return true;
e.reply('只能查看自己的哦~')
return true
}
wifeList = await selfUser.getCfg(`wife.${targetCfg.key}`, []);
wifeList = await selfUser.getCfg(`wife.${targetCfg.key}`, [])
if (wifeList && wifeList.length > 0) {
e.reply(`你的${targetCfg.keyword[0]}是:${wifeList.join("")}`);
e.reply(`你的${targetCfg.keyword[0]}是:${wifeList.join('')}`)
} else {
e.reply(`尚未设置,回复#${targetCfg.keyword[0]}设置+角色名 来设置,如果设置多位请用逗号间隔`)
}
break;
break
}
return true;
return true
}
export async function pokeWife(e, components) {
return await wife(e, components);
}
export async function pokeWife (e, components) {
return await wife(e, components)
}

View File

@ -1,252 +1,247 @@
/*
* 面板公共方法及处理
* */
import lodash from "lodash";
import { segment } from "oicq";
import Profile from "../../components/Profile.js";
import { Character } from "../../components/models.js";
import lodash from 'lodash'
import { segment } from 'oicq'
import Profile from '../../components/Profile.js'
import { Character } from '../../components/models.js'
/*
* 获取面板查询的 目标uid
* */
export async function getTargetUid(e) {
let uidReg = /[1-9][0-9]{8}/;
export async function getTargetUid (e) {
let uidReg = /[1-9][0-9]{8}/
if (e.uid && uidReg.test(e.uid)) {
return e.uid;
return e.uid
}
let uidRet = uidReg.exec(e.msg);
let uidRet = uidReg.exec(e.msg)
if (uidRet) {
return uidRet[0]
}
let uid = false;
let uid = false
let getUid = async function (qq) {
if (NoteCookie && NoteCookie[qq]) {
let nc = NoteCookie[qq];
let nCookie = global.NoteCookie || false
if (nCookie && nCookie[qq]) {
let nc = nCookie[qq]
if (nc.uid && uidReg.test(nc.uid)) {
return nc.uid;
return nc.uid
}
}
uid = await redis.get(`genshin:id-uid:${qq}`) || await redis.get(`genshin:uid:${qq}`);
uid = await redis.get(`genshin:id-uid:${qq}`) || await redis.get(`genshin:uid:${qq}`)
if (uid && uidReg.test(uid)) {
return uid;
return uid
}
}
if (e.at && e.at != BotConfig.account.qq) {
uid = await getUid(e.at);
if (e.at && e.at != global?.BotConfig?.account?.qq) {
uid = await getUid(e.at)
if (uid) {
return uid;
return uid
}
}
uid = await getUid(e.user_id);
uid = await getUid(e.user_id)
if (uid) {
return uid;
return uid
}
try {
let MysApi = await e.getMysApi({
auth: "all",
targetType: "all",
cookieType: "all"
});
auth: 'all',
targetType: 'all',
cookieType: 'all'
})
if (!MysApi || !e.targetUser) {
return false;
return false
}
uid = e.targetUser.uid;
uid = e.targetUser.uid
if (!uid || !uidReg.test(uid)) {
e.reply("请先发送【#绑定+你的UID】来绑定查询目标")
return false;
e.reply('请先发送【#绑定+你的UID】来绑定查询目标')
return false
}
} catch (err) {
console.log(err);
console.log(err)
}
return uid || false;
return uid || false
}
/*
* 自动更新面板数据
* */
export async function autoRefresh(e) {
let uid = await getTargetUid(e);
export async function autoRefresh (e) {
let uid = await getTargetUid(e)
if (!uid || e.isRefreshed) {
return false;
return false
}
let refreshMark = await redis.get(`miao:profile-refresh-cd:${uid}`);
let inCd = await redis.get(`miao:role-all:${uid}`);
let refreshMark = await redis.get(`miao:profile-refresh-cd:${uid}`)
let inCd = await redis.get(`miao:role-all:${uid}`)
if (refreshMark || inCd) {
return false;
return false
}
await redis.set(`miao:profile-refresh-cd:${uid}`, "TRUE", { EX: 3600 * 12 });
e.isRefreshed = true;
await redis.set(`miao:profile-refresh-cd:${uid}`, 'TRUE', { EX: 3600 * 12 })
e.isRefreshed = true
// 数据更新
let data = await Profile.request(uid, e);
let data = await Profile.request(uid, e)
if (!data) {
return false;
return false
}
if (!data.chars) {
e.reply("请确认角色已在【游戏内】橱窗展示并开放了查看详情。请在设置完毕5分钟后使用 #面板更新 重新获取");
return false;
e.reply('请确认角色已在【游戏内】橱窗展示并开放了查看详情。请在设置完毕5分钟后使用 #面板更新 重新获取')
return false
} else {
let ret = [];
let ret = []
lodash.forEach(data.chars, (ds) => {
let char = Character.get(ds.id);
let char = Character.get(ds.id)
if (char) {
ret.push(char.name);
ret.push(char.name)
}
})
if (ret.length === 0) {
e.reply("请确认角色已在【游戏内】橱窗展示并开放了查看详情。请在设置完毕5分钟后使用 #面板更新 重新获取")
return false;
e.reply('请确认角色已在【游戏内】橱窗展示并开放了查看详情。请在设置完毕5分钟后使用 #面板更新 重新获取')
return false
} else {
// e.reply(`本次获取成功角色: ${ret.join(", ")} `)
return true;
return true
}
}
return true;
return true
}
export async function autoGetProfile(e, uid, avatar, callback) {
export async function autoGetProfile (e, uid, avatar, callback) {
let refresh = async () => {
let refreshRet = await autoRefresh(e);
let refreshRet = await autoRefresh(e)
if (refreshRet) {
await callback();
await callback()
}
return refreshRet;
return refreshRet
}
let char = Character.get(avatar);
let char = Character.get(avatar)
if (!char) {
return { err: true };
return { err: true }
}
let profile = await Profile.get(uid, char.id);
let profile = await Profile.get(uid, char.id)
if (!profile) {
if (await refresh()) {
return { err: true };
return { err: true }
} else {
e.reply(`请确认${char.name}已展示在【游戏内】的角色展柜中,并打开了“显示角色详情”。然后请使用 #更新面板\n命令来获取${char.name}的面板详情`);
e.reply(`请确认${char.name}已展示在【游戏内】的角色展柜中,并打开了“显示角色详情”。然后请使用 #更新面板\n命令来获取${char.name}的面板详情`)
}
return { err: true };
return { err: true }
} else if (!['enka', 'input2', 'miao', 'miao-pre'].includes(profile.dataSource)) {
if (!await refresh()) {
e.reply(`由于数据格式升级,请重新获取面板信息后查看`);
e.reply('由于数据格式升级,请重新获取面板信息后查看')
}
return { err: true };
return { err: true }
}
return { profile, char, refresh }
}
/*
* 面板数据更新
* */
export async function getProfile(e, mode = "refresh") {
let uid = await getTargetUid(e);
export async function getProfile (e, mode = 'refresh') {
let uid = await getTargetUid(e)
if (!uid) {
return true;
return true
}
if (mode === "input") {
if (mode === 'input') {
if (e.inputData.trim().length < 5) {
e.reply(`【输入示例】\n#录入夜兰面板 生命14450+25469, 攻击652+444, 防御548+144, 元素精通84, 暴击76.3, 爆伤194.2, 治疗0,充能112.3,元素伤害61.6,物伤0`)
return true;
//await profileHelp(e);
e.reply('【输入示例】\n#录入夜兰面板 生命14450+25469, 攻击652+444, 防御548+144, 元素精通84, 暴击76.3, 爆伤194.2, 治疗0,充能112.3,元素伤害61.6,物伤0')
return true
// await profileHelp(e);
}
let ret = Profile.inputProfile(uid, e);
let char = Character.get(e.avatar);
let ret = Profile.inputProfile(uid, e)
let char = Character.get(e.avatar)
if (lodash.isString(ret)) {
e.reply(ret);
return true;
e.reply(ret)
return true
} else if (ret) {
e.reply(`${char.name}信息手工录入完成,你可以使用 #角色名+面板 / #角色名+伤害 来查看详细角色面板属性了`)
} else {
e.reply(`${char.name}信息手工录入失败,请检查录入格式。回复 #角色面板帮助 可查看录入提示`);
e.reply(`【输入示例】\n#录入夜兰面板 生命14450+25469, 攻击652+444, 防御548+144, 元素精通84, 暴击76.3, 爆伤194.2, 治疗0,充能112.3,元素伤害61.6,物伤0`)
e.reply(`${char.name}信息手工录入失败,请检查录入格式。回复 #角色面板帮助 可查看录入提示`)
e.reply('【输入示例】\n#录入夜兰面板 生命14450+25469, 攻击652+444, 防御548+144, 元素精通84, 暴击76.3, 爆伤194.2, 治疗0,充能112.3,元素伤害61.6,物伤0')
}
return true;
return true
}
// 数据更新
let data = await Profile.request(uid, e);
let data = await Profile.request(uid, e)
if (!data) {
return true;
return true
}
if (!data.chars) {
e.reply("获取角色面板数据失败请确认角色已在游戏内橱窗展示并开放了查看详情。设置完毕后请5分钟后再进行请求~");
e.reply('获取角色面板数据失败请确认角色已在游戏内橱窗展示并开放了查看详情。设置完毕后请5分钟后再进行请求~')
} else {
let ret = [];
let ret = []
lodash.forEach(data.chars, (ds) => {
let char = Character.get(ds.id);
let char = Character.get(ds.id)
if (char) {
ret.push(char.name);
ret.push(char.name)
}
})
if (ret.length === 0) {
e.reply("获取角色面板数据失败未能请求到角色数据。请确认角色已在游戏内橱窗展示并开放了查看详情。设置完毕后请5分钟后再进行请求~")
e.reply('获取角色面板数据失败未能请求到角色数据。请确认角色已在游戏内橱窗展示并开放了查看详情。设置完毕后请5分钟后再进行请求~')
} else {
e.reply(`获取角色面板数据成功!\n${ret.length > 8 ? "所有已获取" : "本次获取成功"}角色: ${ret.join(", ")}\n你可以使用 #角色名+面板 来查看详细角色面板属性了。`)
e.reply(`获取角色面板数据成功!\n${ret.length > 8 ? '所有已获取' : '本次获取成功'}角色: ${ret.join(', ')}\n你可以使用 #角色名+面板 来查看详细角色面板属性了。`)
}
}
return true;
return true
}
/*
* 获取面板列表
* */
export async function getProfileAll(e) {
let uid = await getTargetUid(e);
export async function getProfileAll (e) {
let uid = await getTargetUid(e)
if (!uid) {
return true;
return true
}
let profiles = Profile.getAll(uid) || {};
let profiles = Profile.getAll(uid) || {}
let chars = [];
let chars = []
lodash.forEach(profiles || [], (ds) => {
if (!['enka', 'input2', 'miao-pre', 'miao'].includes(ds.dataSource)) {
return;
return
}
ds.name && chars.push(ds.name)
});
})
if (chars.length === 0) {
if (await autoRefresh(e)) {
await getProfileAll(e);
return true;
await getProfileAll(e)
return true
} else {
e.reply("尚未获取任何角色数据");
await profileHelp(e);
e.reply('尚未获取任何角色数据')
await profileHelp(e)
}
return true;
return true
}
e.reply(`uid${uid} 已获取面板角色: ` + chars.join(", "));
e.reply(`uid${uid} 已获取面板角色: ` + chars.join(', '))
return true;
return true
}
/*
* 面板帮助
* */
export async function profileHelp(e) {
export async function profileHelp (e) {
e.reply(segment.image(`file://${process.cwd()}/plugins/miao-plugin/resources/character/imgs/help.jpg`))
return true;
}
return true
}

View File

@ -1,59 +1,56 @@
import lodash from "lodash";
import { autoRefresh } from "./profile-common.js";
import { Calc, Common, Format, Models, Profile } from "../../components/index.js";
import { getArtis } from "./profile-artis.js";
const { Character } = Models;
export async function renderProfile(e, char, render, mode = "profile", params = {}) {
import lodash from 'lodash'
import { autoRefresh } from './profile-common.js'
import { Calc, Common, Format, Profile } from '../../components/index.js'
import { getArtis } from './profile-artis.js'
export async function renderProfile (e, char, render, mode = 'profile', params = {}) {
let selfUser = await e.checkAuth({
auth: "self"
auth: 'self'
})
let { uid } = e;
let { uid } = e
if (['荧', '空', '主角', '旅行者'].includes(char.name)) {
e.reply("暂不支持主角的面板信息查看");
return true;
e.reply('暂不支持主角的面板信息查看')
return true
}
if (char.isCustom) {
e.reply(`暂不支持自定义角色${char.name}的面板信息查看`);
return true;
e.reply(`暂不支持自定义角色${char.name}的面板信息查看`)
return true
}
let refresh = async () => {
let refreshRet = await autoRefresh(e);
let refreshRet = await autoRefresh(e)
if (refreshRet) {
await renderProfile(e, char, render, mode, params);
await renderProfile(e, char, render, mode, params)
}
return refreshRet;
return refreshRet
}
let profile = await Profile.get(uid, char.id);
let profile = await Profile.get(uid, char.id)
if (!profile) {
if (await refresh()) {
return true;
return true
} else {
e.reply(`请确认${char.name}已展示在【游戏内】的角色展柜中,并打开了“显示角色详情”。然后请使用 #更新面板\n命令来获取${char.name}的面板详情`);
e.reply(`请确认${char.name}已展示在【游戏内】的角色展柜中,并打开了“显示角色详情”。然后请使用 #更新面板\n命令来获取${char.name}的面板详情`)
}
return true;
return true
} else if (!profile.attr) {
if (!await refresh()) {
e.reply(`由于数据Api变更请重新获取面板信息后查看`);
e.reply('由于数据Api变更请重新获取面板信息后查看')
}
return true;
return true
} else if (!['enka', 'input2', 'miao', 'miao-pre'].includes(profile.dataSource)) {
if (!await refresh()) {
e.reply(`由于数据格式升级,请重新获取面板信息后查看`);
e.reply('由于数据格式升级,请重新获取面板信息后查看')
}
return true;
return true
}
let a = profile.attr;
let c = Format.comma,
p = Format.pct;
let a = profile.attr
let c = Format.comma
let p = Format.pct
let attr = {
hp: c(a.hp),
hpPlus: c(a.hp - a.hpBase),
@ -66,50 +63,48 @@ export async function renderProfile(e, char, render, mode = "profile", params =
mastery: c(a.mastery),
recharge: p(a.recharge),
dmgBonus: p(Math.max(a.dmgBonus * 1 || 0, a.phyBonus * 1 || 0))
};
}
let { artis, totalMark, totalMarkClass, usefulMark } = getArtis(char.name, profile.artis)
let { artis, totalMark, totalMarkClass, usefulMark } = getArtis(char.name, profile.artis);
let enemyLv = await selfUser.getCfg(`char.enemyLv`, 91);
let dmgMsg = [], dmgData = [];
let enemyLv = await selfUser.getCfg('char.enemyLv', 91)
let dmgMsg = []
let dmgData = []
let dmgCalc = await Calc.calcData({
profile,
char,
enemyLv,
mode,
...params
});
})
if (dmgCalc && dmgCalc.ret) {
lodash.forEach(dmgCalc.ret, (ds) => {
ds.dmg = Format.comma(ds.dmg, 0);
ds.avg = Format.comma(ds.avg, 0);
dmgData.push(ds);
ds.dmg = Format.comma(ds.dmg, 0)
ds.avg = Format.comma(ds.avg, 0)
dmgData.push(ds)
})
lodash.forEach(dmgCalc.msg, (msg) => {
msg.replace(":", "");
dmgMsg.push(msg.split(""))
msg.replace(':', '')
dmgMsg.push(msg.split(''))
})
}
if (mode === "dmg") {
let basic = dmgCalc.dmgCfg.basicRet;
if (mode === 'dmg') {
let basic = dmgCalc.dmgCfg.basicRet
lodash.forEach(dmgCalc.dmgRet, (row) => {
lodash.forEach(row, (ds) => {
ds.val = (ds.avg > basic.avg ? "+" : "") + Format.comma(ds.avg - basic.avg);
ds.dmg = Format.comma(ds.dmg, 0);
ds.avg = Format.comma(ds.avg, 0);
ds.val = (ds.avg > basic.avg ? '+' : '') + Format.comma(ds.avg - basic.avg)
ds.dmg = Format.comma(ds.dmg, 0)
ds.avg = Format.comma(ds.avg, 0)
})
});
basic.dmg = Format.comma(basic.dmg);
basic.avg = Format.comma(basic.avg);
})
basic.dmg = Format.comma(basic.dmg)
basic.avg = Format.comma(basic.avg)
}
//渲染图像
return await Common.render("character/detail", {
// 渲染图像
return await Common.render('character/detail', {
save_id: uid,
uid: uid,
uid,
data: profile,
attr,
cons: char.cons,
@ -122,12 +117,12 @@ export async function renderProfile(e, char, render, mode = "profile", params =
dmgCfg: dmgCalc.dmgCfg,
artis,
enemyLv,
enemyName: dmgCalc.enemyName || "小宝",
enemyName: dmgCalc.enemyName || '小宝',
totalMark: c(totalMark, 1),
totalMarkClass,
usefulMark,
talentMap: { a: "普攻", e: "战技", q: "爆发" },
talentMap: { a: '普攻', e: '战技', q: '爆发' },
bodyClass: `char-${char.name}`,
mode,
}, { e, render, scale: 1.6 });
}
mode
}, { e, render, scale: 1.6 })
}

View File

@ -1,24 +1,22 @@
import { Cfg } from "../components/index.js";
import { segment } from "oicq";
import lodash from "lodash";
import { currentVersion, changelogs } from "../components/Changelog.js";
import Common from "../components/Common.js";
import fs from "fs";
import { Cfg } from '../components/index.js'
import lodash from 'lodash'
import { currentVersion, changelogs } from '../components/Changelog.js'
import Common from '../components/Common.js'
import fs from 'fs'
const _path = process.cwd();
const helpPath = `${_path}/plugins/miao-plugin/resources/help`;
const _path = process.cwd()
const helpPath = `${_path}/plugins/miao-plugin/resources/help`
export async function help(e, { render }) {
if (!/喵喵/.test(e.msg) && !Cfg.get("sys.help", false)) {
return false;
export async function help (e, { render }) {
if (!/喵喵/.test(e.msg) && !Cfg.get('sys.help', false)) {
return false
}
let custom = {}, help = {};
let custom = {}; let help = {}
if (fs.existsSync(`${helpPath}/help-cfg.js`)) {
help = await import(`file://${helpPath}/help-cfg.js?version=${new Date().getTime()}`);
help = await import(`file://${helpPath}/help-cfg.js?version=${new Date().getTime()}`)
} else if (fs.existsSync(`${helpPath}/help-list.js`)) {
help = await import(`file://${helpPath}/help-list.js?version=${new Date().getTime()}`);
help = await import(`file://${helpPath}/help-list.js?version=${new Date().getTime()}`)
}
// 兼容一下旧字段
@ -26,48 +24,47 @@ export async function help(e, { render }) {
custom = {
helpList: help.helpCfg,
helpCfg: {}
};
}
} else {
custom = help;
custom = help
}
let def = await import(`file://${helpPath}/help-cfg_default.js?version=${new Date().getTime()}`);
let def = await import(`file://${helpPath}/help-cfg_default.js?version=${new Date().getTime()}`)
let helpCfg = lodash.defaults(custom.helpCfg, def.helpCfg);
let helpList = custom.helpList || def.helpList;
let helpCfg = lodash.defaults(custom.helpCfg, def.helpCfg)
let helpList = custom.helpList || def.helpList
let helpGroup = [];
let helpGroup = []
lodash.forEach(helpList, (group) => {
if (group.auth && group.auth === "master" && !e.isMaster) {
return;
if (group.auth && group.auth === 'master' && !e.isMaster) {
return
}
lodash.forEach(group.list, (help) => {
let icon = help.icon * 1;
let icon = help.icon * 1
if (!icon) {
help.css = `display:none`;
help.css = 'display:none'
} else {
let x = (icon - 1) % 10, y = (icon - x - 1) / 10;
help.css = `background-position:-${x * 50}px -${y * 50}px`;
let x = (icon - 1) % 10; let y = (icon - x - 1) / 10
help.css = `background-position:-${x * 50}px -${y * 50}px`
}
})
});
helpGroup.push(group)
})
helpGroup.push(group);
});
return await Common.render("help/index", {
return await Common.render('help/index', {
helpCfg,
helpGroup,
element: 'default'
}, { e, render, scale: 1.2 })
}
export async function versionInfo(e, { render }) {
return await Common.render("help/version-info", {
export async function versionInfo (e, { render }) {
return await Common.render('help/version-info', {
currentVersion,
changelogs,
elem: "cryo",
elem: 'cryo'
}, { e, render, scale: 1.2 })
}
}

119
apps/index.js Normal file
View File

@ -0,0 +1,119 @@
import { wifeReg } from './character.js'
import { consStat, abyssPct, abyssTeam, uploadData } from './stat.js'
import { wiki, calendar } from './wiki.js'
import { help, versionInfo } from './help.js'
import lodash from 'lodash'
import { rule as adminRule, updateRes, sysCfg, updateMiaoPlugin, profileCfg } from './admin.js'
export {
character,
getProfile,
wife,
pokeWife,
enemyLv,
profileArtisList,
getProfileAll,
profileHelp,
getOriginalPicture,
uploadCharacterImg
} from './character.js'
export {
consStat,
abyssPct,
abyssTeam,
wiki,
updateRes,
updateMiaoPlugin,
sysCfg,
help,
versionInfo,
calendar,
profileCfg,
uploadData
}
let rule = {
character: {
reg: '^(#(.*)|#*(更新|录入)?(.*)(详细|详情|面板|面版|伤害[1-7]?)(更新)?)$',
// reg: "noCheck",
describe: '【#角色】角色详情'
},
uploadCharacterImg: {
reg: '^#*(喵喵)?(上传|添加)(.+)(照片|写真|图片|图像)\\s*$',
describe: '喵喵上传角色写真'
},
profileArtisList: {
reg: '^#圣遗物列表\\s*(\\d{9})?$',
describe: '【#角色】圣遗物列表'
},
getProfileAll: {
reg: '^#(面板角色|角色面板|面板)(列表)?\\s*(\\d{9})?$',
describe: '【#角色】查看当前已获取面板数据的角色列表'
},
profileHelp: {
reg: '^#角色面板帮助$',
describe: '【#角色】查看当前已获取面板数据的角色列表'
},
wife: {
reg: wifeReg,
describe: '【#角色】#老公 #老婆 查询'
},
pokeWife: {
reg: '#poke#',
describe: '【#角色】戳一戳'
},
getOriginalPicture: {
reg: '^#?(获取|给我|我要|求|发|发下|发个|发一下)?原图(吧|呗)?$',
describe: '【#原图】 回复角色卡片,可获取原图'
},
consStat: {
reg: '^#(喵喵)?角色(持有|持有率|命座|命之座|.命)(分布|统计|持有|持有率)?$',
describe: '【#统计】 #角色持有率 #角色5命统计'
},
abyssPct: {
reg: '^#(喵喵)?深渊(第?.{1,2}层)?(角色)?(出场|使用)(率|统计)*$',
describe: '【#统计】 #深渊出场率 #深渊12层出场率'
},
abyssTeam: {
reg: '#深渊(组队|配队)',
describe: '【#角色】 #深渊组队'
},
wiki: {
reg: '^(#|喵喵)?.*(天赋|技能|命座|命之座|资料|照片|写真|图片|图像)$',
describe: '【#资料】 #神里天赋 #夜兰命座'
},
help: {
reg: '^#?(喵喵)?(命令|帮助|菜单|help|说明|功能|指令|使用说明)$',
describe: '【#帮助】 #喵喵帮助'
},
getProfile: {
reg: '^#(全部面板更新|更新全部面板|获取游戏角色详情|更新面板|面板更新)\\s*(\\d{9})?$',
describe: '【#角色】 获取游戏橱窗详情数据'
},
enemyLv: {
reg: '^#(敌人|怪物)等级\\s*\\d{1,3}\\s*$',
describe: '【#角色】 设置伤害计算中目标敌人的等级'
},
versionInfo: {
reg: '^#?喵喵版本$',
describe: '【#帮助】 喵喵版本介绍'
},
calendar: {
reg: '^(#|喵喵)+(日历|日历列表)$',
describe: '【#日历】 活动日历'
},
uploadData: {
reg: '^#上传(深渊|数据)+$'
},
...adminRule
}
lodash.forEach(rule, (r) => {
r.priority = r.priority || 50
r.prehash = true
r.hashMark = true
})
export { rule }

View File

@ -2,146 +2,144 @@
* 胡桃数据库的统计
*
* */
import { HutaoApi, Character } from "../components/models.js";
import { Cfg, Data } from "../components/index.js";
import lodash from "lodash";
import fs from "fs";
import Common from "../components/Common.js";
import { HutaoApi, Character } from '../components/models.js'
import { Cfg } from '../components/index.js'
import lodash from 'lodash'
import fs from 'fs'
import Common from '../components/Common.js'
export async function consStat(e, { render }) {
if (Cfg.isDisable(e, "wiki.abyss")) {
return;
export async function consStat (e, { render }) {
if (Cfg.isDisable(e, 'wiki.abyss')) {
return
}
let consData = await HutaoApi.getCons();
let consData = await HutaoApi.getCons()
if (!consData) {
e.reply("角色持有数据获取失败,请稍后重试~");
return true;
e.reply('角色持有数据获取失败,请稍后重试~')
return true
}
let msg = e.msg;
let msg = e.msg
let mode = /持有/.test(msg) ? "char" : "cons";
let mode = /持有/.test(msg) ? 'char' : 'cons'
let conNum = -1;
if (mode === "cons") {
let conNum = -1
if (mode === 'cons') {
lodash.forEach([/0|零/, /1|一/, /2|二/, /3|三/, /4|四/, /5|五/, /6|六|满/], (reg, idx) => {
if (reg.test(msg)) {
conNum = idx;
return false;
conNum = idx
return false
}
})
}
if (!consData && !consData.data) {
return true;
return true
}
let data = consData.data;
let data = consData.data
let Lumine = lodash.filter(data, (ds) => ds.avatar === 10000007)[0] || {},
Aether = lodash.filter(data, (ds) => ds.avatar === 10000005)[0] || {};
let Lumine = lodash.filter(data, (ds) => ds.avatar === 10000007)[0] || {}
let Aether = lodash.filter(data, (ds) => ds.avatar === 10000005)[0] || {}
Lumine.holdingRate = (1 - Aether.holdingRate) || Lumine.holdingRate;
Lumine.holdingRate = (1 - Aether.holdingRate) || Lumine.holdingRate
let ret = [];
let ret = []
lodash.forEach(data, (ds) => {
let char = Character.get(ds.avatar);
let char = Character.get(ds.avatar)
let data = {
name: char.name || ds.avatar,
star: char.star || 3,
hold: ds.holdingRate
};
}
if (mode === "char") {
if (mode === 'char') {
data.cons = lodash.map(ds.rate, (c) => {
c.value = c.value * ds.holdingRate;
return c;
});
c.value = c.value * ds.holdingRate
return c
})
} else {
data.cons = ds.rate
}
data.cons = lodash.sortBy(data.cons, ['id'])
ret.push(data);
});
ret.push(data)
})
if (conNum > -1) {
ret = lodash.sortBy(ret, [`cons[${conNum}].value`]);
ret.reverse();
ret = lodash.sortBy(ret, [`cons[${conNum}].value`])
ret.reverse()
} else {
ret = lodash.sortBy(ret, ['hold']);
ret = lodash.sortBy(ret, ['hold'])
}
//渲染图像
return await Common.render("stat/character", {
// 渲染图像
return await Common.render('stat/character', {
chars: ret,
abbr: Character.getAbbr(),
mode: mode,
mode,
conNum,
lastUpdate: consData.lastUpdate,
pct: function (num) {
return (num * 100).toFixed(2);
},
}, { e, render, scale: 1.5 });
return (num * 100).toFixed(2)
}
}, { e, render, scale: 1.5 })
}
export async function abyssPct(e, { render }) {
if (Cfg.isDisable(e, "wiki.abyss")) {
return;
export async function abyssPct (e, { render }) {
if (Cfg.isDisable(e, 'wiki.abyss')) {
return
}
let mode = /使用/.test(e.msg) ? "use" : "pct",
modeName, abyssData, modeMulti = 1;
let mode = /使用/.test(e.msg) ? 'use' : 'pct'
let modeName
let abyssData
let modeMulti = 1
if (mode === "use") {
modeName = "使用率";
abyssData = await HutaoApi.getAbyssUse();
if (mode === 'use') {
modeName = '使用率'
abyssData = await HutaoApi.getAbyssUse()
} else {
modeName = "出场率";
abyssData = await HutaoApi.getAbyssPct();
modeMulti = 8;
modeName = '出场率'
abyssData = await HutaoApi.getAbyssPct()
modeMulti = 8
}
if (!abyssData) {
e.reply(`深渊${modeName}数据获取失败,请稍后重试~`);
return true;
e.reply(`深渊${modeName}数据获取失败,请稍后重试~`)
return true
}
let ret = [], chooseFloor = -1, msg = e.msg;
let ret = []
let chooseFloor = -1
let msg = e.msg
const floorName = {
"12": "十二层",
"11": "十一层",
"10": "十层",
"9": "九层"
};
12: '十二层',
11: '十一层',
10: '十层',
9: '九层'
}
// 匹配深渊楼层信息
lodash.forEach(floorName, (cn, num) => {
let reg = new RegExp(`${cn}|${num}`);
let reg = new RegExp(`${cn}|${num}`)
if (reg.test(msg)) {
chooseFloor = num;
return false;
chooseFloor = num
return false
}
});
})
let data = abyssData.data;
data = lodash.sortBy(data, "floor");
data = data.reverse();
let data = abyssData.data
data = lodash.sortBy(data, 'floor')
data = data.reverse()
lodash.forEach(data, (floorData) => {
let floor = {
floor: floorData.floor,
};
let avatars = [];
let avatars = []
lodash.forEach(floorData.avatarUsage, (ds) => {
let char = Character.get(ds.id);
let char = Character.get(ds.id)
if (char) {
avatars.push({
name: char.name,
@ -150,155 +148,155 @@ export async function abyssPct(e, { render }) {
})
}
})
avatars = lodash.sortBy(avatars, "value", ["asc"]);
avatars.reverse();
avatars = lodash.sortBy(avatars, 'value', ['asc'])
avatars.reverse()
if (chooseFloor === -1) {
avatars = avatars.slice(0, 14);
avatars = avatars.slice(0, 14)
}
ret.push({
floor: floorData.floor,
avatars
});
})
})
return await Common.render("stat/abyss-pct", {
return await Common.render('stat/abyss-pct', {
abyss: ret,
floorName,
chooseFloor,
mode,
modeName,
lastUpdate: abyssData.lastUpdate
}, { e, render, scale: 1.5 });
}, { e, render, scale: 1.5 })
}
async function getTalentData(e, isUpdate = false) {
async function getTalentData (e, isUpdate = false) {
// 技能查询缓存
let cachePath = `./data/cache/`;
let cachePath = './data/cache/'
if (!fs.existsSync(cachePath)) {
fs.mkdirSync(cachePath);
fs.mkdirSync(cachePath)
}
cachePath += "talentList/";
cachePath += 'talentList/'
if (!fs.existsSync(cachePath)) {
fs.mkdirSync(cachePath);
fs.mkdirSync(cachePath)
}
let avatarRet = [];
let uid = e.selfUser.uid;
let avatarRet = []
let uid = e.selfUser.uid
let hasCache = await redis.get(`cache:uid-talent-new:${uid}`);
let hasCache = await redis.get(`cache:uid-talent-new:${uid}`)
if (hasCache) {
// 有缓存优先使用缓存
let jsonRet = fs.readFileSync(cachePath + `${uid}.json`, "utf8");
avatarRet = JSON.parse(jsonRet);
return avatarRet;
let jsonRet = fs.readFileSync(cachePath + `${uid}.json`, 'utf8')
avatarRet = JSON.parse(jsonRet)
return avatarRet
} else if (!isUpdate) {
e.noReplyTalentList = true;
await YunzaiApps.mysInfo.talentList(e);
return await getTalentData(e, true);
e.noReplyTalentList = true
await global.YunzaiApps.mysInfo.talentList(e)
return await getTalentData(e, true)
}
return false;
return false
}
export async function abyssTeam(e, { render }) {
export async function abyssTeam (e, { render }) {
if (Common.todoV3(e)) {
return true
}
let MysApi = await e.getMysApi({
auth: "cookie", // 所有用户均可查询
targetType: "self", // 被查询用户可以是任意用户
cookieType: "self" // cookie可以是任意可用cookie
});
auth: 'cookie', // 所有用户均可查询
targetType: 'self', // 被查询用户可以是任意用户
cookieType: 'self' // cookie可以是任意可用cookie
})
if (!MysApi || !MysApi.selfUser || !MysApi.selfUser.uid) {
return true;
return true
}
let abyssData = await HutaoApi.getAbyssTeam();
let abyssData = await HutaoApi.getAbyssTeam()
if (!abyssData || !abyssData.data) {
e.reply("深渊组队数据获取失败,请稍后重试~");
return true;
e.reply('深渊组队数据获取失败,请稍后重试~')
return true
}
abyssData = abyssData.data;
let talentData = await getTalentData(e);
abyssData = abyssData.data
let talentData = await getTalentData(e)
if (!talentData || talentData.length === 0) {
e.reply("暂时未能获得角色的练度信息,请使用【#练度统计】命令尝试手工获取...");
return true;
e.reply('暂时未能获得角色的练度信息,请使用【#练度统计】命令尝试手工获取...')
return true
}
let avatarRet = {};
let data = {};
let avatarRet = {}
let data = {}
let noAvatar = {};
let noAvatar = {}
lodash.forEach(talentData, (avatar) => {
avatarRet[avatar.id] = Math.min(avatar.level, avatar.weapon_level) * 100 + Math.max(avatar.a_original, avatar.e_original, avatar.q_original) * 1000
});
})
let getTeamCfg = (str) => {
let teams = str.split(",");
teams.sort();
let teamMark = 0;
let teams = str.split(',')
teams.sort()
let teamMark = 0
lodash.forEach(teams, (a) => {
if (!avatarRet[a]) {
teamMark = -1;
noAvatar[a] = true;
teamMark = -1
noAvatar[a] = true
}
if (teamMark !== -1) {
teamMark += avatarRet[a] * 1;
teamMark += avatarRet[a] * 1
}
})
if (teamMark === -1) {
teamMark = 1;
teamMark = 1
}
return {
key: teams.join(","),
key: teams.join(','),
mark: teamMark
};
}
}
let hasSame = function (team1, team2) {
for (let idx = 0; idx < team1.length; idx++) {
if (team2.includes(team1[idx])) {
return true;
return true
}
}
return false;
return false
}
lodash.forEach(abyssData, (ds) => {
let floor = ds.level.floor;
let floor = ds.level.floor
if (!data[floor]) {
data[floor] = {
up: {},
down: {},
teams: []
};
}
}
lodash.forEach(ds.teams, (ds) => {
lodash.forEach(['up', 'down'], (halfKey) => {
let teamCfg = getTeamCfg(ds.id[`${halfKey}Half`]);
let teamCfg = getTeamCfg(ds.id[`${halfKey}Half`])
if (teamCfg) {
if (!data[floor][halfKey][teamCfg.key]) {
data[floor][halfKey][teamCfg.key] = {
count: 0,
mark: 0,
hasTeam: teamCfg.mark > 1
};
}
}
data[floor][halfKey][teamCfg.key].count += ds.value;
data[floor][halfKey][teamCfg.key].mark += ds.value * teamCfg.mark;
data[floor][halfKey][teamCfg.key].count += ds.value
data[floor][halfKey][teamCfg.key].mark += ds.value * teamCfg.mark
}
})
});
})
let temp = [];
let temp = []
lodash.forEach(['up', 'down'], (halfKey) => {
lodash.forEach(data[floor][halfKey], (ds, team) => {
temp.push({
team,
teamArr: team.split(","),
teamArr: team.split(','),
half: halfKey,
count: ds.count,
mark: ds.mark,
@ -306,67 +304,66 @@ export async function abyssTeam(e, { render }) {
hasTeam: ds.hasTeam
})
})
temp = lodash.sortBy(temp, "mark")
data[floor].teams = temp.reverse();
});
});
temp = lodash.sortBy(temp, 'mark')
data[floor].teams = temp.reverse()
})
})
let ret = {};
let ret = {}
lodash.forEach(data, (floorData, floor) => {
ret[floor] = {}
let ds = ret[floor];
let ds = ret[floor]
lodash.forEach(floorData.teams, (t1) => {
if (t1.mark2 <= 0) {
return;
return
}
lodash.forEach(floorData.teams, (t2) => {
if (t1.mark2 <= 0) {
return false;
return false
}
if (t1.half === t2.half || t2.mark2 <= 0) {
return;
return
}
let teamKey = t1.half === "up" ? (t1.team + "+" + t2.team) : (t2.team + "+" + t1.team);
let teamKey = t1.half === 'up' ? (t1.team + '+' + t2.team) : (t2.team + '+' + t1.team)
if (ds[teamKey]) {
return;
return
}
if (hasSame(t1.teamArr, t2.teamArr)) {
return;
return
}
ds[teamKey] = {
up: t1.half === "up" ? t1 : t2,
down: t1.half === "up" ? t2 : t1,
up: t1.half === 'up' ? t1 : t2,
down: t1.half === 'up' ? t2 : t1,
count: Math.min(t1.count, t2.count),
mark: t1.hasTeam && t2.hasTeam ? t1.mark + t2.mark : t1.count + t2.count // 如果不存在组队则进行评分惩罚
}
t1.mark2--;
t2.mark2--;
return false;
});
t1.mark2--
t2.mark2--
return false
})
if (lodash.keys(ds).length >= 20) {
return false;
return false
}
})
});
lodash.forEach(ret, (ds, floor) => {
ds = lodash.sortBy(lodash.values(ds), 'mark');
ds = ds.reverse();
ds = ds.slice(0, 4);
lodash.forEach(ds, (team) => {
team.up.teamArr = Character.sortIds(team.up.teamArr);
team.down.teamArr = Character.sortIds(team.down.teamArr);
})
ret[floor] = ds;
})
let avatarMap = {};
lodash.forEach(ret, (ds, floor) => {
ds = lodash.sortBy(lodash.values(ds), 'mark')
ds = ds.reverse()
ds = ds.slice(0, 4)
lodash.forEach(ds, (team) => {
team.up.teamArr = Character.sortIds(team.up.teamArr)
team.down.teamArr = Character.sortIds(team.down.teamArr)
})
ret[floor] = ds
})
let avatarMap = {}
lodash.forEach(talentData, (ds) => {
avatarMap[ds.id] = {
@ -379,79 +376,79 @@ export async function abyssTeam(e, { render }) {
})
lodash.forEach(noAvatar, (d, id) => {
let char = Character.get(id);
let char = Character.get(id)
avatarMap[id] = {
id,
name: char.name,
star: char.star,
level: 0,
cons: 0,
cons: 0
}
})
return await Common.render("stat/abyss-team", {
return await Common.render('stat/abyss-team', {
teams: ret,
avatars: avatarMap,
}, { e, render, scale: 1.5 });
avatars: avatarMap
}, { e, render, scale: 1.5 })
}
export async function uploadData(e) {
export async function uploadData (e) {
let MysApi = await e.getMysApi({
auth: "cookie",
targetType: "self",
cookieType: "self",
action: "获取信息"
});
if (!MysApi) return;
let ret = {};
let uid = e.selfUser.uid;
auth: 'cookie',
targetType: 'self',
cookieType: 'self',
action: '获取信息'
})
if (!MysApi) return
let ret = {}
let uid = e.selfUser.uid
try {
let resDetail = await MysApi.getCharacter();
let resAbyss = await MysApi.getSpiralAbyss(1);
//Data.writeJson('/test-data', 'abyss.json', resAbyss);
let resDetail = await MysApi.getCharacter()
let resAbyss = await MysApi.getSpiralAbyss(1)
// Data.writeJson('/test-data', 'abyss.json', resAbyss);
if (!resDetail || !resAbyss || !resDetail.avatars || resDetail.avatars.length <= 3) {
e.reply("角色信息获取失败");
return true;
e.reply('角色信息获取失败')
return true
}
delete resDetail._res;
delete resAbyss._res;
delete resDetail._res
delete resAbyss._res
ret = await HutaoApi.uploadData({
uid,
resDetail,
resAbyss
});
})
} catch (err) {
// console.log(err);
}
if (ret && ret.retcode === 0) {
let msg = [`uid:${uid}本次深渊记录上传成功~ \n多谢支持,φ(>ω<*) 喵~`];
let msg = [`uid:${uid}本次深渊记录上传成功~ \n多谢支持,φ(>ω<*) 喵~`]
if (ret.data) {
let addMsg = function (title, ds) {
if (!ds && !ds.avatarId && !ds.percent) {
return;
return
}
let char = Character.get(ds.avatarId);
title = `${title}${char.name}(${(ds.value / 10000).toFixed(1)}W)`;
let char = Character.get(ds.avatarId)
title = `${title}${char.name}(${(ds.value / 10000).toFixed(1)}W)`
let pct = (percent, name) => {
if (percent < 0.2) {
title += `,少于${(Math.max(0.1, 100 - percent * 100)).toFixed(1)}%的${name}`;
title += `,少于${(Math.max(0.1, 100 - percent * 100)).toFixed(1)}%的${name}`
} else {
title += `,超过${(Math.min(99.9, percent * 100)).toFixed(1)}%的${name}`;
title += `,超过${(Math.min(99.9, percent * 100)).toFixed(1)}%的${name}`
}
}
pct(ds.percent, char.name);
pct(ds.percentTotal, "总记录");
msg.push(title);
pct(ds.percent, char.name)
pct(ds.percentTotal, '总记录')
msg.push(title)
}
msg.push("当前数据记录:");
addMsg("【最强一击】", ret.data.damage || {});
addMsg("【承受伤害】", ret.data.takeDamage || {});
msg.push("排行会随时间而更新,数据仅供参考~");
msg.push('当前数据记录:')
addMsg('【最强一击】', ret.data.damage || {})
addMsg('【承受伤害】', ret.data.takeDamage || {})
msg.push('排行会随时间而更新,数据仅供参考~')
}
e.reply(msg.join("\n"))
e.reply(msg.join('\n'))
} else {
e.reply(`${ret.message || "上传失败"},请稍后重试...`);
e.reply(`${ret.message || '上传失败'},请稍后重试...`)
}
return true;
}
return true
}

View File

@ -1,93 +1,91 @@
import { segment } from "oicq";
import { Character } from "../components/models.js";
import lodash from "lodash";
import Calendar from "../components/Calendar.js";
import Common from "../components/Common.js";
import { Cfg } from "../components/index.js";
import { segment } from 'oicq'
import { Character } from '../components/models.js'
import lodash from 'lodash'
import Calendar from '../components/Calendar.js'
import Common from '../components/Common.js'
import { Cfg } from '../components/index.js'
// eslint-disable-next-line no-unused-vars
let action = {
wiki: {
keyword: "命座|天赋|技能|资料|照片|写真|图片|插画"
keyword: '命座|天赋|技能|资料|照片|写真|图片|插画'
}
}
export async function wiki(e, { render }) {
export async function wiki (e, { render }) {
if (!e.msg) {
return false;
return false
}
let reg = /#?(.+)(命座|命之座|天赋|技能|资料|照片|写真|图片|图像)$/, msg = e.msg;
let ret = reg.exec(msg);
let reg = /#?(.+)(命座|命之座|天赋|技能|资料|照片|写真|图片|图像)$/; let msg = e.msg
let ret = reg.exec(msg)
if (!ret || !ret[1] || !ret[2]) {
return false;
return false
}
let mode = "talent";
let mode = 'talent'
if (/命/.test(ret[2])) {
mode = "cons";
mode = 'cons'
} else if (/图|画|写真|照片/.test(ret[2])) {
mode = "pic";
mode = 'pic'
}
if ((mode === "pic" && Common.isDisable(e, "wiki.pic"))
|| (mode !== "pic" && Common.isDisable("wiki.wiki"))) {
return;
if ((mode === 'pic' && Common.isDisable(e, 'wiki.pic')) ||
(mode !== 'pic' && Common.isDisable('wiki.wiki'))) {
return
}
let char = Character.get(ret[1]);
let char = Character.get(ret[1])
if (!char) {
return false;
return false
}
if (mode === "pic") {
let img = char.getCardImg(Cfg.get("char.se", false), false);
if (mode === 'pic') {
let img = char.getCardImg(Cfg.get('char.se', false), false)
if (img && img.img) {
e.reply(segment.image(process.cwd() + "/plugins/miao-plugin/resources/" + img.img));
e.reply(segment.image(process.cwd() + '/plugins/miao-plugin/resources/' + img.img))
} else {
e.reply("暂无图片");
e.reply('暂无图片')
}
return true;
return true
}
if (char.isCustom) {
e.reply("暂不支持自定义角色");
return true;
e.reply('暂不支持自定义角色')
return true
}
return await Common.render("wiki/character", {
save_id: "天赋" + char.name,
return await Common.render('wiki/character', {
save_id: '天赋' + char.name,
...char,
mode,
line: getLineData(char),
_char: `/meta/character/${char.name}/`,
}, { e, render, scale: 1 });
_char: `/meta/character/${char.name}/`
}, { e, render, scale: 1 })
}
const getLineData = function (data) {
let ret = [];
lodash.forEach(data.lvStat.detail["90"], (num, idx) => {
let ret = []
lodash.forEach(data.lvStat.detail['90'], (num, idx) => {
ret.push({
num,
label: data.lvStat.stat[idx]
})
})
return ret;
return ret
}
export async function calendar(e, { render }) {
let calData = await Calendar.get();
let mode = "calendar";
export async function calendar (e, { render }) {
let calData = await Calendar.get()
let mode = 'calendar'
if (/(日历列表|活动)$/.test(e.msg)) {
mode = "list";
mode = 'list'
}
return await Common.render("wiki/calendar", {
return await Common.render('wiki/calendar', {
...calData,
displayMode: mode,
}, { e, render, scale: 1.1 });
}
displayMode: mode
}, { e, render, scale: 1.1 })
}

View File

@ -1,195 +0,0 @@
//技能列表配置了cookie能查
import fs from "fs";
import lodash from "lodash";
import common from "../../../lib/common.js";
import { segment } from "oicq";
export async function talentList(e, { render }) {
//缓存时间,单位小时
let cacheCd = 6;
let msg = e.msg.replace("#", "").trim();
if (msg === "角色统计" || msg === "武器统计") {
//暂时避让一下抽卡分析的关键词
return false;
}
let MysApi = await getMysApi(e);
if (!MysApi) return true;
let uid = MysApi.targetUid;
//禁止重复获取
if (skillLoading[e.user_id]) {
e.reply("角色数据获取中,请耐心等待...");
setTimeout(() => {
if (skillLoading[e.user_id]) delete skillLoading[e.user_id];
}, 60000);
return;
}
const displayMode = /(角色|武器|练度)/.test(e.msg) ? "weapon" : "talent";
//四星五星
let star = 0;
if (/(四|4)/.test(msg)) star = 4;
if (/(五|5)/.test(msg)) star = 5;
// 技能查询缓存
let cachePath = `./data/cache/`;
if (!fs.existsSync(cachePath)) {
fs.mkdirSync(cachePath);
}
cachePath += "talentList/";
if (!fs.existsSync(cachePath)) {
fs.mkdirSync(cachePath);
}
let avatarRet = [];
let hasCache = await redis.get(`cache:uid-talent-new:${uid}`); // 由于数据结构改变,临时修改一下键值,防止命中历史缓存导致展示错误
if (hasCache && !/force/.test(e.msg)) {
// 有缓存优先使用缓存
let jsonRet = fs.readFileSync(cachePath + `${uid}.json`, "utf8");
avatarRet = JSON.parse(jsonRet);
} else {
skillLoading[e.user_id] = true;
let resIndex = await MysApi.getCharacter();
if (!resIndex) {
delete skillLoading[e.user_id];
return true;
}
let avatarData = resIndex && resIndex.avatars || [];
// let skillRet = [], skill = [];
//配置了cookie的才去获取技能
// if (NoteCookie[e.user_id]) {
let skillRet = [], skill = [];
//配置了完整cookie的才去获取技能
if (NoteCookie[e.user_id] && NoteCookie[e.user_id].cookie.includes("cookie_token")) {
e.reply("角色数据获取中,请耐心等待...");
//批量获取技能数据分组10个id一次延迟100ms
let num = 10, ms = 100;
let avatarArr = lodash.chunk(avatarData, num);
for (let val of avatarArr) {
for (let avatar of val) {
skillRet.push(getSkill(e, uid, avatar, MysApi));
}
skillRet = await Promise.all(skillRet);
//过滤没有获取成功的
skillRet.filter(item => item.a);
skillRet = skillRet.filter(item => item.a);
await common.sleep(ms);
}
skill = lodash.keyBy(skillRet, "id");
}
// 天赋等级背景
const talentLvMap = '0,1,1,1,2,2,3,3,3,4,5'.split(',')
// 根据每日素材构建 角色->素材的映射关系
let charTalentMap = {};
daily.forEach((weekCfg, week) => {
lodash.forIn(weekCfg[0], (talentCfg, talentName) => {
talentCfg[1].forEach((charName) => {
charTalentMap[charName] = { name: talentName, week: [3, 1, 2][week] };
})
})
});
for (let idx in avatarData) {
let curr = avatarData[idx];
let avatar = lodash.pick(curr, "id,name,rarity,level,rarity,fetter".split(","));
// 埃洛伊rarity是105...
avatar.rarity = avatar.rarity > 5 ? 5 : avatar.rarity;
let weapon = curr.weapon || {};
"name,level,rarity,affix_level".split(",").forEach((idx) => {
avatar[`weapon_${idx}`] = curr.weapon[idx];
});
avatar.cons = curr.actived_constellation_num;
if (avatar.id == 10000007) {
avatar.name = "荧";
} else if (avatar.id == 10000005) {
avatar.name = "空";
} else {
let talent = charTalentMap[avatar.name] || {};
avatar.talent = talent.name;
avatar.talentWeek = talent.week; //`${talent.week}${talent.week + 3}`;
}
let skillRet = skill[avatar.id] || {};
const talentConsCfg = { a: 0, e: 3, q: 5 };
lodash.forIn(talentConsCfg, (consLevel, key) => {
let talent = skillRet[key] || {};
// 天赋等级
avatar[key] = talent.level_current || '-';
// 是否有命座加成
avatar[`${key}_plus`] = talent.level_current > talent.level_original;
// 天赋书星级
avatar[`${key}_lvl`] = talentLvMap[talent.level_original * 1];
avatar[`${key}_original`] = talent.level_original * 1;
})
avatar.aeq = avatar.a * 1 + avatar.e + avatar.q;
avatarRet.push(avatar);
}
fs.writeFileSync(cachePath + `${uid}.json`, JSON.stringify(avatarRet));
//缓存
await redis.set(`cache:uid-talent-new:${uid}`, uid, { EX: 3600 * cacheCd });
delete skillLoading[e.user_id];
// }
}
//超过八个角色才分类四星五星
if (star >= 4 && avatarRet.length > 8) {
avatarRet = avatarRet.filter(item => item.rarity == star);
}
let sortKey = ({
talent: "aeq,rarity,level,star,fetter,talentWeek",
weapon: "level,rarity,aeq,cons,weapon_level,weapon_rarity,weapon_affix_level,fetter"
})[displayMode].split(",");
avatarRet = lodash.orderBy(avatarRet, sortKey, lodash.repeat("desc,", sortKey.length).split(","));
let noTalent = avatarRet.length == 0 || /^\-+$/.test(avatarRet.map((d) => d.a).join(""));
let talentNotice = `技能列表每${cacheCd}小时更新一次`;
if (noTalent) {
talentNotice = "未绑定体力Cookie无法获取天赋列表。请回复 #体力 获取配置教程";
}
let week = new Date().getDay();
if (new Date().getHours() < 4) {
week--;
}
let base64 = await render("genshin", "talentList", {
save_id: uid,
uid: uid,
avatars: avatarRet,
bgType: Math.ceil(Math.random() * 3),
abbr: genshin.abbr,
displayMode,
isSelf: e.isSelf,
week: [3, 1, 2][week % 3],
talentNotice
});
if (base64) {
let msg = [];
if (e.isGroup) {
let name = lodash.truncate(e.sender.card, { length: 8 });
msg.push(segment.at(e.user_id, name));
}
msg.push(segment.image(`base64://${base64}`));
e.reply(msg);
}
return true; //事件结束不再往下
}

View File

@ -1,47 +1,48 @@
import fs from "fs";
import lodash from "lodash";
import fs from 'fs'
import lodash from 'lodash'
const _path = process.cwd();
const _logPath = `${_path}/plugins/miao-plugin/CHANGELOG.md`;
const _path = process.cwd()
const _logPath = `${_path}/plugins/miao-plugin/CHANGELOG.md`
let logs = {};
let changelogs = [];
let currentVersion;
let versionCount = 4;
let logs = {}
let changelogs = []
let currentVersion
let versionCount = 4
let packageJson = JSON.parse(fs.readFileSync("package.json", "utf8"));
let packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'))
const getLine = function (line) {
line = line.replace(/(^\s*\*|\r)/g, '');
line = line.replace(/\s*`([^`]+`)/g, '<span class="cmd">$1');
line = line.replace(/`\s*/g, '</span>');
line = line.replace(/\s*\*\*([^\*]+\*\*)/g, '<span class="strong">$1')
line = line.replace(/\*\*\s*/g, '</span>');
line = line.replace(/ⁿᵉʷ/g, '<span class="new"></span>');
return line;
line = line.replace(/(^\s*\*|\r)/g, '')
line = line.replace(/\s*`([^`]+`)/g, '<span class="cmd">$1')
line = line.replace(/`\s*/g, '</span>')
line = line.replace(/\s*\*\*([^\\*]+\*\*)/g, '<span class="strong">$1')
line = line.replace(/\*\*\s*/g, '</span>')
line = line.replace(/ⁿᵉʷ/g, '<span class="new"></span>')
return line
}
try {
if (fs.existsSync(_logPath)) {
logs = fs.readFileSync(_logPath, "utf8") || "";
logs = logs.split("\n");
logs = fs.readFileSync(_logPath, 'utf8') || ''
logs = logs.split('\n')
let temp = {}, lastLine = {};
let temp = {};
let lastLine = {}
lodash.forEach(logs, (line) => {
if (versionCount <= -1) {
return false;
return false
}
let versionRet = /^#\s*([0-9\\.~\s]+?)\s*$/.exec(line);
let versionRet = /^#\s*([0-9\\.~\s]+?)\s*$/.exec(line)
if (versionRet && versionRet[1]) {
let v = versionRet[1].trim();
let v = versionRet[1].trim()
if (!currentVersion) {
currentVersion = v;
currentVersion = v
} else {
changelogs.push(temp);
changelogs.push(temp)
if (/0\s*$/.test(v) && versionCount > 0) {
versionCount = 0;
versionCount = 0
} else {
versionCount--;
versionCount--
}
}
@ -51,24 +52,25 @@ try {
}
} else {
if (!line.trim()) {
return;
return
}
if (/^\*/.test(line)) {
lastLine = {
title: getLine(line),
logs: []
}
temp.logs.push(lastLine);
temp.logs.push(lastLine)
} else if (/^\s{3,}\*/.test(line)) {
lastLine.logs.push(getLine(line));
lastLine.logs.push(getLine(line))
}
}
});
})
}
} catch (e) {
// do nth
}
const yunzaiVersion = packageJson.version;
const yunzaiVersion = packageJson.version
const isV3 = yunzaiVersion[0] === '3'
export { currentVersion, yunzaiVersion, changelogs };
export { currentVersion, yunzaiVersion, isV3, changelogs }

View File

@ -1,32 +1,40 @@
import { Cfg } from "./index.js";
import { segment } from "oicq";
import { currentVersion, yunzaiVersion } from "./Changelog.js";
import { Cfg } from './index.js'
import { segment } from 'oicq'
import { currentVersion, yunzaiVersion, isV3 } from './Changelog.js'
export const render = async function (path, params, cfg) {
let paths = path.split("/");
let { render, e } = cfg;
let _layout_path = process.cwd() + "/plugins/miao-plugin/resources/common/layout/";
let paths = path.split('/')
let { render, e } = cfg
let layoutPath = process.cwd() + '/plugins/miao-plugin/resources/common/layout/'
let base64 = await render(paths[0], paths[1], {
...params,
_layout_path,
defaultLayout: _layout_path + "default.html",
elemLayout: _layout_path + "elem.html",
_layout_path: layoutPath,
defaultLayout: layoutPath + 'default.html',
elemLayout: layoutPath + 'elem.html',
sys: {
scale: Cfg.scale(cfg.scale || 1),
copyright: `Created By Yunzai-Bot<span class="version">${yunzaiVersion}</span> & Miao-Plugin<span class="version">${currentVersion}</span>`
}
});
})
if (base64) {
return await e.reply(segment.image(`base64://${base64}`));
return isV3 ? await e.reply(base64) : await e.reply(segment.image(`base64://${base64}`))
}
return true;
return true
}
export const todoV3 = function (e) {
if (isV3) {
e.reply('本功能暂时不支持V3版Yunzai...')
return true
}
return false
}
export default {
render,
cfg: Cfg.get,
isDisable: Cfg.isDisable
};
isDisable: Cfg.isDisable,
todoV3
}

View File

@ -1,24 +1,23 @@
import lodash from "lodash";
import fs from "fs";
import request from "request";
import lodash from 'lodash'
import fs from 'fs'
const _path = process.cwd();
const _path = process.cwd()
let Data = {
/*
* 根据指定的path依次检查与创建目录
* */
createDir(rootPath = "", path = "", includeFile = false) {
let pathList = path.split("/"),
nowPath = rootPath;
createDir (rootPath = '', path = '', includeFile = false) {
let pathList = path.split('/')
let nowPath = rootPath
pathList.forEach((name, idx) => {
name = name.trim();
name = name.trim()
if (!includeFile && idx <= pathList.length - 1) {
nowPath += name + "/";
nowPath += name + '/'
if (name) {
if (!fs.existsSync(nowPath)) {
fs.mkdirSync(nowPath);
fs.mkdirSync(nowPath)
}
}
}
@ -28,15 +27,15 @@ let Data = {
/*
* 读取json
* */
readJSON(root, path) {
readJSON (root, path) {
if (!/\.json$/.test(path)) {
path = path + ".json";
path = path + '.json'
}
// 检查并创建目录
Data.createDir(root, path, true);
Data.createDir(root, path, true)
if (fs.existsSync(`${root}/${path}`)) {
let jsonRet = fs.readFileSync(`${root}/${path}`, "utf8");
return JSON.parse(jsonRet);
let jsonRet = fs.readFileSync(`${root}/${path}`, 'utf8')
return JSON.parse(jsonRet)
}
return {}
},
@ -44,27 +43,27 @@ let Data = {
/*
* 写JSON
* */
writeJson(path, file, data, space = "\t") {
writeJson (path, file, data, space = '\t') {
if (!/\.json$/.test(file)) {
file = file + ".json";
file = file + '.json'
}
// 检查并创建目录
Data.createDir(_path, path, false);
console.log(data);
delete data._res;
return fs.writeFileSync(`${_path}/${path}/${file}`, JSON.stringify(data, null, space));
Data.createDir(_path, path, false)
console.log(data)
delete data._res
return fs.writeFileSync(`${_path}/${path}/${file}`, JSON.stringify(data, null, space))
},
async importModule(path, file, rootPath = _path) {
async importModule (path, file, rootPath = _path) {
if (!/\.js$/.test(file)) {
file = file + ".js";
file = file + '.js'
}
// 检查并创建目录
Data.createDir(_path, path, true);
Data.createDir(_path, path, true)
if (fs.existsSync(`${_path}/${path}/${file}`)) {
let data = await import (`file://${_path}/${path}/${file}`);
return data || {};
let data = await import(`file://${_path}/${path}/${file}`)
return data || {}
}
return {}
},
@ -80,42 +79,42 @@ let Data = {
*
* */
getData(target, keyList = "", cfg = {}) {
target = target || {};
let defaultData = cfg.defaultData || {};
let ret = {};
getData (target, keyList = '', cfg = {}) {
target = target || {}
let defaultData = cfg.defaultData || {}
let ret = {}
// 分割逗号
if (typeof (keyList) === "string") {
keyList = keyList.split(",");
if (typeof (keyList) === 'string') {
keyList = keyList.split(',')
}
lodash.forEach(keyList, (keyCfg) => {
// 处理通过:指定 toKey & fromKey
let _keyCfg = keyCfg.split(":");
let keyTo = _keyCfg[0].trim(),
keyFrom = (_keyCfg[1] || _keyCfg[0]).trim(),
keyRet = keyTo;
let _keyCfg = keyCfg.split(':')
let keyTo = _keyCfg[0].trim()
let keyFrom = (_keyCfg[1] || _keyCfg[0]).trim()
let keyRet = keyTo
if (cfg.lowerFirstKey) {
keyRet = lodash.lowerFirst(keyRet);
keyRet = lodash.lowerFirst(keyRet)
}
if (cfg.keyPrefix) {
keyRet = cfg.keyPrefix + keyRet;
keyRet = cfg.keyPrefix + keyRet
}
// 通过Data.getVal获取数据
ret[keyRet] = Data.getVal(target, keyFrom, defaultData[keyTo], cfg);
ret[keyRet] = Data.getVal(target, keyFrom, defaultData[keyTo], cfg)
})
return ret;
return ret
},
getVal(target, keyFrom, defaultValue) {
return lodash.get(target, keyFrom, defaultValue);
getVal (target, keyFrom, defaultValue) {
return lodash.get(target, keyFrom, defaultValue)
},
getUrlPath(url) {
let reg = /^https*:\/\/(.*)\/(\w+\.(png|jpg|jpeg|webp))(\?.*)?$/;
let ret = reg.exec(url);
getUrlPath (url) {
let reg = /^https*:\/\/(.*)\/(\w+\.(png|jpg|jpeg|webp))(\?.*)?$/
let ret = reg.exec(url)
if (!ret) {
return false;
return false
}
return {
path: ret[1],
@ -124,91 +123,68 @@ let Data = {
url
}
},
pathExists(root, path) {
if (fs.existsSync(root + "/" + path)) {
return true;
pathExists (root, path) {
if (fs.existsSync(root + '/' + path)) {
return true
}
path = path.replace("\\", "/");
const dirList = path.split("/");
let currentDir = root;
path = path.replace('\\', '/')
const dirList = path.split('/')
let currentDir = root
for (let dir of dirList) {
currentDir = currentDir + "/" + dir;
currentDir = currentDir + '/' + dir
if (!fs.existsSync(currentDir)) {
fs.mkdirSync(currentDir);
fs.mkdirSync(currentDir)
}
}
return true;
return true
},
async asyncPool(poolLimit, array, iteratorFn) {
const ret = []; // 存储所有的异步任务
const executing = []; // 存储正在执行的异步任务
async asyncPool (poolLimit, array, iteratorFn) {
const ret = [] // 存储所有的异步任务
const executing = [] // 存储正在执行的异步任务
for (const item of array) {
// 调用iteratorFn函数创建异步任务
const p = Promise.resolve().then(() => iteratorFn(item, array));
const p = Promise.resolve().then(() => iteratorFn(item, array))
// 保存新的异步任务
ret.push(p);
ret.push(p)
// 当poolLimit值小于或等于总任务个数时进行并发控制
if (poolLimit <= array.length) {
// 当任务完成后,从正在执行的任务数组中移除已完成的任务
const e = p.then(() => executing.splice(executing.indexOf(e), 1));
executing.push(e); // 保存正在执行的异步任务
const e = p.then(() => executing.splice(executing.indexOf(e), 1))
executing.push(e) // 保存正在执行的异步任务
if (executing.length >= poolLimit) {
// 等待较快的任务执行完成
await Promise.race(executing);
await Promise.race(executing)
}
}
}
return Promise.all(ret);
return Promise.all(ret)
},
async cacheFile(fileList, cacheRoot) {
let ret = {};
let cacheFn = async function (url) {
let path = Data.getUrlPath(url);
if (fs.existsSync(`${cacheRoot}/${path.path}/${path.filename}`)) {
console.log("已存在,跳过 " + path.path + "/" + path.filename);
ret[url] = `${path.path}/${path.filename}`;
return true;
}
Data.pathExists(cacheRoot, path.path);
await request(url).pipe(fs.createWriteStream(`${cacheRoot}/${path.path}/` + path.filename));
console.log("下载成功: " + path.path + "/" + path.filname);
ret[url] = `${path.path}/${path.filename}`;
return true;
};
await Data.asyncPool(10, fileList, cacheFn);
return ret;
sleep (ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
},
sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
},
def() {
def () {
for (let idx in arguments) {
if (!lodash.isUndefined(arguments[idx])) {
return arguments[idx];
return arguments[idx]
}
}
},
eachStr: (arr, fn) => {
if (lodash.isString(arr)) {
arr = arr.replace(/\s*(;||、|)\s*/, ",");
arr = arr.split(",");
arr = arr.replace(/\s*(;||、|)\s*/, ',')
arr = arr.split(',')
}
lodash.forEach(arr, (str, idx) => {
if (!lodash.isUndefined(str)) {
fn(str.trim ? str.trim() : str, idx)
}
});
})
}
}
export default Data;
export default Data

View File

@ -1,158 +0,0 @@
import md5 from "md5";
import lodash from 'lodash';
import fetch from "node-fetch";
let MysApi = {
getUrl(type, uid, data = {}) {
let url = "https://api-takumi.mihoyo.com";
let game_record = "/game_record/app/genshin/api/";
let server = MysApi.getServer(uid);
let query, body;
switch (type) {
//首页宝箱
case "index":
url += game_record + "index";
query = `role_id=${uid}&server=${server}`;
break;
//深渊
case "spiralAbyss":
url += game_record + "spiralAbyss";
query = `role_id=${uid}&schedule_type=${data.schedule_type}&server=${server}`;
break;
//角色详情
case "character":
url += game_record + "character";
body = JSON.stringify(data);
break;
//树脂每日任务只能当前id
case "dailyNote":
url += game_record + "dailyNote";
query = `role_id=${uid}&server=${server}`;
break;
case "detail":
url += "/event/e20200928calculate/v1/sync/avatar/detail";
query = `uid=${uid}&region=${server}&avatar_id=${data.avatar_id}`;
break;
case "getAnnouncement":
url += "/game_record/card/wapi/getAnnouncement";
break;
case "getGameRecordCard":
url += "/game_record/card/wapi/getGameRecordCard";
query = `uid=${uid}`;//米游社id
break;
case "bbs_sign_info":
url += "/event/bbs_sign_reward/info";
query = `act_id=e202009291139501&region=${server}&uid=${uid}`;
break;
case "bbs_sign_home":
url += "/event/bbs_sign_reward/home";
query = `act_id=e202009291139501&region=${server}&uid=${uid}`;
break;
case "bbs_sign":
url += "/event/bbs_sign_reward/sign";
body = JSON.stringify({ act_id: "e202009291139501", region: server, uid: uid, });
break;
case "ys_ledger":
url = "https://hk4e-api.mihoyo.com/event/ys_ledger/monthInfo";
query = `month=${data.month}&bind_uid=${uid}&bind_region=${server}`;
break;
}
if (query) {
url += "?" + query;
}
let headers;
if (type === "bbs_sign") {
headers = MysApi.getHeaders_sign();
} else {
headers = MysApi.getHeaders(query, body);
}
return { url, headers, query, body };
},
getServer(uid) {
switch (uid.toString()[0]) {
case "1":
case "2":
return "cn_gf01"; //官服
case "5":
return "cn_qd01"; //B服
}
return "cn_gf01"; //官服
},
//# Github-@lulu666lulu
getDs(q = "", b = "") {
let n = "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs";
let t = Math.round(new Date().getTime() / 1000);
let r = Math.floor(Math.random() * 900000 + 100000);
let DS = md5(`salt=${n}&t=${t}&r=${r}&b=${b}&q=${q}`);
return `${t},${r},${DS}`;
},
//签到ds
getDS_sign() {
const n = "h8w582wxwgqvahcdkpvdhbh2w9casgfl";
const t = Math.round(new Date().getTime() / 1000);
const r = lodash.sampleSize("abcdefghijklmnopqrstuvwxyz0123456789", 6).join("");
const DS = md5(`salt=${n}&t=${t}&r=${r}`);
return `${t},${r},${DS}`;
},
getHeaders(q = "", b = "") {
return {
"x-rpc-app_version": "2.20.1",
"x-rpc-client_type": 5,
DS: MysApi.getDs(q, b),
};
},
getHeaders_sign() {
return {
"x-rpc-app_version": "2.3.0",
"x-rpc-client_type": 5,
"x-rpc-device_id": MysApi.guid(),
"User-Agent": " miHoYoBBS/2.3.0",
DS: MysApi.getDS_sign(),
};
},
guid() {
function S4() {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}
return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
},
// 按type请求
request: async function (type, cfg) {
let { uid } = cfg;
let { url, headers } = MysApi.getUrl(type, uid);
return await MysApi.fetch(url, headers, cfg);
},
// 发送请求
fetch: async function (url, cfg) {
let { cookie, error, success, headers, method } = cfg;
headers = headers || {};
method = method || "get";
headers.Cookie = cookie;
let response = await fetch(url, { method, headers });
if (!response.ok) {
return await error(-1, {
msg: "米游社接口错误"
})
}
let res = await response.json();
if (res.retcode * 1 !== 0) {
return await error(res.retcode * 1, res)
}
return await success(res.data, res)
}
}
export default MysApi;

View File

@ -1,252 +1,249 @@
import Base from "./Base.js";
import lodash from "lodash";
import fs from "fs";
import Data from "../Data.js";
import sizeOf from "image-size";
import { customCharacters } from "../../config/character_default.js";
import Base from './Base.js'
import lodash from 'lodash'
import fs from 'fs'
import Data from '../Data.js'
import sizeOf from 'image-size'
let aliasMap = {}, idMap = {}, abbrMap = {}, wifeMap = {};
const _path = process.cwd();
let aliasMap = {};
let idMap = {};
let abbrMap = {};
let wifeMap = {}
const _path = process.cwd()
const metaPath = `${_path}/plugins/miao-plugin/resources/meta/character/`
async function init() {
let sysCfg = await Data.importModule(`config/genshin`, 'roleId.js'),
charCfg = await Data.importModule(`plugins/miao-plugin/config`, 'character_default.js'),
custom = await Data.importModule(`plugins/miao-plugin/config`, 'character.js');
async function init () {
let sysCfg = await Data.importModule('plugins/miao-plugin/config/system', 'character.js')
let custom = await Data.importModule('plugins/miao-plugin/config', 'character.js')
lodash.forEach([custom.customCharacters, charCfg.customCharacters, sysCfg.roleId], (roleIds) => {
lodash.forEach([custom.customCharacters, sysCfg.characters], (roleIds) => {
lodash.forEach(roleIds || {}, (aliases, id) => {
aliases = aliases || [];
aliases = aliases || []
if (aliases.length === 0) {
return;
return
}
// 建立别名映射
lodash.forEach(aliases || [], (alias) => {
aliasMap[alias] = id;
alias = alias.toLowerCase()
aliasMap[alias] = id
})
aliasMap[id] = id;
idMap[id] = aliases[0];
aliasMap[id] = id
idMap[id] = aliases[0]
})
})
lodash.forEach([sysCfg.wifeData, charCfg.wifeData, custom.wifeData], (wifeData) => {
lodash.forEach([sysCfg.wifeData, custom.wifeData], (wifeData) => {
lodash.forEach(wifeData || {}, (ids, type) => {
type = Data.def({ girlfriend: 0, boyfriend: 1, daughter: 2, son: 3 }[type], type);
type = Data.def({ girlfriend: 0, boyfriend: 1, daughter: 2, son: 3 }[type], type)
if (!wifeMap[type]) {
wifeMap[type] = {};
wifeMap[type] = {}
}
Data.eachStr(ids, (id) => {
id = aliasMap[id];
id = aliasMap[id]
if (id) {
wifeMap[type][id] = true;
wifeMap[type][id] = true
}
})
})
})
abbrMap = sysCfg.abbr;
abbrMap = sysCfg.abbr
}
await init();
await init()
class Character extends Base {
constructor(name, id) {
super();
constructor (name, id) {
super()
if (id * 1 === 10000005) {
name = "空";
name = '空'
} else if (id * 1 === 10000007) {
name = "荧";
name = '荧'
}
this.name = name;
lodash.extend(this, getMeta(name));
if (name === "主角" || name === "旅行者" || /.主/.test(name)) {
this.id = 20000000;
this.name = name
lodash.extend(this, getMeta(name))
if (name === '主角' || name === '旅行者' || /.主/.test(name)) {
this.id = 20000000
}
this.id = id;
this.id = id
}
getCardImg(se = false, def = true) {
let name = this.name;
const charImgPath = `./plugins/miao-plugin/resources/character-img/${name}/`;
let list = [];
getCardImg (se = false, def = true) {
let name = this.name
let list = []
let addImg = function (charImgPath, disable = false) {
let dirPath = `./plugins/miao-plugin/resources/${charImgPath}`;
let dirPath = `./plugins/miao-plugin/resources/${charImgPath}`
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath);
fs.mkdirSync(dirPath)
}
if (disable) {
return
}
let imgs = fs.readdirSync(dirPath);
imgs = imgs.filter((img) => /\.(png|jpg|webp|jpeg)/i.test(img));
let imgs = fs.readdirSync(dirPath)
imgs = imgs.filter((img) => /\.(png|jpg|webp|jpeg)/i.test(img))
lodash.forEach(imgs, (img) => {
list.push(`${charImgPath}/${img}`);
});
list.push(`${charImgPath}/${img}`)
})
}
addImg(`character-img/${name}`);
addImg(`character-img/${name}/upload`);
addImg(`character-img/${name}`)
addImg(`character-img/${name}/upload`)
addImg(`character-img/${name}/se`, !se)
const plusPath = `./plugins/miao-plugin/resources/miao-res-plus/`;
const plusPath = './plugins/miao-plugin/resources/miao-res-plus/'
if (fs.existsSync(plusPath)) {
addImg(`miao-res-plus/character-img/${name}`);
addImg(`miao-res-plus/character-img/${name}/se`, !se);
addImg(`miao-res-plus/character-img/${name}`)
addImg(`miao-res-plus/character-img/${name}/se`, !se)
}
let img = lodash.sample(list);
let img = lodash.sample(list)
if (!img) {
if (def) {
img = "/character-img/default/01.jpg";
img = '/character-img/default/01.jpg'
} else {
return false
}
}
let ret = sizeOf(`./plugins/miao-plugin/resources/${img}`);
ret.img = img;
ret.mode = ret.width > ret.height ? "left" : "bottom";
return ret;
let ret = sizeOf(`./plugins/miao-plugin/resources/${img}`)
ret.img = img
ret.mode = ret.width > ret.height ? 'left' : 'bottom'
return ret
}
checkAvatars(avatars) {
checkAvatars (avatars) {
if (!lodash.includes([20000000, 10000005, 10000007], this.id * 1)) {
return;
return
}
let avatarIds = [];
let avatarIds = []
if (lodash.isArray(avatars)) {
avatarIds = lodash.map(avatars, (a) => a.id * 1);
avatarIds = lodash.map(avatars, (a) => a.id * 1)
} else {
avatarIds = [avatars.id];
avatarIds = [avatars.id]
}
if (lodash.includes(avatarIds, 10000005)) {
// 空
lodash.extend(this, getMeta('空'));
lodash.extend(this, getMeta('空'))
} else if (lodash.includes(avatarIds, 10000007)) {
// 荧
lodash.extend(this, getMeta('荧'));
lodash.extend(this, getMeta('荧'))
}
}
getAvatarTalent(talent = {}, cons = 0, mode = "level") {
let ret = {};
let consTalent = this.getConsTalent();
getAvatarTalent (talent = {}, cons = 0, mode = 'level') {
let ret = {}
let consTalent = this.getConsTalent()
lodash.forEach(['a', 'e', 'q'], (key) => {
let ds = talent[key];
let ds = talent[key]
if (ds) {
let level;
let level
if (lodash.isNumber(ds)) {
level = ds;
level = ds
} else {
level = mode === "level" ? ds.level || ds.level_current || ds.original || ds.level_original :
ds.original || ds.level_original || ds.level || ds.level_current;
level = mode === 'level' ? ds.level || ds.level_current || ds.original || ds.level_original : ds.original || ds.level_original || ds.level || ds.level_current
}
if (mode === "level") {
if (mode === 'level') {
// 基于level计算original
ret[key] = {
level,
original: (key !== "a" && cons >= consTalent[key]) ? (level - 3) : level
original: (key !== 'a' && cons >= consTalent[key]) ? (level - 3) : level
}
} else {
// 基于original计算level
ret[key] = {
original: level,
level: (key !== "a" && cons >= consTalent[key]) ? (level + 3) : level
level: (key !== 'a' && cons >= consTalent[key]) ? (level + 3) : level
}
}
}
})
return ret;
return ret
}
getConsTalent() {
let talent = this.talent || false;
getConsTalent () {
let talent = this.talent || false
if (!talent) {
return { e: 3, q: 5 }
}
let e = talent.e.name,
q = talent.q.name;
let c3 = this.cons['3'].desc,
c5 = this.cons['5'].desc;
let e = talent.e.name
let q = talent.q.name
let c3 = this.cons['3'].desc
let c5 = this.cons['5'].desc
return {
e: c3.includes(e) ? 3 : 5,
q: c5.includes(q) ? 5 : 3,
q: c5.includes(q) ? 5 : 3
}
}
get weaponType() {
get weaponType () {
const map = {
sword: "单手剑",
catalyst: "法器",
bow: "弓",
claymore: "双手剑",
polearm: "长柄武器"
sword: '单手剑',
catalyst: '法器',
bow: '弓',
claymore: '双手剑',
polearm: '长柄武器'
}
let weaponType = this.weapon || "";
return map[weaponType.toLowerCase()] || "";
let weaponType = this.weapon || ''
return map[weaponType.toLowerCase()] || ''
}
get isCustom() {
return !/10\d{6}/.test(this.id);
get isCustom () {
return !/10\d{6}/.test(this.id)
}
checkWifeType(type) {
return !!wifeMap[type][this.id];
checkWifeType (type) {
return !!wifeMap[type][this.id]
}
}
let getMeta = function (name) {
return Data.readJSON(`${_path}/plugins/miao-plugin/resources/meta/character/${name}/`, "data.json") || {};
return Data.readJSON(`${_path}/plugins/miao-plugin/resources/meta/character/${name}/`, 'data.json') || {}
}
Character.get = function (val) {
let id, name;
let id, name
if (!val) {
return false;
return false
}
if (typeof (val) === "number" || /^\d*$/.test(val)) {
id = val;
if (typeof (val) === 'number' || /^\d*$/.test(val)) {
id = val
} else if (val.id) {
id = val.id;
name = val.name || idMap[id];
id = val.id
name = val.name || idMap[id]
} else {
id = aliasMap[val];
id = aliasMap[val]
}
if (!name) {
name = idMap[id];
name = idMap[id]
}
if (!name) {
return false;
return false
}
return new Character(name, id);
};
return new Character(name, id)
}
Character.getAbbr = function () {
return abbrMap;
return abbrMap
}
Character.checkWifeType = function (charid, type) {
return !!wifeMap[type][charid];
}
Character.getRandomImg = function (type) {
let chars = fs.readdirSync(metaPath);
let ret = [];
type = type === "party" ? "party" : "profile";
let chars = fs.readdirSync(metaPath)
let ret = []
type = type === 'party' ? 'party' : 'profile'
lodash.forEach(chars, (char) => {
if (fs.existsSync(`${metaPath}/${char}/${type}.png`)) {
ret.push(`/meta/character/${char}/${type}.png`);
ret.push(`/meta/character/${char}/${type}.png`)
}
});
return lodash.sample(ret);
})
return lodash.sample(ret)
}
let charPosIdx = {
1: '宵宫,雷神,胡桃,甘雨,优菈,一斗,公子,绫人,魈,可莉,迪卢克,凝光,刻晴,辛焱,烟绯,雷泽',
2: '夜兰,八重,九条,行秋,香菱,安柏,凯亚,丽莎,北斗,菲谢尔,重云,罗莎莉亚,埃洛伊',
@ -254,20 +251,19 @@ let charPosIdx = {
4: '班尼特,心海,琴,芭芭拉,七七,迪奥娜,托马,空,荧,阿贝多,钟离'
}
let idSort = {};
let idSort = {}
lodash.forEach(charPosIdx, (chars, pos) => {
chars = chars.split(",");
chars = chars.split(',')
lodash.forEach(chars, (name, idx) => {
let id = aliasMap[name];
let id = aliasMap[name]
if (id) {
idSort[id] = pos * 100 + idx;
idSort[id] = pos * 100 + idx
}
})
})
Character.sortIds = function (arr) {
return arr.sort((a, b) => (idSort[a] || 300) - (idSort[b] || 300));
return arr.sort((a, b) => (idSort[a] || 300) - (idSort[b] || 300))
}
export default Character;
export default Character

View File

@ -15,31 +15,10 @@ export const customCharacters = {
// 已有角色添加别名示例:为魈增加新的别名
// roleid请参见Yunzai roleId.js
10000026: ["魈", "风夜叉"],
10000059: ["鹿野院平藏", "小鹿"],
// ID暂无使用临时ID代替
10000100: ["提纳里", "提那里", "驴"],
10000101: ["柯莱", "柯来", "科莱", "科来", "小天使"],
10000102: ["多莉", "多利", "多力"],
10000026: ['魈', '风夜叉'],
// 以下为新增自定义角色角色id请以小写英文定义
paimon: ["派蒙", "应急食物", "应急食品", "吉祥物", "宠物", "外置器官", "会说话的动物", "矮堇瓜", "飞行矮堇瓜", "最好的伙伴"],
sb: ["散兵", "国崩", "雷电国崩", "大炮", "雷电大炮", "雷大炮", "伞兵"],
nvshi: ["女士", "炽热的炎之魔女", "炎之魔女"],
baizhu: ["白术", "长生"],
yaoyao: ["瑶瑶", "遥遥", "遥遥无期"],
fanan: ["伐难", "水夜叉"],
yingda: ["应达", "火夜叉", "火鼠大将"],
ping: ["萍姥姥", "歌尘浪市真君", "歌尘浪市", "萍儿"],
puren: ["阿蕾奇诺", "仆人", "黑优菈", "黑暗优菈"],
shaonv: ["哥伦比娅", "少女"],
furen: ["潘塔罗涅", "富人", "黑白术", "黑术"],
boshi: ["多托雷", "博士"],
muou: ["桑多涅", "木偶"]
sb: ['散兵', '国崩']
}
/*
@ -48,13 +27,13 @@ export const customCharacters = {
* */
export const wifeData = {
// 老婆&女朋友:成女、少女
girlfriend: "伐难, 女士, 萍姥姥, 柯莱, 多莉",
girlfriend: '雷神',
// 老公&男朋友:成男、少男
boyfriend: "散兵, 白术, 提纳里",
boyfriend: '散兵, 魈',
// 女儿:萝莉
daughter: "派蒙, 瑶瑶",
daughter: '派蒙, 瑶瑶',
// 儿子:正太
son: ""
son: ''
}

159
config/system/character.js Normal file
View File

@ -0,0 +1,159 @@
/*
* 请不要直接修改此或删除此文件防止后续更新冲突
* 如需新增自定义角色可复制此文件改名为character.js
* 复制的character.js中可按格式及自己需求进行配置
* 最终character.js character_default.js两份配置会叠加生效
*
* 暂未做热更新修改完毕请重启yunzai
* */
/*
* 角色列表别名的第一个是标准名字后面的为别名
* 实装的角色需要以数字roleid为key自定义的角色及非实装角色请以英文为key
* */
export const characters = {
10000003: ['琴', 'Jean', '团长', '代理团长', '琴团长', '蒲公英骑士'],
10000006: ['丽莎', 'Lisa', '图书管理员', '图书馆管理员', '蔷薇魔女', '阿姨'],
10000005: ['空', '男主', '男主角', '龙哥', '空哥', '男爷'],
10000007: ['荧', '女主', '女主角', '莹', '萤', '黄毛阿姨', '荧妹', '女爷', '包包', '宴宁'],
20000000: ['主角', '旅行者', '卑鄙的外乡人', '荣誉骑士', '爷', '风主', '岩主', '雷主', '履刑者', '抽卡不歪真君'],
10000014: ['芭芭拉', 'Barbara', '巴巴拉', '拉粑粑', '拉巴巴', '内鬼', '加湿器', '肉身解咒', '肉身解咒真君', '闪耀偶像', '偶像'],
10000015: ['凯亚', 'Kaeya', '盖亚', '凯子哥', '凯鸭', '矿工', '矿工头子', '骑兵队长', '凯子', '凝冰渡海真君'],
10000016: ['迪卢克', 'Diluc', '卢姥爷', '姥爷', '卢老爷', '卢锅巴', '正义人', '正e人', '正E人', '卢本伟', '暗夜英雄', '卢卢伯爵', '落魄了', '落魄了家人们'],
10000020: ['雷泽', 'Razor', '狼少年', '狼崽子', '狼崽', '卢皮卡', '小狼', '小狼狗', '狼孩'],
10000021: ['安柏', 'Amber', '安伯', '兔兔伯爵', '飞行冠军', '侦查骑士', '点火姬', '点火机', '打火机', '打火姬'],
10000022: ['温迪', 'Venti', '温蒂', '风神', '卖唱的', '巴巴托斯', '巴巴脱丝', '芭芭托斯', '芭芭脱丝', '干点正事', '不干正事', '吟游诗人', '诶嘿', '唉嘿', '摸鱼'],
10000023: ['香菱', 'Xiangling', '香玲', '锅巴', '厨师', '万民堂厨师', '香师傅'],
10000024: ['北斗', 'Beidou', '大姐头', '大姐', '无冕的龙王', '龙王'],
10000025: ['行秋', 'Xingqiu', '秋秋人', '秋妹妹', '书呆子', '水神', '飞云商会二少爷'],
10000026: ['魈', 'Xiao', '打桩机', '插秧', '三眼五显仙人', '三眼五显真人', '降魔大圣', '护法夜叉', '快乐风男', '无聊', '靖妖傩舞', '矮子仙人', '三点五尺仙人', '跳跳虎', '风夜叉'],
10000027: ['凝光', 'Ningguang', '富婆', '天权星', '天权'],
10000029: ['可莉', '逃跑的太阳', 'Klee', '嘟嘟可', '火花骑士', '蹦蹦炸弹', '炸鱼', '放火烧山', '放火烧山真君', '蒙德最强战力', '逃跑的太阳', '啦啦啦', '哒哒哒', '炸弹人', '禁闭室', '太阳', '小太阳'],
10000030: ['钟离', 'Zhongli', '摩拉克斯', '岩王爷', '岩神', '钟师傅', '天动万象', '岩王帝君', '未来可期', '帝君', '拒收病婿'],
10000031: ['菲谢尔', 'Fischl', '皇女', '小艾米', '小艾咪', '奥兹', '断罪皇女', '中二病', '中二少女', '中二皇女', '奥兹发射器'],
10000032: ['班尼特', 'Bennett', '点赞哥', '点赞', '倒霉少年', '倒霉蛋', '霹雳闪雷真君', '班神', '班爷', '倒霉', '火神', '六星真神'],
10000033: ['达达利亚', 'Tartaglia', 'Childe', 'Ajax', '达达鸭', '达达利鸭', '公子', '玩具销售员', '玩具推销员', '钱包', '鸭鸭', '愚人众末席'],
10000034: ['诺艾尔', 'Noelle', '女仆', '高达', '岩王帝姬'],
10000035: ['七七', 'Qiqi', '僵尸', '肚饿真君', '度厄真君', '77'],
10000036: ['重云', 'Chongyun', '纯阳之体', '冰棍'],
10000037: ['甘雨', 'Ganyu', '椰羊', '椰奶', '王小美'],
10000038: ['阿贝多', 'Albedo', '可莉哥哥', '升降机', '升降台', '电梯', '白垩之子', '贝爷', '白垩', '阿贝少', '花呗多', '阿贝夕', '阿师傅'],
10000039: ['迪奥娜', 'Diona', '迪欧娜', 'dio娜', '冰猫', '猫猫', '猫娘', '喵喵', '调酒师'],
10000041: ['莫娜', 'Mona', '穷鬼', '穷光蛋', '穷', '莫纳', '占星术士', '占星师', '讨龙真君', '半部讨龙真君', '阿斯托洛吉斯·莫娜·梅姬斯图斯', '梅姬斯图斯', '梅姬斯图斯姬'],
10000042: ['刻晴', 'Keqing', '刻情', '氪晴', '刻师傅', '刻师父', '牛杂', '牛杂师傅', '斩尽牛杂', '免疫', '免疫免疫', '屁斜剑法', '玉衡星', '阿晴', '啊晴', '璃月雷神'],
10000043: ['砂糖', 'Sucrose', 'sucrose'],
10000044: ['辛焱', 'Xinyan', '辛炎', '黑妹', '摇滚'],
10000045: ['罗莎莉亚', 'Rosaria', '罗莎莉娅', '白色史莱姆', '白史莱姆', '修女', '罗莎利亚', '罗莎利娅', '罗沙莉亚', '罗沙莉娅', '罗沙利亚', '罗沙利娅', '萝莎莉亚', '萝莎莉娅', '萝莎利亚', '萝莎利娅', '萝沙莉亚', '萝沙莉娅', '萝沙利亚', '萝沙利娅'],
10000046: ['胡桃', 'Hu Tao', 'HuTao', 'Hutao', '胡淘', '往生堂堂主', '火化', '抬棺的', '蝴蝶', '核桃', '堂主', '胡堂主', '雪霁梅香', '桃子', '桃'],
10000047: ['枫原万叶', 'Kaedehara Kazuha', 'Kazuha', '万叶', '叶天帝', '天帝', '叶师傅'],
10000048: ['烟绯', 'Yanfei', '烟老师', '律师', '罗翔'],
10000051: ['优菈', 'Eula', '优拉', '尤拉', '尤菈', '浪花骑士', '记仇', '劳伦斯'],
// 2.0
10000002: ['神里绫华', 'Kamisato Ayaka', 'Ayaka', '神里', '绫华', '神里凌华', '凌华', '白鹭公主', '神里大小姐', '龟', '龟龟', '乌龟'],
10000049: ['宵宫', 'Yoimiya', '霄宫', '烟花', '肖宫', '肖工', '绷带女孩'],
10000052: ['雷电将军', 'Raiden Shogun', 'Raiden', '雷神', '将军', '雷军', '巴尔', '阿影', '影', '巴尔泽布', '煮饭婆', '奶香一刀', '无想一刀', '宅女'],
10000053: ['早柚', 'Sayu', '小狸猫', '狸猫', '忍者', '貉'],
10000054: ['珊瑚宫心海', 'Sangonomiya Kokomi', 'Kokomi', '心海', '军师', '珊瑚宫', '书记', '观赏鱼', '水母', '鱼', '美人鱼'],
10000056: ['九条裟罗', 'Kujou Sara', 'Sara', '九条', '九条沙罗', '裟罗', '沙罗', '天狗'],
10000062: ['埃洛伊', 'Aloy'],
10000050: ['托马', 'Thoma', '家政官', '太郎丸', '地头蛇', '男仆', '拖马'],
10000055: ['五郎', 'Gorou', '柴犬', '土狗', '希娜', '希娜小姐'],
10000057: ['荒泷一斗', 'Arataki Itto', 'Itto', '荒龙一斗', '荒泷天下第一斗', '一斗', '一抖', '荒泷', '1斗', '牛牛', '斗子哥', '牛子哥', '牛子', '孩子王', '斗虫', '巧乐兹', '放牛的'],
10000058: ['八重神子', 'Yae Miko', 'Miko', '八重', '神子', '狐狸', '想得美哦', '巫女', '屑狐狸', '骚狐狸', '八重宫司', '婶子', '小八', '八重寄子', '寄子', '八神虫子', '八神重子'],
10000059: ['鹿野院平藏', 'shikanoin heizou', 'Heizou', '鹿野苑', '鹿野院', '平藏', '鹿野苑平藏', '小鹿'],
10000060: ['夜兰', 'Yelan', '夜阑', '叶澜', '腋兰', '夜天后'],
10000063: ['申鹤', 'Shenhe', '神鹤', '小姨', '小姨子', '审鹤'],
10000064: ['云堇', 'Yun Jin', 'yun jin', '云瑾', '云先生', '云锦', '神女劈观'],
10000065: ['久岐忍', 'Kuki Shinobu', 'Kuki', 'Shinobu', '97忍', '小忍', '久歧忍', '97', '茄忍', '茄子', '紫茄子', '阿忍', '忍姐'],
10000066: ['神里绫人', 'Kamisato Ayato', 'Ayato', '绫人', '神里凌人', '凌人', '0人', '神人', '零人', '大舅哥'],
// 3.0
// ID暂无使用临时ID代替
10000100: ['提纳里', '提那里', '驴'],
10000101: ['柯莱', '柯来', '科莱', '科来', '小天使'],
10000102: ['多莉', '多利', '多力'],
// 以下为Miao新增自定义角色
paimon: ['派蒙', '应急食物', '应急食品', '吉祥物', '宠物', '外置器官', '会说话的动物', '矮堇瓜', '飞行矮堇瓜', '最好的伙伴'],
sb: ['散兵', '国崩', '雷电国崩', '大炮', '雷电大炮', '雷大炮', '伞兵'],
nvshi: ['女士', '炽热的炎之魔女', '炎之魔女'],
baizhu: ['白术', '长生'],
yaoyao: ['瑶瑶', '遥遥', '遥遥无期'],
fanan: ['伐难', '水夜叉'],
yingda: ['应达', '火夜叉', '火鼠大将'],
ping: ['萍姥姥', '歌尘浪市真君', '歌尘浪市', '萍儿'],
puren: ['阿蕾奇诺', '仆人', '黑优菈', '黑暗优菈'],
shaonv: ['哥伦比娅', '少女'],
furen: ['潘塔罗涅', '富人', '黑白术', '黑术'],
boshi: ['多托雷', '博士'],
muou: ['桑多涅', '木偶']
}
/*
* 追加设置每个关系的可选角色会与yunzai的设置同时起作用
* 一个角色可以在多个关系中
* */
export const wifeData = {
// 老婆&女朋友:成女、少女
girlfriend: `琴,丽莎,荧,芭芭拉,安柏,香菱,北斗,凝光,菲谢尔,诺艾尔,甘雨,莫娜,刻晴,砂糖,辛焱,罗莎莉亚,胡桃,烟绯,优菈,神里绫华
宵宫,雷电将军,珊瑚宫心海,九条裟罗,八重神子,埃洛伊,申鹤,云堇,夜兰,久岐忍,柯莱,多莉,伐难, 女士,萍姥姥,柯莱,多莉`,
// 老公&男朋友:成男、少男
boyfriend: `空,凯亚,迪卢克,雷泽,温迪,行秋,魈,钟离,班尼特,达达利亚,重云,阿贝多,枫原万叶,托马,五郎,荒泷一斗,鹿野院平藏,神里绫人
提纳里,散兵,白术,提纳里`,
// 女儿:萝莉
daughter: '可莉,七七,迪奥娜,早柚,派蒙,瑶瑶',
// 儿子:正太
son: ''
}
export const abbr = {
达达利亚: '公子',
神里绫华: '绫华',
神里绫人: '绫人',
枫原万叶: '万叶',
雷电将军: '雷神',
珊瑚宫心海: '心海',
荒泷一斗: '一斗',
八重神子: '八重',
九条裟罗: '九条',
罗莎莉亚: '罗莎',
鹿野院平藏: '平藏',
松籁响起之时: '松籁',
无工之剑: '无工',
狼的末路: '狼末',
苍古自由之誓: '苍古',
雾切之回光: '雾切',
终末嗟叹之诗: '终末',
阿莫斯之弓: '阿莫斯',
冬极白星: '冬极',
飞雷之弦振: '飞雷',
护摩之杖: '护摩',
薙草之稻光: '薙刀',
赤角石溃杵: '赤角',
嘟嘟可故事集: '嘟嘟可',
讨龙英杰谭: '讨龙',
'「渔获」': '渔获',
天目影打刀: '天目刀',
喜多院十文字: '喜多院',
雪葬的星银: '雪葬星银',
辰砂之纺锤: '辰砂纺锤',
万国诸海图谱: '万国图谱',
神乐之真意: '神乐',
证誓之明瞳: '证誓明瞳',
波乱月白经津: '波乱',
笼钓瓶一心: '笼钓瓶',
角斗士的终幕礼: '角斗士',
流浪大地的乐团: '流浪乐团',
华馆梦醒形骸记: '华馆梦醒',
平息鸣雷的尊者: '平雷尊者',
炽烈的炎之魔女: '炽烈魔女',
渡过烈火的贤人: '渡火贤人',
冰风迷途的勇士: '冰风勇士'
}

View File

View File

@ -1,12 +0,0 @@
/*
* 请将此文件复制一份命名为 config.js 放置在miao-plugin的根目录
* 由于服务侧压力过大目前已经暂停 #角色面板 token的发放请见谅~
* */
export const config = {
/*miaoApi: {
api: "http://49.232.91.210:88/miaoPlugin/getRoleAll", // Miao APi 地址
qq: "", // 申请的主人qq
token: "" // 申请的token申请API Token请在Bot群内联系 @喵喵 或者 @九章
}*/
}

152
index.js
View File

@ -1,136 +1,26 @@
export {
character,
getProfile,
wife,
pokeWife,
enemyLv,
profileArtisList,
getProfileAll,
profileHelp,
getOriginalPicture,
uploadCharacterImg
} from "./apps/character.js";
// 适配V3 Yunzai将index.js移至app/index.js
import { currentVersion, isV3 } from './components/Changelog.js'
import Data from './components/Data.js'
import { wifeReg } from "./apps/character.js";
export * from './apps/index.js'
let index = { miao: {} }
if (isV3) {
index = await Data.importModule('/plugins/miao-plugin/adapter', 'index.js')
console.log(index)
}
export const miao = index.miao || {}
import { consStat, abyssPct, abyssTeam, uploadData } from "./apps/stat.js";
import { wiki, calendar } from "./apps/wiki.js";
import { help, versionInfo } from "./apps/help.js";
import lodash from "lodash";
import common from "../../lib/common.js";
import { rule as adminRule, updateRes, sysCfg, updateMiaoPlugin, profileCfg } from "./apps/admin.js";
import { currentVersion } from "./components/Changelog.js";
export {
consStat,
abyssPct,
abyssTeam,
wiki,
updateRes,
updateMiaoPlugin,
sysCfg,
help,
versionInfo,
calendar,
profileCfg,
uploadData
};
let rule = {
character: {
reg: "^(#(.*)|#*(更新|录入)?(.*)(详细|详情|面板|面版|伤害[1-7]?)(更新)?)$",
//reg: "noCheck",
describe: "【#角色】角色详情",
},
uploadCharacterImg: {
reg: "^#*(喵喵)?(上传|添加)(.+)(照片|写真|图片|图像)\\s*$",
describe: "喵喵上传角色写真",
},
profileArtisList: {
reg: "^#圣遗物列表\\s*(\\d{9})?$",
describe: "【#角色】圣遗物列表",
},
getProfileAll: {
reg: "^#(面板角色|角色面板|面板)(列表)?\\s*(\\d{9})?$",
describe: "【#角色】查看当前已获取面板数据的角色列表",
},
profileHelp: {
reg: "^#角色面板帮助$",
describe: "【#角色】查看当前已获取面板数据的角色列表",
},
wife: {
reg: wifeReg,
describe: "【#角色】#老公 #老婆 查询",
},
pokeWife: {
reg: "#poke#",
describe: "【#角色】戳一戳",
},
getOriginalPicture: {
reg: "^#?(获取|给我|我要|求|发|发下|发个|发一下)?原图(吧|呗)?$",
describe: "【#原图】 回复角色卡片,可获取原图",
},
consStat: {
reg: "^#(喵喵)?角色(持有|持有率|命座|命之座|.命)(分布|统计|持有|持有率)?$",
describe: "【#统计】 #角色持有率 #角色5命统计",
},
abyssPct: {
reg: "^#(喵喵)?深渊(第?.{1,2}层)?(角色)?(出场|使用)(率|统计)*$",
describe: "【#统计】 #深渊出场率 #深渊12层出场率",
},
abyssTeam: {
reg: "#深渊(组队|配队)",
describe: "【#角色】 #深渊组队",
},
wiki: {
reg: "^(#|喵喵)?.*(天赋|技能|命座|命之座|资料|照片|写真|图片|图像)$",
describe: "【#资料】 #神里天赋 #夜兰命座",
},
help: {
reg: "^#?(喵喵)?(命令|帮助|菜单|help|说明|功能|指令|使用说明)$",
describe: "【#帮助】 #喵喵帮助",
},
getProfile: {
reg: "^#(全部面板更新|更新全部面板|获取游戏角色详情|更新面板|面板更新)\\s*(\\d{9})?$",
describe: "【#角色】 获取游戏橱窗详情数据",
},
enemyLv: {
reg: "^#(敌人|怪物)等级\\s*\\d{1,3}\\s*$",
describe: "【#角色】 设置伤害计算中目标敌人的等级",
},
versionInfo: {
reg: "^#?喵喵版本$",
describe: "【#帮助】 喵喵版本介绍",
},
calendar: {
reg: "^(#|喵喵)+(日历|日历列表)$",
describe: "【#日历】 活动日历",
},
uploadData: {
reg: "^#上传(深渊|数据)+$"
},
...adminRule
};
lodash.forEach(rule, (r) => {
r.priority = r.priority || 50;
r.prehash = true;
r.hashMark = true;
});
export { rule };
console.log(`喵喵插件${currentVersion}初始化~`);
console.log(`喵喵插件${currentVersion}初始化~`)
setTimeout(async function () {
let msgStr = await redis.get("miao:restart-msg");
if (msgStr) {
let msg = JSON.parse(msgStr);
await common.relpyPrivate(msg.qq, msg.msg);
await redis.del("miao:restart-msg");
let msgs = [`当前喵喵版本: ${currentVersion}`, `您可使用 #喵喵版本 命令查看更新信息`];
await common.relpyPrivate(msg.qq, msgs.join("\n"));
let msgStr = await redis.get('miao:restart-msg')
let relpyPrivate = async function () {
}
}, 1000);
if (msgStr) {
let msg = JSON.parse(msgStr)
await relpyPrivate(msg.qq, msg.msg)
await redis.del('miao:restart-msg')
let msgs = [`当前喵喵版本: ${currentVersion}`, '您可使用 #喵喵版本 命令查看更新信息']
await relpyPrivate(msg.qq, msgs.join('\n'))
}
}, 1000)

View File

@ -38,7 +38,7 @@
<div class="item arti">
{{if ds && ds.name && ds.main && ds.main.title && ds.main.title!="undefined"}}
<div class="arti-icon">
<img src="{{_sys_res_path}}/genshin/logo/reliquaries/{{ds.name}}.png"/>
<img src="{{_res_path}}/meta/reliquaries/icon/{{ds.name}}.png"/>
<span>+{{ds.level}}</span>
</div>
<div class="head">

View File

@ -17,7 +17,7 @@
<img src="{{_res_path}}meta/character/{{ds.avatar}}/side.png" onerror="whenError(this)"/>
</div>
<div class="arti-icon">
<img src="{{_sys_res_path}}/genshin/logo/reliquaries/{{ds.name}}.png"/>
<img src="{{_res_path}}/meta/reliquaries/icon/{{ds.name}}.png"/>
</div>
<div class="head">
<strong>{{ds.name}}</strong>

View File

@ -23,7 +23,7 @@
<div class="weapon">
<div class="weapon_cont">
<img title="{{weapon.name}}" src="{{_sys_res_path}}/genshin/logo/weapon/{{weapon.name}}.png"/>
<img title="{{weapon.name}}" src="{{_res_path}}/meta/weapons/icon/{{weapon.name}}.png"/>
<p class="weapon_lv">Lv.{{weapon.level}}</p>
<p class="weapon_affix">{{weapon.affix}}</p>

View File

@ -56,7 +56,7 @@
<div class="artis">
<div>
<div class="item weapon">
<img src="{{_sys_res_path}}/genshin/logo/weapon/{{weapon.name}}.png"/>
<img src="{{_res_path}}/meta/weapons/icon/{{weapon.name}}.png"/>
<div class="head">
<strong>{{weapon.name}}</strong>
<div class="star star-{{weapon.star}}"></div>
@ -73,7 +73,7 @@
<div class="item arti">
{{if ds && ds.name && ds.main && ds.main.title && ds.main.title!="undefined"}}
<div class="arti-icon">
<img src="{{_sys_res_path}}/genshin/logo/reliquaries/{{ds.name}}.png"/>
<img src="{{_res_path}}/meta/reliquaries/icon/{{ds.name}}.png"/>
<span>+{{ds.level}}</span>
</div>
<div class="head">
@ -114,7 +114,7 @@
<div class="artis input-mode">
<div class="item weapon">
<img src="{{_sys_res_path}}/genshin/logo/weapon/{{weapon.name}}.png"/>
<img src="{{_res_path}}/meta/weapons/icon/{{weapon.name}}.png"/>
<div class="head">
<strong>{{weapon.name}}</strong>
<div class="star star-{{weapon.star}}"></div>
@ -127,7 +127,7 @@
<div class="item arti">
{{if ds && ds.name }}
<div class="arti-icon">
<img src="{{_sys_res_path}}/genshin/logo/reliquaries/{{ds.name}}.png"/>
<img src="{{_res_path}}/meta/reliquaries/icon/{{ds.name}}.png"/>
<span>+{{ds.level}}</span>
</div>
<div class="head">

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -1,12 +1,12 @@
{{set ds = $data[0]}}
{{set {_res_path,_char,_sys_res_path} = $data[1]}}
{{set {_res_path,_char} = $data[1]}}
{{if ds.name && ds.main && ds.main[0] && ds.main[0]!="undefined"}}
<div class="item arti-detail">
<div class="avatar-icon">
<img src="{{_res_path}}/meta/character/{{ds.avatar}}/side.png" onerror="whenError(this)"/>
</div>
<div class="arti-icon">
<img src="{{_sys_res_path}}/genshin/logo/reliquaries/{{ds.name}}.png"/>
<img src="{{_res_path}}/meta/reliquaries/icon/{{ds.name}}.png"/>
</div>
<div class="head">
<strong>{{ds.name}}</strong>

View File

@ -1,9 +1,9 @@
{{set ds = $data[0]}}
{{set {_res_path,_char,_sys_res_path} = $data[1]}}
{{set {_res_path,_char} = $data[1]}}
<div class="item-card">
<span class="badge">
<img src="{{_sys_res_path}}/genshin/logo/side/{{ds.avatar==`旅行者`?`荧`:ds.avatar}}.png"/>
<img src="{{_res_path}}/meta/character/{{ds.avatar==`旅行者`?`荧`:ds.avatar}}/side.png"/>
</span>
<div class="box">
{{ if ds.affix_level>1}}
@ -12,7 +12,7 @@
</span>
{{/if}}
<div class="item-bg bg{{ds.rarity}}"></div>
<img class="item-img" src="{{_sys_res_path}}/genshin/logo/reliquaries/{{ds.name}}.png"/>
<img class="item-img" src="{{_res_path}}/meta/reliquaries/icon/{{ds.name}}.png"/>
<div class="item-desc artis-lvl">Lv.{{ds.level}}</div>
<div class="item-desc artis-name">{{ds.name}}</div>
</div>

View File

@ -1,5 +1,5 @@
{{set ds = $data[0]}}
{{set {_res_path, _char, _sys_res_path, uid} = $data[1]}}
{{set {_res_path, _char, uid} = $data[1]}}
{{set {attr} = ds}}
<div class="profile">

View File

@ -1,16 +1,15 @@
{{ set weapon = $data[0] }}
{{ set {_sys_res_path} = $data[1] }}
<div class="item-card">
<span class="badge">
<img src="{{_sys_res_path}}/genshin/logo/side/{{weapon.avatar==`旅行者`?`荧`:weapon.avatar}}.png"/>
<img src="{{_res_path}}/meta/character/{{weapon.avatar==`旅行者`?`荧`:weapon.avatar}}/side.png"/>
</span>
<div class="box">
{{ if weapon.affix_level>1}}
<span class="item-life life{{weapon.affix_level}}">{{weapon.affix_level}}</span>
{{/if}}
<div class="item-bg bg{{weapon.rarity}}"></div>
<img class="item-img" src="{{_sys_res_path}}genshin/logo/weapon/{{weapon.name}}.png"/>
<img class="item-img" src="{{_res_path}}/meta/weapons/icon//{{weapon.name}}.png"/>
<div class="item-desc weapon-lv">Lv.{{weapon.level}}</div>
<div class="item-desc weapon-name">{{weapon.showName || weapon.name}}</div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Some files were not shown because too many files have changed in this diff Show More