|
|
import util from "node:util" |
|
|
import fs from "node:fs/promises" |
|
|
import lodash from "lodash" |
|
|
import cfg from "../config/config.js" |
|
|
import plugin from "./plugin.js" |
|
|
import schedule from "node-schedule" |
|
|
import { segment } from "oicq" |
|
|
import chokidar from "chokidar" |
|
|
import moment from "moment" |
|
|
import path from "node:path" |
|
|
import Runtime from "./runtime.js" |
|
|
import Handler from "./handler.js" |
|
|
|
|
|
|
|
|
global.plugin = plugin |
|
|
global.segment = segment |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PluginsLoader { |
|
|
priority = [] |
|
|
handler = {} |
|
|
task = [] |
|
|
dir = "plugins" |
|
|
|
|
|
|
|
|
groupCD = {} |
|
|
singleCD = {} |
|
|
|
|
|
|
|
|
watcher = {} |
|
|
eventMap = { |
|
|
message: ["post_type", "message_type", "sub_type"], |
|
|
notice: ["post_type", "notice_type", "sub_type"], |
|
|
request: ["post_type", "request_type", "sub_type"], |
|
|
} |
|
|
|
|
|
msgThrottle = {} |
|
|
|
|
|
|
|
|
srReg = /^#?(\*|星铁|星轨|穹轨|星穹|崩铁|星穹铁道|崩坏星穹铁道|铁道)+/ |
|
|
|
|
|
zzzReg = /^#?(%|%|绝区零|绝区)+/ |
|
|
|
|
|
async getPlugins() { |
|
|
const files = await fs.readdir(this.dir, { withFileTypes: true }) |
|
|
const ret = [] |
|
|
for (const val of files) { |
|
|
if (val.isFile()) continue |
|
|
const tmp = { |
|
|
name: val.name, |
|
|
path: `../../${this.dir}/${val.name}`, |
|
|
} |
|
|
|
|
|
if (await Bot.fsStat(`${this.dir}/${val.name}/index.js`)) { |
|
|
tmp.path = `${tmp.path}/index.js` |
|
|
ret.push(tmp) |
|
|
continue |
|
|
} |
|
|
|
|
|
const apps = await fs.readdir(`${this.dir}/${val.name}`, { withFileTypes: true }) |
|
|
for (const app of apps) { |
|
|
if (!app.isFile()) continue |
|
|
if (!app.name.endsWith(".js")) continue |
|
|
ret.push({ |
|
|
name: `${tmp.name}/${app.name}`, |
|
|
path: `${tmp.path}/${app.name}`, |
|
|
}) |
|
|
|
|
|
this.watch(val.name, app.name) |
|
|
} |
|
|
} |
|
|
return ret |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async load(isRefresh = false) { |
|
|
if (isRefresh) this.priority = [] |
|
|
if (this.priority.length) return |
|
|
|
|
|
Bot.makeLog("info", "-----------", "Plugin") |
|
|
Bot.makeLog("info", "加载插件中...", "Plugin") |
|
|
|
|
|
const files = await this.getPlugins() |
|
|
this.pluginCount = 0 |
|
|
const packageErr = [] |
|
|
|
|
|
await Promise.allSettled(files.map(file => |
|
|
this.importPlugin(file, packageErr) |
|
|
)) |
|
|
|
|
|
this.packageTips(packageErr) |
|
|
this.createTask() |
|
|
|
|
|
Bot.makeLog("info", `加载定时任务[${this.task.length}个]`, "Plugin") |
|
|
Bot.makeLog("info", `加载插件[${this.pluginCount}个]`, "Plugin") |
|
|
|
|
|
|
|
|
this.priority = lodash.orderBy(this.priority, ["priority"], ["asc"]) |
|
|
} |
|
|
|
|
|
async importPlugin(file, packageErr) { |
|
|
try { |
|
|
let app = await import(file.path) |
|
|
if (app.apps) app = { ...app.apps } |
|
|
const pluginArray = [] |
|
|
lodash.forEach(app, p => |
|
|
pluginArray.push(this.loadPlugin(file, p)) |
|
|
) |
|
|
for (const i of await Promise.allSettled(pluginArray)) |
|
|
if (i?.status && i.status !== "fulfilled") { |
|
|
Bot.makeLog("error", [`插件加载错误 ${logger.red(file.name)}`, i], "Plugin") |
|
|
} |
|
|
} catch (error) { |
|
|
if (packageErr && error.stack.includes("Cannot find package")) { |
|
|
packageErr.push({ error, file }) |
|
|
} else { |
|
|
Bot.makeLog("error", [`插件加载错误 ${logger.red(file.name)}`, error], "Plugin") |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
async loadPlugin(file, p) { |
|
|
if (!p?.prototype) return |
|
|
this.pluginCount++ |
|
|
const plugin = new p |
|
|
Bot.makeLog("debug", `加载插件 [${file.name}][${plugin.name}]`, "Plugin") |
|
|
|
|
|
if (plugin.init && await plugin.init() === "return") return |
|
|
|
|
|
this.collectTask(plugin.task, plugin.name) |
|
|
|
|
|
if (plugin.rule) for (const i of plugin.rule) |
|
|
if (!(i.reg instanceof RegExp)) |
|
|
i.reg = new RegExp(i.reg) |
|
|
|
|
|
this.priority.push({ |
|
|
plugin, |
|
|
class: p, |
|
|
key: file.name, |
|
|
name: plugin.name, |
|
|
priority: plugin.priority, |
|
|
}) |
|
|
if (plugin.handler) { |
|
|
lodash.forEach(plugin.handler, ({ fn, key, priority }) => { |
|
|
Handler.add({ |
|
|
ns: plugin.namespace || file.name, |
|
|
key, |
|
|
self: plugin, |
|
|
property: priority || plugin.priority || 500, |
|
|
fn: plugin[fn], |
|
|
}) |
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
packageTips(packageErr) { |
|
|
if (!packageErr.length) return |
|
|
Bot.makeLog("error", "--------- 插件加载错误 ---------", "Plugin") |
|
|
for (const i of packageErr) { |
|
|
const pack = i.error.stack.match(/'(.+?)'/g)[0].replace(/'/g, "") |
|
|
Bot.makeLog("error", `${logger.cyan(i.file.name)} 缺少依赖 ${logger.red(pack)}`, "Plugin") |
|
|
} |
|
|
Bot.makeLog("error", `安装插件后请 ${logger.red("pnpm i")} 安装依赖`, "Plugin") |
|
|
Bot.makeLog("error", `仍报错${logger.red("进入插件目录")} pnpm add 依赖`, "Plugin") |
|
|
Bot.makeLog("error", "--------------------------------", "Plugin") |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async deal(e) { |
|
|
this.count(e, "receive", e.message) |
|
|
|
|
|
if (!this.checkBlack(e)) return |
|
|
|
|
|
if (!this.checkLimit(e)) return |
|
|
|
|
|
this.dealEvent(e) |
|
|
|
|
|
this.reply(e) |
|
|
|
|
|
await Runtime.init(e) |
|
|
|
|
|
const priority = [] |
|
|
for (const i of this.priority) { |
|
|
|
|
|
if (this.checkDisable(Object.assign(i.plugin, { e })) && this.filtEvent(e, i.plugin)) |
|
|
priority.push(i) |
|
|
} |
|
|
|
|
|
for (const i of priority) { |
|
|
|
|
|
if (!i.plugin.getContext) continue |
|
|
const context = { |
|
|
...i.plugin.getContext(), |
|
|
...i.plugin.getContext(false, true), |
|
|
} |
|
|
if (!lodash.isEmpty(context)) { |
|
|
let ret |
|
|
for (const fnc in context) |
|
|
ret ||= await Object.assign(new i.class(e), { e })[fnc](context[fnc]) |
|
|
if (ret === "continue") continue |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (!this.onlyReplyAt(e)) return |
|
|
|
|
|
|
|
|
|
|
|
Object.defineProperty(e, "isSr", { |
|
|
get: () => e.game === "sr", |
|
|
set: (v) => e.game = v ? "sr" : "gs" |
|
|
}) |
|
|
Object.defineProperty(e, "isGs", { |
|
|
get: () => e.game === "gs", |
|
|
set: (v) => e.game = v ? "gs" : "sr" |
|
|
}) |
|
|
if (this.srReg.test(e.msg)) { |
|
|
e.game = "sr" |
|
|
e.msg = e.msg.replace(this.srReg, "#星铁") |
|
|
} else if (this.zzzReg.test(e.msg)) { |
|
|
e.game = "zzz" |
|
|
e.msg = e.msg.replace(this.zzzReg, "#绝区零") |
|
|
} |
|
|
|
|
|
|
|
|
for (const i of priority) |
|
|
if (i.plugin.accept) { |
|
|
const res = await Object.assign(new i.class(e), { e }).accept(e) |
|
|
if (res === "return") return |
|
|
if (res) break |
|
|
} |
|
|
|
|
|
a: for (const i of priority) { |
|
|
if (i.plugin.rule) for (const v of i.plugin.rule) { |
|
|
|
|
|
if (v.event && !this.filtEvent(e, v)) continue |
|
|
|
|
|
|
|
|
if (!v.reg.test(e.msg)) continue |
|
|
const plugin = Object.assign(new i.class(e), { e }) |
|
|
e.logFnc = `${logger.blue(`[${plugin.name}(${v.fnc})]`)}` |
|
|
|
|
|
Bot.makeLog(v.log === false ? "debug" : "info", `${e.logText}${e.logFnc}${logger.yellow("[开始处理]")}`, false) |
|
|
|
|
|
|
|
|
if (!this.filtPermission(e, v)) break a |
|
|
|
|
|
try { |
|
|
const start_time = Date.now() |
|
|
const res = plugin[v.fnc] && (await plugin[v.fnc](e)) |
|
|
if (res !== false) { |
|
|
|
|
|
this.setLimit(e) |
|
|
Bot.makeLog(v.log === false ? "debug" : "mark", `${e.logText}${e.logFnc}${logger.green(`[完成${Bot.getTimeDiff(start_time)}]`)}`, false) |
|
|
break a |
|
|
} |
|
|
} catch (err) { |
|
|
Bot.makeLog("error", [`${e.logText}${e.logFnc}`, err], false) |
|
|
break a |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
filtEvent(e, v) { |
|
|
if (!v.event) return false |
|
|
const event = v.event.split(".") |
|
|
const eventMap = this.eventMap[e.post_type] || [] |
|
|
const newEvent = [] |
|
|
for (const i in event) { |
|
|
if (event[i] === "*") |
|
|
newEvent.push(event[i]) |
|
|
else |
|
|
newEvent.push(e[eventMap[i]]) |
|
|
} |
|
|
return v.event === newEvent.join(".") |
|
|
} |
|
|
|
|
|
|
|
|
filtPermission(e, v) { |
|
|
if (v.permission === "all" || !v.permission) return true |
|
|
|
|
|
if (v.permission === "master") { |
|
|
if (e.isMaster) { |
|
|
return true |
|
|
} else { |
|
|
e.reply("暂无权限,只有主人才能操作") |
|
|
return false |
|
|
} |
|
|
} |
|
|
|
|
|
if (e.isGroup) { |
|
|
if (v.permission === "owner") { |
|
|
if (!e.member.is_owner) { |
|
|
e.reply("暂无权限,只有群主才能操作") |
|
|
return false |
|
|
} |
|
|
} |
|
|
if (v.permission === "admin") { |
|
|
if (!e.member.is_admin) { |
|
|
e.reply("暂无权限,只有管理员才能操作") |
|
|
return false |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return true |
|
|
} |
|
|
|
|
|
dealText(text = "") { |
|
|
if (cfg.bot["/→#"]) |
|
|
text = text.replace(/^\s*\/\s*/, "#") |
|
|
return text |
|
|
.replace(/^\s*[#井]\s*/, "#") |
|
|
.replace(/^\s*[*※]\s*/, "*") |
|
|
.trim() |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dealEvent(e) { |
|
|
if (e.message) for (const i of e.message) { |
|
|
switch (i.type) { |
|
|
case "text": |
|
|
e.msg = (e.msg || "") + this.dealText(i.text) |
|
|
break |
|
|
case "image": |
|
|
if (Array.isArray(e.img)) |
|
|
e.img.push(i.url) |
|
|
else |
|
|
e.img = [i.url] |
|
|
break |
|
|
case "at": |
|
|
if (i.qq == e.self_id) |
|
|
e.atBot = true |
|
|
else |
|
|
e.at = i.qq |
|
|
break |
|
|
case "reply": |
|
|
e.reply_id = i.id |
|
|
if (e.group?.getMsg) |
|
|
e.getReply = () => e.group.getMsg(e.reply_id) |
|
|
else if (e.friend?.getMsg) |
|
|
e.getReply = () => e.friend.getMsg(e.reply_id) |
|
|
break |
|
|
case "file": |
|
|
e.file = i |
|
|
break |
|
|
case "xml": |
|
|
case "json": |
|
|
e.msg = (e.msg || "") + (typeof i.data === "string" ? i.data : JSON.stringify(i.data)) |
|
|
break |
|
|
} |
|
|
} |
|
|
|
|
|
e.logText = "" |
|
|
|
|
|
if (e.message_type === "private" || e.notice_type === "friend") { |
|
|
e.isPrivate = true |
|
|
|
|
|
if (e.sender) { |
|
|
e.sender.card = e.sender.nickname |
|
|
} else { |
|
|
e.sender = { |
|
|
user_id: e.user_id, |
|
|
nickname: e.friend?.nickname, |
|
|
card: e.friend?.nickname, |
|
|
} |
|
|
} |
|
|
|
|
|
e.logText = `[${e.sender?.nickname ? `${e.sender.nickname}(${e.user_id})` : e.user_id}]` |
|
|
} else if (e.message_type === "group" || e.notice_type === "group") { |
|
|
e.isGroup = true |
|
|
|
|
|
if (e.sender) { |
|
|
if (!e.sender.card) |
|
|
e.sender.card = e.sender.nickname |
|
|
} else { |
|
|
e.sender = { |
|
|
user_id: e.user_id, |
|
|
nickname: e.member?.nickname || e.friend?.nickname, |
|
|
card: e.member?.card || e.member?.nickname || e.friend?.nickname, |
|
|
} |
|
|
} |
|
|
|
|
|
if (!e.group_name && e.group?.name) |
|
|
e.group_name = e.group.name |
|
|
|
|
|
e.logText = `[${e.group_name ? `${e.group_name}(${e.group_id})` : e.group_id}, ${e.sender?.card ? `${e.sender.card}(${e.user_id})` : e.user_id}]` |
|
|
} |
|
|
|
|
|
e.logText = `${logger.cyan(e.logText)}${logger.red(`[${lodash.truncate(e.msg || e.raw_message || Bot.String(e), { length: 100 })}]`)}` |
|
|
|
|
|
if (e.user_id && cfg.master[e.self_id]?.includes(String(e.user_id))) |
|
|
e.isMaster = true |
|
|
|
|
|
|
|
|
if (e.msg && e.isGroup) { |
|
|
const alias = cfg.getGroup(e.self_id, e.group_id).botAlias |
|
|
for (const i of Array.isArray(alias) ? alias : [alias]) |
|
|
if (e.msg.startsWith(i)) { |
|
|
e.msg = lodash.trimStart(e.msg, i).trim() |
|
|
e.hasAlias = true |
|
|
break |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
reply(e) { |
|
|
const reply = e.reply ? e.reply.bind(e) : msg => { |
|
|
if (e.isGroup) { |
|
|
if (e.group?.sendMsg) |
|
|
return e.group.sendMsg(msg) |
|
|
else |
|
|
return e.bot.pickGroup(e.group_id).sendMsg(msg) |
|
|
} else { |
|
|
if (e.friend?.sendMsg) |
|
|
return e.friend.sendMsg(msg) |
|
|
else |
|
|
return e.bot.pickFriend(e.user_id).sendMsg(msg) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
e.reply = async (msg = "", quote = false, data = {}) => { |
|
|
if (!msg) return false |
|
|
|
|
|
let { recallMsg = 0, at = "" } = data |
|
|
|
|
|
if (at && e.isGroup) { |
|
|
if (at === true) |
|
|
at = e.user_id |
|
|
if (Array.isArray(msg)) |
|
|
msg.unshift(segment.at(at), "\n") |
|
|
else |
|
|
msg = [segment.at(at), "\n", msg] |
|
|
} |
|
|
|
|
|
if (quote && e.message_id) { |
|
|
if (Array.isArray(msg)) |
|
|
msg.unshift(segment.reply(e.message_id)) |
|
|
else |
|
|
msg = [segment.reply(e.message_id), msg] |
|
|
} |
|
|
|
|
|
let res |
|
|
try { |
|
|
res = await reply(msg) |
|
|
} catch (err) { |
|
|
Bot.makeLog("error", ["发送消息错误", msg, err], e.self_id) |
|
|
res = { error: [err] } |
|
|
} |
|
|
|
|
|
if (recallMsg > 0 && res?.message_id) { |
|
|
if (e.group?.recallMsg) |
|
|
setTimeout(() => { |
|
|
e.group.recallMsg(res.message_id) |
|
|
if (e.message_id) |
|
|
e.group.recallMsg(e.message_id) |
|
|
}, recallMsg * 1000) |
|
|
else if (e.friend?.recallMsg) |
|
|
setTimeout(() => { |
|
|
e.friend.recallMsg(res.message_id) |
|
|
if (e.message_id) |
|
|
e.friend.recallMsg(e.message_id) |
|
|
}, recallMsg * 1000) |
|
|
} |
|
|
|
|
|
this.count(e, "send", msg) |
|
|
return res |
|
|
} |
|
|
} |
|
|
|
|
|
async count(e, type, msg) { |
|
|
if (cfg.bot.msg_type_count) |
|
|
for (const i of Array.isArray(msg) ? msg : [msg]) |
|
|
await this.saveCount(e, `${type}:${i?.type || "text"}`) |
|
|
await this.saveCount(e, `${type}:msg`) |
|
|
} |
|
|
|
|
|
async saveCount(e, type) { |
|
|
const key = [] |
|
|
|
|
|
const day = moment().format("YYYY:MM:DD") |
|
|
const month = moment().format("YYYY:MM") |
|
|
const year = moment().format("YYYY") |
|
|
for (const i of [day, month, year, "total"]) { |
|
|
key.push(`total:${i}`) |
|
|
if (e.self_id) key.push(`bot:${e.self_id}:${i}`) |
|
|
if (e.user_id) key.push(`user:${e.user_id}:${i}`) |
|
|
if (e.group_id) key.push(`group:${e.group_id}:${i}`) |
|
|
} |
|
|
|
|
|
for (const i of key) |
|
|
await redis.incr(`Yz:count:${type}:${i}`) |
|
|
} |
|
|
|
|
|
|
|
|
collectTask(task, name) { |
|
|
for (const i of Array.isArray(task) ? task : [task]) |
|
|
if (i.cron && i.fnc) { |
|
|
i.name ??= name |
|
|
this.task.push(i) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
createTask() { |
|
|
const created = [] |
|
|
for (const i of this.task) { |
|
|
if (i.job?.cancel) i.job.cancel() |
|
|
const name = `${logger.blue(`[${i.name}(${i.cron})]`)}` |
|
|
if (created.includes(name)) { |
|
|
Bot.makeLog("warn", `重复定时任务 ${name} 已跳过`, "Task") |
|
|
continue |
|
|
} |
|
|
created.push(name) |
|
|
Bot.makeLog("debug", `加载定时任务 ${name}`, "Task") |
|
|
i.job = schedule.scheduleJob(i.cron, async () => { try { |
|
|
const start_time = Date.now() |
|
|
Bot.makeLog(i.log === false ? "debug" : "mark", `${name}${logger.yellow("[开始处理]")}`, false) |
|
|
await i.fnc() |
|
|
Bot.makeLog(i.log === false ? "debug" : "mark", `${name}${logger.green(`[完成${Bot.getTimeDiff(start_time)}]`)}`, false) |
|
|
} catch (err) { |
|
|
Bot.makeLog("error", [name, err], false) |
|
|
}}) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
checkLimit(e) { |
|
|
|
|
|
if (e.isGroup && e.group?.mute_left > 0) return false |
|
|
if (!e.message || e.isPrivate) return true |
|
|
|
|
|
const config = cfg.getGroup(e.self_id, e.group_id) |
|
|
|
|
|
if (config.groupCD && this.groupCD[e.group_id]) |
|
|
return false |
|
|
|
|
|
if (config.singleCD && this.singleCD[`${e.group_id}.${e.user_id}`]) |
|
|
return false |
|
|
|
|
|
const msgId = `${e.self_id}:${e.user_id}:${e.raw_message}` |
|
|
if (this.msgThrottle[msgId]) return false |
|
|
|
|
|
this.msgThrottle[msgId] = true |
|
|
setTimeout(() => delete this.msgThrottle[msgId], 1000) |
|
|
|
|
|
return true |
|
|
} |
|
|
|
|
|
|
|
|
setLimit(e) { |
|
|
if (!e.message || e.isPrivate) return |
|
|
let config = cfg.getGroup(e.self_id, e.group_id) |
|
|
|
|
|
if (config.groupCD) { |
|
|
this.groupCD[e.group_id] = true |
|
|
setTimeout(() => delete this.groupCD[e.group_id], config.groupCD) |
|
|
} |
|
|
if (config.singleCD) { |
|
|
const key = `${e.group_id}.${e.user_id}` |
|
|
this.singleCD[key] = true |
|
|
setTimeout(() => delete this.singleCD[key], config.singleCD) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
onlyReplyAt(e) { |
|
|
if (!e.message || e.isPrivate) return true |
|
|
|
|
|
let groupCfg = cfg.getGroup(e.self_id, e.group_id) |
|
|
|
|
|
|
|
|
if (groupCfg.onlyReplyAt === 0 || !groupCfg.botAlias) return true |
|
|
|
|
|
|
|
|
if (groupCfg.onlyReplyAt === 2 && e.isMaster) return true |
|
|
|
|
|
|
|
|
if (e.atBot) return true |
|
|
|
|
|
|
|
|
if (e.hasAlias) return true |
|
|
|
|
|
return false |
|
|
} |
|
|
|
|
|
|
|
|
checkBlack(e) { |
|
|
const other = cfg.getOther() |
|
|
|
|
|
|
|
|
if (other.blackUser?.length && other.blackUser.includes(Number(e.user_id) || String(e.user_id))) |
|
|
return false |
|
|
|
|
|
if (other.whiteUser?.length && !other.whiteUser.includes(Number(e.user_id) || String(e.user_id))) |
|
|
return false |
|
|
|
|
|
if (e.group_id) { |
|
|
|
|
|
if (other.blackGroup?.length && other.blackGroup.includes(Number(e.group_id) || String(e.group_id))) |
|
|
return false |
|
|
|
|
|
if (other.whiteGroup?.length && !other.whiteGroup.includes(Number(e.group_id) || String(e.group_id))) |
|
|
return false |
|
|
} |
|
|
|
|
|
return true |
|
|
} |
|
|
|
|
|
|
|
|
checkDisable(p) { |
|
|
const groupCfg = cfg.getGroup(p.e.self_id, p.e.group_id) |
|
|
if (groupCfg.disable?.length && groupCfg.disable.includes(p.name)) |
|
|
return false |
|
|
if (groupCfg.enable?.length && !groupCfg.enable.includes(p.name)) |
|
|
return false |
|
|
return true |
|
|
} |
|
|
|
|
|
async changePlugin(key) { |
|
|
try { |
|
|
let app = await import(`../../${this.dir}/${key}?${moment().format("x")}`) |
|
|
if (app.apps) app = { ...app.apps } |
|
|
lodash.forEach(app, p => { |
|
|
const plugin = new p |
|
|
if (plugin.rule) for (const i of plugin.rule) |
|
|
if (!(i.reg instanceof RegExp)) |
|
|
i.reg = new RegExp(i.reg) |
|
|
for (const i of this.priority) |
|
|
if (i.key === key && i.name === plugin.name) |
|
|
Object.assign(i, { |
|
|
plugin, |
|
|
class: p, |
|
|
priority: plugin.priority, |
|
|
}) |
|
|
}) |
|
|
this.priority = lodash.orderBy(this.priority, ["priority"], ["asc"]) |
|
|
} catch (err) { |
|
|
Bot.makeLog("error", [`插件加载错误 ${logger.red(key)}`, err], "Plugin") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
watch(dirName, appName) { |
|
|
this.watchDir(dirName) |
|
|
if (this.watcher[`${dirName}.${appName}`]) return |
|
|
|
|
|
const file = `./${this.dir}/${dirName}/${appName}` |
|
|
const watcher = chokidar.watch(file) |
|
|
const key = `${dirName}/${appName}` |
|
|
|
|
|
|
|
|
watcher.on("change", path => { |
|
|
Bot.makeLog("mark", `[修改插件][${dirName}][${appName}]`, "Plugin") |
|
|
this.changePlugin(key) |
|
|
}) |
|
|
|
|
|
|
|
|
watcher.on("unlink", async path => { |
|
|
Bot.makeLog("mark", `[卸载插件][${dirName}][${appName}]`, "Plugin") |
|
|
|
|
|
this.watcher[`${dirName}.${appName}`].removeAllListeners("change") |
|
|
this.priority = this.priority.filter(i => i.key !== key) |
|
|
}) |
|
|
this.watcher[`${dirName}.${appName}`] = watcher |
|
|
} |
|
|
|
|
|
|
|
|
watchDir(dirName) { |
|
|
if (this.watcher[dirName]) return |
|
|
const watcher = chokidar.watch(`./${this.dir}/${dirName}/`) |
|
|
|
|
|
Bot.once("online", () => { |
|
|
|
|
|
watcher.on("add", async PluPath => { |
|
|
const appName = path.basename(PluPath) |
|
|
if (!appName.endsWith(".js")) return |
|
|
Bot.makeLog("mark", `[新增插件][${dirName}][${appName}]`, "Plugin") |
|
|
const key = `${dirName}/${appName}` |
|
|
await this.importPlugin({ |
|
|
name: key, |
|
|
path: `../../${this.dir}/${key}?${moment().format("X")}`, |
|
|
}) |
|
|
|
|
|
this.priority = lodash.orderBy(this.priority, ["priority"], ["asc"]) |
|
|
this.watch(dirName, appName) |
|
|
}) |
|
|
}) |
|
|
this.watcher[dirName] = watcher |
|
|
} |
|
|
} |
|
|
export default new PluginsLoader() |