部分架构底层升级,文件结构调整

This commit is contained in:
yoimiya-kokomi 2022-08-24 09:07:06 +08:00
parent 90947380ee
commit 9686e9b277
46 changed files with 620 additions and 586 deletions

View File

@ -1,55 +1,20 @@
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'
import fs from 'fs'
import _puppeteer from './lib/puppeteer.js'
import _plugin from './lib/plugin.js'
export class miao extends plugin {
constructor () {
let rule = {
reg: '.+',
fnc: 'dispatch'
}
super({
name: 'miao-plugin',
desc: '喵喵插件',
event: 'message',
priority: 50,
rule: [rule]
})
Object.defineProperty(rule, 'log', {
get: () => !!this.isDispatch
})
}
accept () {
this.e.original_msg = this.e.original_msg || this.e.msg
}
async dispatch (e) {
let msg = e.original_msg || ''
if (!msg) {
return false
}
e.checkAuth = async function (cfg) {
return await checkAuth(e, cfg)
}
e.getMysApi = async function (cfg) {
return await getMysApi(e, cfg)
}
msg = msg.replace('#', '').trim()
msg = '#' + msg
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) {
this.isDispatch = true
return true
}
}
}
return false
const importV3 = async function (file, def, key = 'default') {
if (fs.existsSync(process.cwd() + file)) {
let obj = await import(`file://${process.cwd()}/${file}`)
return obj[key] || def
}
return def
}
let MysInfo = await importV3('/plugins/genshin/model/mys/mysInfo.js')
let plugin = await importV3('lib/plugins/plugin.js', _plugin)
let puppeteer = await importV3('/lib/puppeteer/puppeteer.js', _puppeteer)
export {
plugin,
MysInfo,
puppeteer
}

103
adapter/lib/plugin.js Normal file
View File

@ -0,0 +1,103 @@
/*
* V3版Yunzai plugin
* */
let stateArr = {}
export default class plugin {
/**
* @param name 插件名称
* @param dsc 插件描述
* @param event 执行事件默认message
* @param priority 优先级数字越小优先级越高
* @param rule.reg 命令正则
* @param rule.fnc 命令执行方法
* @param rule.event 执行事件默认message
* @param rule.log false时不显示执行日志
* @param rule.permission 权限 master,owner,admin,all
* @param task.name 定时任务名称
* @param task.cron 定时任务cron表达式
* @param task.fnc 定时任务方法名
* @param task.log false时不显示执行日志
*/
constructor (data) {
/** 插件名称 */
this.name = data.name
/** 插件描述 */
this.dsc = data.dsc
/** 监听事件默认message https://oicqjs.github.io/oicq/#events */
this.event = data.event || 'message'
/** 优先级 */
this.priority = data.priority || 5000
/** 定时任务,可以是数组 */
this.task = {
/** 任务名 */
name: '',
/** 任务方法名 */
fnc: data.task?.fnc || '',
/** 任务cron表达式 */
cron: data.task?.cron || ''
}
/** 命令规则 */
this.rule = data.rule || []
}
/**
* @param msg 发送的消息
* @param quote 是否引用回复
* @param data.recallMsg 群聊是否撤回消息0-1200不撤回
* @param data.at 是否at用户
*/
reply (msg = '', quote = false, data = {}) {
if (!this.e.reply || !msg) return false
return this.e.reply(msg, quote, data)
}
conKey (isGroup = false) {
if (isGroup) {
return `${this.name}.${this.e.group_id}`
} else {
return `${this.name}.${this.userId || this.e.user_id}`
}
}
/**
* @param type 执行方法
* @param isGroup 是否群聊
* @param time 操作时间默认120秒
*/
setContext (type, isGroup = false, time = 120) {
let key = this.conKey(isGroup)
if (!stateArr[key]) stateArr[key] = {}
stateArr[key][type] = this.e
if (time) {
/** 操作时间 */
setTimeout(() => {
if (stateArr[key][type]) {
delete stateArr[key][type]
this.e.reply('操作超时已取消', true)
}
}, time * 1000)
}
}
getContext () {
let key = this.conKey()
return stateArr[key]
}
getContextGroup () {
let key = this.conKey(true)
return stateArr[key]
}
/**
* @param type 执行方法
* @param isGroup 是否群聊
*/
finish (type, isGroup = false) {
if (stateArr[this.conKey(isGroup)] && stateArr[this.conKey(isGroup)][type]) {
delete stateArr[this.conKey(isGroup)][type]
}
}
}

226
adapter/lib/puppeteer.js Normal file
View File

@ -0,0 +1,226 @@
/*
* V3版Yunzai puppeteer
* */
import template from 'art-template'
import fs from 'fs'
import lodash from 'lodash'
import { segment } from 'oicq'
const _path = process.cwd()
let puppeteer = {}
let logger = global.logger || Bot.logger
logger.green = logger.green || ((t) => t)
class Puppeteer {
constructor () {
this.browser = false
this.lock = false
this.shoting = []
/** 截图数达到时重启浏览器 避免生成速度越来越慢 */
this.restartNum = 400
/** 截图次数 */
this.renderNum = 0
this.config = {
headless: true,
args: [
'--disable-gpu',
'--disable-dev-shm-usage',
'--disable-setuid-sandbox',
'--no-first-run',
'--no-sandbox',
'--no-zygote',
'--single-process'
]
}
this.html = {}
this.watcher = {}
this.createDir('./data/html')
}
async initXlsx () {
if (!lodash.isEmpty(puppeteer)) return puppeteer
puppeteer = (await import('puppeteer')).default
}
createDir (dir) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir)
}
}
/**
* 初始化chromium
*/
async browserInit () {
await this.initXlsx()
if (this.browser) return this.browser
if (this.lock) return false
this.lock = true
logger.mark('puppeteer Chromium 启动中。。')
/** 初始化puppeteer */
this.browser = await puppeteer.launch(this.config).catch((err) => {
logger.error(err.toString())
if (String(err).includes('correct Chromium')) {
logger.error('没有正确安装Chromium可以尝试执行安装命令node ./node_modules/puppeteer/install.js')
}
})
this.lock = false
if (!this.browser) {
logger.error('puppeteer Chromium 启动失败')
return false
}
logger.mark('puppeteer Chromium 启动成功')
/** 监听Chromium实例是否断开 */
this.browser.on('disconnected', (e) => {
logger.error('Chromium实例关闭或崩溃')
this.browser = false
})
return this.browser
}
/**
* `chromium` 截图
* @param data 模板参数
* @param data.tplFile 模板路径必传
* @param data.saveId 生成html名称为空name代替
* @param data.imgType screenshot参数生成图片类型jpegpng
* @param data.quality screenshot参数图片质量 0-100jpeg是可传默认90
* @param data.omitBackground screenshot参数隐藏默认的白色背景背景透明默认不透明
* @param data.path screenshot参数截图保存路径截图图片类型将从文件扩展名推断出来如果是相对路径则从当前路径解析如果没有指定路径图片将不会保存到硬盘
* @return oicq img
*/
async screenshot (name, data = {}) {
if (!await this.browserInit()) {
return false
}
let savePath = this.dealTpl(name, data)
if (!savePath) return false
let buff = ''
let start = Date.now()
try {
this.shoting.push(name)
const page = await this.browser.newPage()
await page.goto(`file://${_path}${lodash.trim(savePath, '.')}`)
let body = await page.$('#container') || await page.$('body')
let randData = {
// encoding: 'base64',
type: data.imgType || 'jpeg',
omitBackground: data.omitBackground || false,
quality: data.quality || 90,
path: data.path || ''
}
if (data.imgType == 'png') delete randData.quality
buff = await body.screenshot(randData)
page.close().catch((err) => logger.error(err))
this.shoting.pop()
} catch (error) {
logger.error(`图片生成失败:${name}:${error}`)
/** 关闭浏览器 */
if (this.browser) {
await this.browser.close().catch((err) => logger.error(err))
}
this.browser = false
buff = ''
return false
}
if (!buff) {
logger.error(`图片生成为空:${name}`)
return false
}
this.renderNum++
/** 计算图片大小 */
let kb = (buff.length / 1024).toFixed(2) + 'kb'
logger.mark(`[图片生成][${name}][${this.renderNum}次] ${kb} ${logger.green(`${Date.now() - start}ms`)}`)
this.restart()
return segment.image(buff)
}
/** 模板 */
dealTpl (name, data) {
let { tplFile, saveId = name } = data
let savePath = `./data/html/${name}/${saveId}.html`
/** 读取html模板 */
if (!this.html[tplFile]) {
this.createDir(`./data/html/${name}`)
try {
this.html[tplFile] = fs.readFileSync(tplFile, 'utf8')
} catch (error) {
logger.error(`加载html错误${tplFile}`)
return false
}
this.watch(tplFile)
}
data.resPath = `${_path}/resources/`
/** 替换模板 */
let tmpHtml = template.render(this.html[tplFile], data)
/** 保存模板 */
fs.writeFileSync(savePath, tmpHtml)
logger.debug(`[图片生成][使用模板] ${savePath}`)
return savePath
}
/** 监听配置文件 */
watch (tplFile) {
/*
if (this.watcher[tplFile]) return
const watcher = chokidar.watch(tplFile)
watcher.on('change', path => {
delete this.html[tplFile]
logger.mark(`[修改html模板] ${tplFile}`)
})
this.watcher[tplFile] = watcher
*/
}
/** 重启 */
restart () {
/** 截图超过重启数时,自动关闭重启浏览器,避免生成速度越来越慢 */
if (this.renderNum % this.restartNum == 0) {
if (this.shoting.length <= 0) {
setTimeout(async () => {
this.browser.removeAllListeners('disconnected')
await this.browser.close().catch((err) => logger.error(err))
this.browser = false
logger.mark('puppeteer 关闭重启')
}, 100)
}
}
}
}
export default new Puppeteer()

View File

@ -1,5 +1,5 @@
import MysInfo from '../../genshin/model/mys/mysInfo.js'
import lodash from 'lodash'
import { MysInfo } from './index.js'
class User {
constructor (cfg) {

View File

@ -1,21 +0,0 @@
import lodash from 'lodash'
import {Data} from '../components/index.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)
}

50
adapter/v3-entrance.js Normal file
View File

@ -0,0 +1,50 @@
import * as Miao from '../apps/index.js'
import { checkAuth, getMysApi } from './mys.js'
import { plugin } from './index.js'
export class miao extends plugin {
constructor () {
let rule = {
reg: '.+',
fnc: 'dispatch'
}
super({
name: 'miao-plugin',
desc: '喵喵插件',
event: 'message',
priority: 50,
rule: [rule]
})
Object.defineProperty(rule, 'log', {
get: () => !!this.isDispatch
})
}
accept () {
this.e.original_msg = this.e.original_msg || this.e.msg
}
async dispatch (e) {
let msg = e.original_msg || ''
if (!msg) {
return false
}
e.checkAuth = async function (cfg) {
return await checkAuth(e, cfg)
}
e.getMysApi = async function (cfg) {
return await getMysApi(e, cfg)
}
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, {})
if (ret === true) {
this.isDispatch = true
return true
}
}
}
return false
}
}

View File

@ -53,7 +53,7 @@ const checkAuth = async function (e) {
})
}
export async function sysCfg (e, { render }) {
export async function sysCfg (e) {
if (!await checkAuth(e)) {
return true
}
@ -100,7 +100,7 @@ export async function sysCfg (e, { render }) {
// 渲染图像
return await Common.render('admin/index', {
...cfg
}, { e, render, scale: 1.4 })
}, { e, scale: 1.4 })
}
const getStatus = function (rote, def = true) {
@ -200,7 +200,7 @@ export async function updateMiaoPlugin (e) {
return true
}
export async function profileCfg (e, { render }) {
export async function profileCfg (e) {
if (!await checkAuth(e)) {
return true
}
@ -257,5 +257,5 @@ export async function profileCfg (e, { render }) {
// 渲染图像
return await Common.render('admin/profile', {
...cfg
}, { e, render, scale: 1.4 })
}, { e, scale: 1.4 })
}

View File

@ -25,7 +25,7 @@ export { profileList } from './character/profile-list.js'
export { profileStat } from './character/profile-stat.js'
// 查看当前角色
export async function character (e, { render }) {
export async function character (e) {
let msg = e.original_msg || e.msg
if (!msg) {
return
@ -95,7 +95,7 @@ export async function character (e, { render }) {
}
if (mode === 'card') {
return renderAvatar(e, char.name, render)
return renderAvatar(e, char.name)
}
let uid = await getTargetUid(e)
@ -111,15 +111,15 @@ export async function character (e, { render }) {
}
if (mode === 'profile' || mode === 'dmg') {
return renderProfile(e, char, render, mode, { dmgIdx })
return renderProfile(e, char, mode, { dmgIdx })
} else if (mode === 'input') {
await inputProfile(e, mode)
return true
} else if (mode === 'refresh') {
await getProfile(e, { render })
await getProfile(e)
return true
} else if (mode === 'artis') {
return profileArtis(e, { render })
return profileArtis(e)
}
return true
}

View File

@ -7,7 +7,7 @@ import { segment } from 'oicq'
let abbr = Character.getAbbr()
export async function renderAvatar (e, avatar, render, renderType = 'card') {
export async function renderAvatar (e, avatar, renderType = 'card') {
// 如果传递的是名字,则获取
if (typeof (avatar) === 'string') {
let char = Character.get(avatar)
@ -45,11 +45,11 @@ export async function renderAvatar (e, avatar, render, renderType = 'card') {
}
}
}
return await renderCard(e, avatar, render, renderType)
return await renderCard(e, avatar, renderType)
}
// 渲染角色卡片
async function renderCard (e, avatar, render, renderType = 'card') {
async function renderCard (e, avatar, renderType = 'card') {
let char = Character.get(avatar)
if (!char) {
@ -69,7 +69,7 @@ async function renderCard (e, avatar, render, renderType = 'card') {
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/character-card', {
save_id: uid,
uid,
talent,
@ -79,7 +79,7 @@ async function renderCard (e, avatar, render, renderType = 'card') {
custom: char.isCustom,
...getCharacterData(avatar),
ds: char.getData('name,id,title,desc')
}, { e, render, scale: 1.6, retMsgId: true })
}, { e, scale: 1.6, retMsgId: true })
if (msgRes && msgRes.message_id) {
// 如果消息发送成功就将message_id和图片路径存起来1小时过期
await redis.set(`miao:original-picture:${msgRes.message_id}`, bg.img, { EX: 3600 })

View File

@ -34,7 +34,7 @@ const relationMap = {
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 }) {
export async function wife (e, { User }) {
let msg = e.msg || ''
if (!msg && !e.isPoke) return false
@ -104,10 +104,10 @@ export async function wife (e, { render, User }) {
// 如果选择为全部,则从列表中随机选择一个
avatarList = await getAvatarList(e, targetCfg.type, MysApi)
let avatar = lodash.sample(avatarList)
return renderAvatar(e, avatar, render, renderType)
return renderAvatar(e, avatar, renderType)
} else {
// 如果指定过,则展示指定角色
return renderAvatar(e, lodash.sample(wifeList), render, renderType)
return renderAvatar(e, lodash.sample(wifeList), renderType)
}
}
}
@ -116,13 +116,13 @@ export async function wife (e, { render, User }) {
avatarList = await getAvatarList(e, false, MysApi)
if (avatarList && avatarList.length > 0) {
avatar = lodash.sample(avatarList)
return await renderAvatar(e, avatar, render, renderType)
return await renderAvatar(e, avatar, renderType)
}
} else {
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)
return await renderAvatar(e, avatar, renderType)
}
}
e.reply('在当前米游社公开展示的角色中未能找到适合展示的角色..')

View File

@ -52,7 +52,7 @@ export async function uploadCharacterImg (e) {
}
async function saveImages (e, name, imageMessages) {
let imgMaxSize = e.groupConfig.imgMaxSize || 1
let imgMaxSize = e?.groupConfig?.imgMaxSize || 5
let pathSuffix = `character-img/${name}/upload`
let path = resPath + pathSuffix

View File

@ -10,11 +10,11 @@ import { Artifact } from '../../models/index.js'
/*
* 角色圣遗物面板
* */
export async function profileArtis (e, { render }) {
export async function profileArtis (e) {
let { uid, avatar } = e
let { profile, char, err } = await autoGetProfile(e, uid, avatar, async () => {
await profileArtis(e, { render })
await profileArtis(e)
})
if (err) {
@ -42,13 +42,13 @@ export async function profileArtis (e, { render }) {
usefulMark,
attrMap,
charCfg
}, { e, render, scale: 1.3 })
}, { e, scale: 1.3 })
}
/*
* 圣遗物列表
* */
export async function profileArtisList (e, { render }) {
export async function profileArtisList (e) {
let uid = await getTargetUid(e)
if (!uid) {
return true
@ -85,9 +85,9 @@ export async function profileArtisList (e, { render }) {
artis = artis.slice(0, 28)
// 渲染图像
return await Common.render('character/artis', {
return await Common.render('character/artis-list', {
save_id: uid,
uid,
artis
}, { e, render, scale: 1.4 })
}, { e, scale: 1.4 })
}

View File

@ -133,7 +133,7 @@ export async function autoGetProfile (e, uid, avatar, callback) {
}
let profile = Profile.get(uid, char.id)
if (!profile) {
if (!profile || !profile.hasData) {
if (await refresh()) {
return { err: true }
} else {
@ -179,7 +179,7 @@ export async function inputProfile (e) {
/*
* 面板数据更新
* */
export async function getProfile (e, { render }) {
export async function getProfile (e) {
let uid = await getTargetUid(e)
if (!uid) {
return true
@ -205,7 +205,7 @@ export async function getProfile (e, { render }) {
e.reply('获取角色面板数据失败未能请求到角色数据。请确认角色已在游戏内橱窗展示并开放了查看详情。设置完毕后请5分钟后再进行请求~')
} else {
e.newChar = ret
return await profileList(e, { render })
return await profileList(e)
}
}
return true

View File

@ -2,7 +2,7 @@ import lodash from 'lodash'
import { autoRefresh } from './profile-common.js'
import { Common, Format, Profile } from '../../components/index.js'
export async function renderProfile (e, char, render, mode = 'profile', params = {}) {
export async function renderProfile (e, char, mode = 'profile', params = {}) {
let selfUser = await e.checkAuth({
auth: 'self'
})
@ -21,7 +21,7 @@ export async function renderProfile (e, char, render, mode = 'profile', params =
let refresh = async () => {
let refreshRet = await autoRefresh(e)
if (refreshRet) {
await renderProfile(e, char, render, mode, params)
await renderProfile(e, char, mode, params)
}
return refreshRet
}
@ -116,5 +116,5 @@ export async function renderProfile (e, char, render, mode = 'profile', params =
talentMap: { a: '普攻', e: '战技', q: '爆发' },
bodyClass: `char-${char.name}`,
mode
}, { e, render, scale: 1.6 })
}, { e, scale: 1.6 })
}

View File

@ -2,7 +2,7 @@ import lodash from 'lodash'
import { autoRefresh, getTargetUid } from './profile-common.js'
import { Common, Profile } from '../../components/index.js'
export async function profileList (e, { render }) {
export async function profileList (e) {
let uid = await getTargetUid(e)
if (!uid) {
return true
@ -38,7 +38,7 @@ export async function profileList (e, { render }) {
if (chars.length === 0) {
if (await autoRefresh(e)) {
await profileList(e, { render })
await profileList(e)
return true
} else {
e.reply('尚未获取任何角色数据')
@ -61,5 +61,5 @@ export async function profileList (e, { render }) {
servName,
hasNew,
msg
}, { e, render, scale: 1.6 })
}, { e, scale: 1.6 })
}

View File

@ -2,7 +2,7 @@ import lodash from 'lodash'
import { Common, Profile, Data } from '../../components/index.js'
import { Avatars } from '../../models/index.js'
export async function profileStat (e, { render }) {
export async function profileStat (e) {
// 缓存时间,单位小时
let msg = e.msg.replace('#', '').trim()
@ -68,5 +68,5 @@ export async function profileStat (e, { render }) {
isSelf: e.isSelf,
talentNotice,
elem: 'hydro'
}, { e, render, scale: 1.8 })
}, { e, scale: 1.8 })
}

View File

@ -5,7 +5,7 @@ import { Cfg, Version, Common } from '../components/index.js'
const _path = process.cwd()
const helpPath = `${_path}/plugins/miao-plugin/resources/help`
export async function help (e, { render }) {
export async function help (e) {
if (!/喵喵/.test(e.msg) && !Cfg.get('sys.help', false)) {
return false
}
@ -58,13 +58,13 @@ export async function help (e, { render }) {
helpCfg,
helpGroup,
element: 'default'
}, { e, render, scale: 1.2 })
}, { e, scale: 1.2 })
}
export async function versionInfo (e, { render }) {
export async function versionInfo (e) {
return await Common.render('help/version-info', {
currentVersion: Version.version,
changelogs: Version.changelogs,
elem: 'cryo'
}, { e, render, scale: 1.2 })
}, { e, scale: 1.2 })
}

View File

@ -8,7 +8,7 @@ import { Cfg, Common } from '../components/index.js'
import { Abyss, Avatars, Character } from '../models/index.js'
import HutaoApi from './stat/HutaoApi.js'
export async function consStat (e, { render }) {
export async function consStat (e) {
if (Cfg.isDisable(e, 'wiki.stat')) {
return
}
@ -87,10 +87,10 @@ export async function consStat (e, { render }) {
pct: function (num) {
return (num * 100).toFixed(2)
}
}, { e, render, scale: 1.5 })
}, { e, scale: 1.5 })
}
export async function abyssPct (e, { render }) {
export async function abyssPct (e) {
if (Cfg.isDisable(e, 'wiki.stat')) {
return
}
@ -171,7 +171,7 @@ export async function abyssPct (e, { render }) {
modeName,
totalCount: overview?.data?.collectedPlayerCount || 0,
lastUpdate: abyssData.lastUpdate
}, { e, render, scale: 1.5 })
}, { e, scale: 1.5 })
}
async function getTalentData (e, isUpdate = false) {
@ -202,7 +202,7 @@ async function getTalentData (e, isUpdate = false) {
return false
}
export async function abyssTeam (e, { render }) {
export async function abyssTeam (e) {
if (Common.todoV3(e)) {
return true
}
@ -393,10 +393,10 @@ export async function abyssTeam (e, { render }) {
return await Common.render('stat/abyss-team', {
teams: ret,
avatars: avatarMap
}, { e, render, scale: 1.5 })
}, { e, scale: 1.5 })
}
export async function uploadData (e, { render }) {
export async function uploadData (e) {
let isMatch = /^#(喵喵|上传)深渊(数据)?$/.test(e.original_msg || e.msg || '')
if (!Cfg.get('wiki.abyss', false) && !isMatch) {
return false
@ -498,7 +498,7 @@ export async function uploadData (e, { render }) {
save_id: uid,
totalCount: overview?.collectedPlayerCount || 0,
uid
}, { e, render, scale: 1.8 })
}, { e, scale: 1.8 })
} else {
e.reply('暂未获得本期深渊挑战数据...')
return true

View File

@ -11,7 +11,7 @@ let action = {
}
}
export async function wiki (e, { render }) {
export async function wiki (e) {
if (!e.msg) {
return false
}
@ -62,7 +62,7 @@ export async function wiki (e, { render }) {
mode,
line: getLineData(char),
_char: `/meta/character/${char.name}/`
}, { e, render, scale: 1 })
}, { e, scale: 1 })
}
const getLineData = function (data) {
@ -77,7 +77,7 @@ const getLineData = function (data) {
return ret
}
export async function calendar (e, { render }) {
export async function calendar (e) {
let calData = await Calendar.get()
let mode = 'calendar'
if (/(日历列表|活动)$/.test(e.msg)) {
@ -87,5 +87,5 @@ export async function calendar (e, { render }) {
return await Common.render('wiki/calendar', {
...calData,
displayMode: mode
}, { e, render, scale: 1.1 })
}, { e, scale: 1.1 })
}

View File

@ -1,13 +1,23 @@
import Cfg from './Cfg.js'
import { Version } from './index.js'
import { segment } from 'oicq'
import { Data, Version } from './index.js'
import { puppeteer } from '../adapter/index.js'
const plugin = 'miao-plugin'
const _path = process.cwd()
export const render = async function (path, params, cfg) {
let paths = path.split('/')
let { render, e } = cfg
let [app, tpl] = path.split('/')
let { e } = cfg
let layoutPath = process.cwd() + '/plugins/miao-plugin/resources/common/layout/'
let base64 = await render(paths[0], paths[1], {
let resPath = `../../../../../plugins/${plugin}/resources/`
Data.createDir(_path + '/data/', `html/${plugin}/${app}/${tpl}`)
let data = {
...params,
_plugin: plugin,
saveId: params.saveId || params.save_id || tpl,
tplFile: `./plugins/${plugin}/resources/${app}/${tpl}.html`,
pluResPath: resPath,
_res_path: resPath,
_layout_path: layoutPath,
_tpl_path: process.cwd() + '/plugins/miao-plugin/resources/common/tpl/',
defaultLayout: layoutPath + 'default.html',
@ -16,11 +26,11 @@ export const render = async function (path, params, cfg) {
scale: Cfg.scale(cfg.scale || 1),
copyright: `Created By Yunzai-Bot<span class="version">${Version.yunzai}</span> & Miao-Plugin<span class="version">${Version.version}</span>`
}
})
}
let base64 = await puppeteer.screenshot(`miao-plugin/${app}/${tpl}`, data)
let ret = true
if (base64) {
ret = Version.isV3 ? await e.reply(base64) : await e.reply(segment.image(`base64://${base64}`))
ret = await e.reply(base64)
}
return cfg.retMsgId ? ret : true
}

View File

@ -75,7 +75,7 @@ export const characters = {
// 以下为Miao新增自定义角色
paimon: ['派蒙', '应急食物', '应急食品', '吉祥物', '宠物', '外置器官', '会说话的动物', '矮堇瓜', '飞行矮堇瓜', '最好的伙伴'],
sb: ['散兵', '国崩', '雷电国崩', '大炮', '雷电大炮', '雷大炮', '伞兵', '斯卡拉姆齐'],
sanbing: ['散兵', '国崩', '雷电国崩', '大炮', '雷电大炮', '雷大炮', '伞兵', '斯卡拉姆齐'],
nvshi: ['女士', '炽热的炎之魔女', '炎之魔女', '罗莎琳'],
baizhu: ['白术', '长生'],
yaoyao: ['瑶瑶', '遥遥', '遥遥无期'],

View File

@ -1,14 +1,13 @@
// 适配V3 Yunzai将index.js移至app/index.js
import { Data, Version } from './components/index.js'
export * from './apps/index.js'
let index = { miao: {} }
if (Version.isV3) {
index = await Data.importModule('/plugins/miao-plugin/adapter', 'index.js')
index = await Data.importModule('/plugins/miao-plugin/adapter', 'v3-entrance.js')
}
export const miao = index.miao || {}
if (Bot?.logger?.info) {
Bot.logger.info(`---------^_^---------`)
Bot.logger.info('---------^_^---------')
Bot.logger.info(`喵喵插件${Version.version}初始化~`)
} else {
console.log(`喵喵插件${Version.version}初始化~`)

View File

@ -1,5 +1,4 @@
import { attrMap }
from '../resources/meta/reliquaries/reliquaries-mark-new.js'
import { attrMap } from '../resources/meta/reliquaries/artis-mark.js'
import lodash from 'lodash'
import { Data } from '../components/index.js'

View File

@ -6,7 +6,7 @@ import Base from './Base.js'
import { Artifact, Character } from './index.js'
import { Format } from '../components/index.js'
import ArtisMark from './profile-lib/ArtisMark.js'
import { attrMap, attrValue, usefulAttr } from '../resources/meta/reliquaries/reliquaries-mark-new.js'
import { attrMap, attrValue, usefulAttr } from '../resources/meta/reliquaries/artis-mark.js'
let charCfg = {}

View File

@ -1,6 +1,6 @@
import lodash from 'lodash'
import { Format } from '../../components/index.js'
import { attrNameMap, mainAttr, subAttr } from '../../resources/meta/reliquaries/reliquaries-mark-new.js'
import { attrNameMap, mainAttr, subAttr } from '../../resources/meta/reliquaries/artis-mark.js'
let ArtisMark = {
formatAttr (ds) {

View File

@ -1,144 +0,0 @@
import lodash from 'lodash'
import { Character } from '../index.js'
import { eleBaseDmg, eleMap, } from './calc-meta.js'
import Mastery from './Mastery.js'
let Calc = {
getDmgFn ({ ds, attr, profile, enemyLv, showDetail = false }) {
let { calc } = ds
let dmgFn = function (pctNum = 0, talent = false, ele = false, basicNum = 0, mode = 'talent') {
let { atk, dmg, phy, cdmg, cpct } = attr
// 攻击区
let atkNum = calc(atk)
// 倍率独立乘区
let multiNum = attr.multi / 100
// 增伤区
let dmgNum = (1 + dmg.base / 100 + dmg.plus / 100)
if (ele === 'phy') {
dmgNum = (1 + phy.base / 100 + phy.plus / 100)
}
// console.log({ base: Format.comma(dmg.base, 2), plus: Format.comma(dmg.plus, 2) })
let cpctNum = cpct.base / 100 + cpct.plus / 100
// 爆伤区
let cdmgNum = cdmg.base / 100 + cdmg.plus / 100
let enemyDef = attr.enemy.def / 100
let enemyIgnore = attr.enemy.ignore / 100
let plusNum = 0
if (talent && attr[talent]) {
pctNum = pctNum / 100
let ds = attr[talent]
pctNum += ds.pct / 100
dmgNum += ds.dmg / 100
cpctNum += ds.cpct / 100
cdmgNum += ds.cdmg / 100
enemyDef += ds.def / 100
enemyIgnore += ds.ignore / 100
multiNum += ds.multi / 100
plusNum += ds.plus
}
// 防御区
let lv = profile.lv || profile.level
let defNum = (lv + 100) / ((lv + 100) + (enemyLv + 100) * (1 - enemyDef) * (1 - enemyIgnore))
// 抗性区
let kx = attr.kx
if (talent === 'fy') {
kx = attr.fykx
}
kx = 10 - (kx || 0)
let kNum = 0.9
if (kx >= 0) {
kNum = (100 - kx) / 100
} else {
kNum = 1 - kx / 200
}
// 反应区
let eleNum = 1
let eleBase = 0
if (ele === 'ks' || ele === 'gd') {
eleBase = eleBaseDmg[lv] || 0
}
if (ele === 'phy') {
// do nothing
} else if (ele) {
eleNum = Mastery.getBasePct(ele, attr.element)
if (attr[ele]) {
eleNum = eleNum * (1 + attr[ele] / 100)
}
}
cpctNum = Math.max(0, Math.min(1, cpctNum))
if (cpctNum === 0) {
cdmgNum = 0
}
let ret = {}
if (mode === 'basic') {
ret = {
dmg: basicNum * dmgNum * (1 + cdmgNum) * defNum * kNum * eleNum,
avg: basicNum * dmgNum * (1 + cpctNum * cdmgNum) * defNum * kNum * eleNum
}
} else if (eleBase) {
ret = {
avg: eleBase * kNum * eleNum
}
} else {
// 计算最终伤害
ret = {
dmg: (atkNum * pctNum * (1 + multiNum) + plusNum) * dmgNum * (1 + cdmgNum) * defNum * kNum * eleNum,
avg: (atkNum * pctNum * (1 + multiNum) + plusNum) * dmgNum * (1 + cpctNum * cdmgNum) * defNum * kNum * eleNum
}
}
if (showDetail) {
console.log(attr, { atkNum, pctNum, multiNum, plusNum, dmgNum, cpctNum, cdmgNum, defNum, eleNum, kNum }, ret)
}
return ret
}
dmgFn.basic = function (basicNum = 0, talent = false, ele = false) {
return dmgFn(0, talent, ele, basicNum, 'basic')
}
dmgFn.heal = function (num) {
if (showDetail) {
console.log(num, calc(attr.heal), attr.heal.inc)
}
return {
avg: num * (1 + calc(attr.heal) / 100 + attr.heal.inc / 100)
}
}
dmgFn.shield = function (num) {
return {
avg: num * (calc(attr.shield) / 100) * (attr.shield.inc / 100)
}
}
dmgFn.ks = function () {
return dmgFn(0, 'fy', 'ks')
}
return dmgFn
}
}
export default Calc

View File

@ -3,7 +3,7 @@
* */
import { attrMap, eleMap } from './calc-meta.js'
import lodash from 'lodash'
import Mastery from './Mastery.js'
import DmgMastery from './DmgMastery.js'
import { Format } from '../../components/index.js'
let DmgAttr = {
@ -142,8 +142,7 @@ let DmgAttr = {
// let masteryNum = 2.78 * mastery / (mastery + 1400) * 100;
buff.data = buff.data || {}
lodash.forEach(buff.mastery.split(','), (key) => {
buff.data[key] = Mastery.getMultiple(key, mastery)
// buff.data[key] = masteryNum;
buff.data[key] = DmgMastery.getMultiple(key, mastery)
})
}

View File

@ -2,7 +2,7 @@
* 伤害计算 - 计算伤害
* */
import { eleBaseDmg } from './calc-meta.js'
import Mastery from './Mastery.js'
import DmgMastery from './DmgMastery.js'
let DmgCalc = {
calcRet (fnArgs = {}, data = {}) {
@ -88,7 +88,7 @@ let DmgCalc = {
if (ele === 'phy') {
// do nothing
} else if (ele) {
eleNum = Mastery.getBasePct(ele, attr.element)
eleNum = DmgMastery.getBasePct(ele, attr.element)
if (attr[ele]) {
eleNum = eleNum * (1 + attr[ele] / 100)

View File

@ -1,10 +1,6 @@
import { erType } from './calc-meta.js'
let Mastery = {
getType () {
},
let DmgMastery = {
getMultiple (type = 'zf', mastery = 0) {
let typeCfg = erType[type]
if (typeCfg.type === 'pct') {
@ -22,4 +18,4 @@ let Mastery = {
return 1
}
}
export default Mastery
export default DmgMastery

View File

@ -63,7 +63,6 @@ body {
color: #495366;
font-weight: bold;
border-radius: 0 16px 16px 0;
font-family: Number, "微软雅黑", sans-serif;
}
.cfg-status.status-off {
color: #a95151;
@ -73,4 +72,4 @@ body {
color: #cbc4be;
margin: 5px 0 5px 20px;
}
/*# sourceMappingURL=index.less.map */
/*# sourceMappingURL=index.css.map */

View File

@ -76,7 +76,6 @@ body {
color: #495366;
font-weight: bold;
border-radius: 0 16px 16px 0;
font-family: Number, "微软雅黑", sans-serif;
}
.cfg-status.status-off {

View File

@ -32,4 +32,4 @@
max-width: 100%;
max-height: 100%;
}
/*# sourceMappingURL=artis.css.map */
/*# sourceMappingURL=artis-list.css.map */

View File

@ -2,7 +2,7 @@
{{block 'css'}}
<link rel="stylesheet" type="text/css" href="{{_res_path}}/character/profile-detail.css"/>
<link rel="stylesheet" type="text/css" href="{{_res_path}}/character/artis.css"/>
<link rel="stylesheet" type="text/css" href="{{_res_path}}/character/artis-list.css"/>
{{/block}}
{{block 'main'}}

View File

@ -434,4 +434,4 @@ body.bottom_mode {
.bottom_mode .no-info {
bottom: 25px;
}
/*# sourceMappingURL=card.css.map */
/*# sourceMappingURL=character-card.css.map */

View File

@ -4,7 +4,7 @@
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
<link rel="shortcut icon" href="#"/>
<link rel="stylesheet" type="text/css" href="{{_res_path}}/common/common.css?v=1.0"/>
<link rel="stylesheet" type="text/css" href="{{_res_path}}/character/card.css?v=1.0"/>
<link rel="stylesheet" type="text/css" href="{{_res_path}}/character/character-card.css?v=1.0"/>
</head>
<body class="{{bg.mode}}_mode" {{sys.scale}}>

View File

@ -487,7 +487,6 @@ body.bottom_mode {
.copyright {
position: absolute;
//font-family: Number, YS;
margin-bottom: 0;
}

View File

@ -265,7 +265,7 @@
font-family: Number, "汉仪文黑-65W", YS, PingFangSC-Medium, "PingFang SC", sans-serif;
}
.font-NZBZ {
font-family: Number, "印品南征北战NZBZ体", NZBZ, PingFangSC-Medium, "PingFang SC", sans-serif;
font-family: Number, "印品南征北战NZBZ体", NZBZ, "汉仪文黑-65W", YS, PingFangSC-Medium, "PingFang SC", sans-serif;
}
.profile {
position: relative;
@ -302,7 +302,7 @@
}
.profile .char-name {
font-size: 50px;
font-family: Number, "印品南征北战NZBZ体", NZBZ, PingFangSC-Medium, "PingFang SC", sans-serif;
font-family: Number, "印品南征北战NZBZ体", NZBZ, "汉仪文黑-65W", YS, PingFangSC-Medium, "PingFang SC", sans-serif;
text-shadow: 0 0 3px #000, 2px 2px 4px rgba(0, 0, 0, 0.7);
text-align: right;
}

View File

@ -95,7 +95,6 @@
&.val {
text-align: right;
padding-right: 10px;
//font-family: Number;
}
}

View File

@ -11,7 +11,6 @@ body {
}
.label {
//font-family: Number, "微软雅黑", sans-serif;
}
.head-box {

View File

@ -24,3 +24,8 @@ export const buffs = [{
dmg: 15
}
}, 'zf']
export const artisSect = function ({ attr, calc }) {
let test = calc(attr.cpct) * 2 + calc(attr.cdmg) + calc(attr.dmg)
return test > 150 ? '暴力' : false
}

View File

@ -1,108 +0,0 @@
export const attrMark = {
暴击率: 2,
暴击伤害: 1,
元素精通: 0.25,
大攻击: 1,
大生命: 0.86,
大防御: 0.7,
小攻击: 0.12,
小生命: 0.014,
小防御: 0.18,
充能效率: 0.65
}
export const maxMark = {
暴击率: 46.6,
暴击伤害: 46.6,
元素精通: 35,
大攻击: 35,
大生命: 30.1,
大防御: 30.59,
小攻击: 14.04,
小生命: 25.1,
小防御: 25.02,
充能效率: 25.2
}
export const attrMap = {
atk: {
title: '攻击力',
attr: '小攻击,大攻击'
},
def: {
title: '防御力',
attr: '小防御,大防御'
},
hp: {
title: '生命值',
attr: '小生命,大生命'
},
cRate: {
title: '暴击率',
attr: '暴击率'
},
cDmg: {
title: '暴击伤害',
attr: '暴击伤害'
},
mastery: {
title: '元素精通',
attr: '元素精通'
},
recharge: {
title: '充能效率',
attr: '充能效率'
}
}
export const usefulAttr = {
神里绫人: 'hp,atk,cRate,cDmg,mastery',
八重神子: 'atk,cRate,cDmg,mastery,',
申鹤: 'atk,recharge',
云堇: 'def,recharge',
荒泷一斗: 'def,cRate,cDmg',
五郎: 'def,recharge',
班尼特: 'hp,atk,cRate,cDmg,recharge',
枫原万叶: 'mastery,cRate,cDmg,recharge',
雷电将军: 'atk,cRate,cDmg,recharge',
行秋: 'atk,cRate,cDmg,recharge',
钟离: 'hp,atk,cRate,cDmg,recharge',
神里绫华: 'atk,cRate,cDmg',
香菱: 'atk,cRate,cDmg,recharge,mastery',
胡桃: 'hp,cRate,cDmg,mastery',
甘雨: 'atk,cRate,cDmg,mastery',
温迪: 'mastery,cRate,cDmg,recharge',
珊瑚宫心海: 'hp,recharge',
莫娜: 'mastery,cRate,cDmg,recharge',
阿贝多: 'def,cRate,cDmg',
迪奥娜: 'hp,recharge',
优菈: 'atk,cRate,cDmg',
达达利亚: 'atk,cRate,cDmg,mastery',
: 'atk,cRate,cDmg',
宵宫: 'atk,cRate,cDmg,mastery',
九条裟罗: 'atk,cRate,cDmg,recharge',
: 'atk,cRate,cDmg,recharge',
菲谢尔: 'atk,cRate,cDmg',
罗莎莉亚: 'atk,cRate,cDmg',
可莉: 'atk,cRate,cDmg',
凝光: 'atk,cRate,cDmg',
北斗: 'atk,cRate,cDmg',
刻晴: 'atk,cRate,cDmg',
托马: 'hp,recharge',
迪卢克: 'atk,cRate,cDmg,mastery',
芭芭拉: 'hp,recharge',
诺艾尔: 'def,cRate,cDmg',
旅行者: 'atk,cRate,cDmg',
重云: 'atk,cRate,cDmg',
七七: 'atk,cRate,cDmg,recharge',
凯亚: 'atk,cRate,cDmg',
烟绯: 'atk,cRate,cDmg,mastery',
早柚: 'mastery,recharge',
安柏: 'atk,cRate,cDmg,mastery',
丽莎: 'atk,cRate,cDmg',
埃洛伊: 'atk,cRate,cDmg',
辛焱: 'atk,cRate,cDmg',
砂糖: 'mastery,recharge',
雷泽: 'atk,cRate,cDmg',
夜兰: 'hp,cRate,cDmg,recharge'
}

View File

@ -91,7 +91,6 @@
border-radius: 3px;
padding: 1px 5px;
background: rgb(0 0 0 / 50%);
//font-family: Number;
}
.card-list .card .name,

View File

@ -15,7 +15,6 @@ body {
background: url("./imgs/bg1.png") top left no-repeat #2a3860;
background-size: contain;
width: 600px;
//font-family: Number, YS2;
}
.container {

View File

@ -1,129 +1,135 @@
import fs from "fs";
import cheerio from "cheerio";
import lodash from "lodash";
import fetch from "node-fetch";
import { roleId, abbr } from "../../../config/genshin/roleId.js";
import fs from 'fs'
import cheerio from 'cheerio'
import lodash from 'lodash'
import fetch from 'node-fetch'
import { roleId, abbr } from '../../../config/genshin/roleId.js'
const _path = process.cwd();
let roleIdMap = {};
const _path = process.cwd()
let roleIdMap = {}
lodash.forEach(roleId, (names, id) => {
roleIdMap[names[0]] = id;
});
roleIdMap[names[0]] = id
})
function getBasic($, name) {
function getBasic ($, name) {
let ret = {}
if (name) {
ret.name = name;
ret.name = name
} else {
// 采集基础信息
ret.name = $("#scroll_card_item").next("table").find("tr:first td:eq(1)").text();
ret.name = $('#scroll_card_item').next('table').find('tr:first td:eq(1)').text()
}
ret.abbr = abbr[ret.name] || ret.name;
ret.id = roleIdMap[ret.name] || '';
ret.abbr = abbr[ret.name] || ret.name
ret.id = roleIdMap[ret.name] || ''
let basic = $(".data_cont_wrapper table:first");
let basic = $('.data_cont_wrapper table:first')
console.log('basic', basic)
let title = function (title) {
return basic.find(`td:contains('${title}')`).next("td").text();
return basic.find(`td:contains('${title}')`).next('td').text()
}
ret.title = title("Title");
ret.star = basic.find(`td:contains('Rarity')`).next("td").find(".sea_char_stars_wrap").length;
let elem = basic.find(`td:contains('Element')`).next("td").find("img").attr("data-src");
ret.title = title('Title')
ret.star = basic.find('td:contains(\'Rarity\')').next('td').find('.sea_char_stars_wrap').length
let elem = basic.find('td:contains(\'Element\')').next('td').find('img').attr('data-src')
let elemRet = /\/([^\/]*)_35/.exec(elem);
let elemRet = /\/([^/]*)_35/.exec(elem)
if (elemRet) {
ret.elem = elemRet[1];
ret.elem = elemRet[1]
}
ret.allegiance = title("Allegiance");
ret.weapon = title("Weapon Type");
ret.britydah = title("Birthday");
ret.astro = title("Astrolabe Name");
ret.cncv = title("Chinese Seiyuu");
ret.jpcv = title("Japanese Seiyuu");
ret.desc = title("In-game Description");
return ret;
ret.allegiance = title('Allegiance')
ret.weapon = title('Weapon Type')
ret.britydah = title('Birthday')
ret.astro = title('Astrolabe Name')
ret.cncv = title('Chinese Seiyuu')
ret.jpcv = title('Japanese Seiyuu')
ret.desc = title('In-game Description')
return ret
}
function getStat($) {
function getStat ($) {
// 采集属性信息
let stat = $("#beta_scroll_stat").next(".skilldmgwrapper").find("table");
let attrs = [], idx = 4;
stat.find("tr:first td:gt(0)").each(function (i) {
let title = $(this).text();
if (title === "Ascension") {
idx = i + 1;
console.log(idx);
return false;
let stat = $('#beta_scroll_stat').next('.skilldmgwrapper').find('table')
let attrs = []
let idx = 4
stat.find('tr:first td:gt(0)').each(function (i) {
let title = $(this).text()
if (title === 'Ascension') {
idx = i + 1
console.log(idx)
return false
}
attrs.push($(this).text())
})
let lvs = [], lvStat = {};
stat.find("tr:gt(0)").each(function (i) {
let tr = $(this), lvl = tr.find("td:first").text();
lvs.push(lvl);
let data = [];
let lvs = []
let lvStat = {}
stat.find('tr:gt(0)').each(function (i) {
let tr = $(this)
let lvl = tr.find('td:first').text()
lvs.push(lvl)
let data = []
tr.find(`td:lt(${idx})`).each(function (i) {
if (i > 0) {
data.push($(this).text())
}
})
lvStat[lvl] = data;
});
lvStat[lvl] = data
})
return {
lvs,
stat: attrs,
detail: lvStat
};
}
}
function getTalents($, eq, onlyLv1 = false) {
let root = $("#beta_scroll_attack_talent");
let info = root.nextAll(`.item_main_table:eq(${eq})`);
function getTalents ($, eq, onlyLv1 = false) {
let root = $('#beta_scroll_attack_talent')
let info = root.nextAll(`.item_main_table:eq(${eq})`)
let name = info.find("tr:first td:eq(1)").text();
let icon = info.find("tr:first td:first img").attr("data-src");
let name = info.find('tr:first td:eq(1)').text()
let icon = info.find('tr:first td:first img').attr('data-src')
// 说明
let desc = info.find("tr:eq(1) td div.skill_desc_layout").html();
desc = desc.replace(/<color=[^>]*>/g, "");
desc = desc.replace(/<\/color=[^>]*>/g, "");
desc = desc.replace(/<span class=[^>]*>/g, "<strong>");
desc = desc.replace(/<\/span>/g, "</strong>");
desc = desc.split("<br>");
let desc = info.find('tr:eq(1) td div.skill_desc_layout').html()
desc = desc.replace(/<color=[^>]*>/g, '')
desc = desc.replace(/<\/color=[^>]*>/g, '')
desc = desc.replace(/<span class=[^>]*>/g, '<strong>')
desc = desc.replace(/<\/span>/g, '</strong>')
desc = desc.split('<br>')
lodash.forEach(desc, (txt, i) => {
desc[i] = lodash.trim(txt);
desc[i] = lodash.trim(txt)
})
// detail
let detail = root.nextAll(`.skilldmgwrapper:eq(${eq})`).find("table");
let lvs = [], details = [];
detail.find("tr:first td").each(function (i) {
let detail = root.nextAll(`.skilldmgwrapper:eq(${eq})`).find('table')
let lvs = []
let details = []
detail.find('tr:first td').each(function (i) {
if (onlyLv1 && i > 1) {
return false;
return false
}
if (i > 0) {
lvs.push($(this).text())
}
});
detail.find("tr:gt(0)").each(function () {
let name = $(this).find("td:eq(0)").text();
let values = [], isSame = true;
$(this).find("td:gt(0)").each(function (i) {
})
detail.find('tr:gt(0)').each(function () {
let name = $(this).find('td:eq(0)').text()
let values = []
let isSame = true
$(this).find('td:gt(0)').each(function (i) {
if (onlyLv1 && i > 0) {
return false;
return false
}
let val = lodash.trim($(this).text());
values.push(val);
let val = lodash.trim($(this).text())
values.push(val)
if (i > 0 && values[0] !== val) {
isSame = false;
isSame = false
}
});
})
details.push({
name, isSame, values
})
});
})
return {
name,
@ -132,52 +138,50 @@ function getTalents($, eq, onlyLv1 = false) {
tables: details,
lvs
}
}
let getPassive = function ($, name) {
let table = $("#beta_scroll_passive_talent").next("table")
let ret = [];
let table = $('#beta_scroll_passive_talent').next('table')
let ret = []
table.find("tr").each(function (idx) {
table.find('tr').each(function (idx) {
if (idx % 2 === 0) {
let ds = {};
ds.icon = $(this).find("td:first img").attr("data-src");
ds.name = $(this).find("td:eq(1)").text();
ret[idx / 2] = ds;
let ds = {}
ds.icon = $(this).find('td:first img').attr('data-src')
ds.name = $(this).find('td:eq(1)').text()
ret[idx / 2] = ds
} else {
ret[(idx - 1) / 2].desc = $(this).find("td").text();
ret[(idx - 1) / 2].desc = $(this).find('td').text()
}
})
if (name === "莫娜" || name === "神里绫华") {
if (name === '莫娜' || name === '神里绫华') {
ret.push(getTalents($, 2, true))
}
return ret;
return ret
}
let getCons = function ($) {
let table = $("#beta_scroll_constellation").next("table")
let ret = {};
table.find("tr").each(function (idx) {
let table = $('#beta_scroll_constellation').next('table')
let ret = {}
table.find('tr').each(function (idx) {
if (idx % 2 === 0) {
let ds = {};
ds.icon = $(this).find("td:first img").attr("data-src");
ds.name = $(this).find("td:eq(1)").text();
ret[idx / 2 + 1] = ds;
let ds = {}
ds.icon = $(this).find('td:first img').attr('data-src')
ds.name = $(this).find('td:eq(1)').text()
ret[idx / 2 + 1] = ds
} else {
ret[(idx + 1) / 2].desc = $(this).find("td").text();
ret[(idx + 1) / 2].desc = $(this).find('td').text()
}
})
return ret;
return ret
}
let getImgs = function ($) {
let cont = $("#scroll_gallery").next(".homepage_index_cont");
let cont = $('#scroll_gallery').next('.homepage_index_cont')
let img = function (idx, _cont) {
return (_cont || cont).find(`.gallery_content_cont:eq(${idx}) a`).attr("href");
return (_cont || cont).find(`.gallery_content_cont:eq(${idx}) a`).attr('href')
}
let card = $("#scroll_name_card").nextAll(".homepage_index_cont:first");
let card = $('#scroll_name_card').nextAll('.homepage_index_cont:first')
return {
face: img(0),
side: img(1),
@ -185,98 +189,56 @@ let getImgs = function ($) {
gacha_splash: img(3),
profile: img(1, card),
party: img(2, card),
char: $("#live_data table.item_main_table:first td:first img").attr("data-src")
char: $('#live_data table.item_main_table:first td:first img').attr('data-src')
}
}
let getCharData = async function (url, key, name = '') {
let getCharData = async function (url, name = '') {
url = 'https://genshin.honeyhunterworld.com/' + url
console.log('req' + url)
url = "https://genshin.honeyhunterworld.com/" + url;
console.log('req' + key, url)
let req = await fetch(url)
let txt = await req.text()
let req = await fetch(url);
let txt = await req.text();
const $ = cheerio.load(txt)
let ret = getBasic($, name)
name = ret.name
const $ = cheerio.load(txt);
let ret = getBasic($, name);
name = ret.name;
ret.lvStat = getStat($);
ret.lvStat = getStat($)
ret.talent = {
a: getTalents($, 0),
e: getTalents($, 1),
q: getTalents($, name === "莫娜" || name === "神里绫华" ? 3 : 2)
q: getTalents($, name === '莫娜' || name === '神里绫华' ? 3 : 2)
}
ret.passive = getPassive($, name);
ret.cons = getCons($);
ret.imgs = getImgs($);
return ret;
ret.passive = getPassive($, name)
ret.cons = getCons($)
ret.imgs = getImgs($)
return ret
}
async function saveCharData(url, key, name) {
async function saveCharData (url, name) {
let data = await getCharData(url, name)
let data = await getCharData(url, key, name);
name = name || data.name;
name = name || data.name
if (!name) {
console.log("角色名不存在" + url);
return;
console.log('角色名不存在' + url)
return
}
let charPath = `${_path}/plugins/miao-plugin/resources/meta/character/${data.name}/`
if (!fs.existsSync(charPath)) {
fs.mkdirSync(charPath);
fs.mkdirSync(charPath)
}
fs.writeFileSync(`${charPath}data.json`, JSON.stringify(data, "", "\t"));
console.log(data.name + "下载完成");
fs.writeFileSync(`${charPath}data.json`, JSON.stringify(data, '', '\t'))
console.log(data.name + '下载完成')
}
async function down() {
//const url = "https://genshin.honeyhunterworld.com/db/char/characters/?lang=CHS";
const url = "https://genshin.honeyhunterworld.com/db/char/unreleased-and-upcoming-characters/?lang=CHS";
let req = await fetch(url);
let txt = await req.text();
let $ = cheerio.load(txt);
let char = $(".char_sea_cont");
char.each(async function () {
let url = $(this).find("a:first").attr("href");
let keyRet = /\/char\/(\w*)\//.exec(url);
if (keyRet && keyRet[1]) {
let key = keyRet[1],
tRet = /traveler_(girl|boy)_(\w*)/.exec(key),
name;
if (tRet) {
if (tRet[1] === "girl") {
name = { anemo: "风", geo: "岩", electro: "雷" }[tRet[2]] + "主";
} else {
return
}
}
let nameMap = { dori: "多莉", tighnari: "提纳里", collei: "柯莱" }
if (nameMap[key]) {
name = nameMap[key];
}
await saveCharData(url, key, name);
}
});
async function down () {
await saveCharData('/collei_067/?lang=CHS', '柯莱')
}
//await saveCharData("https://genshin.honeyhunterworld.com/db/char/ayaka/?lang=CHS", "ayaka");
await down();
// await saveCharData("https://genshin.honeyhunterworld.com/db/char/ayaka/?lang=CHS", "ayaka");
await down()