cyberbot-next
Version:
cyberbot, 基于napcat-ts, nodejs,轻量qq机器人框架。
337 lines • 11.6 kB
JavaScript
import * as cron from 'node-cron';
import { logger } from './logger.js';
import path from 'path';
import fs from 'fs';
import { globalJitiLoader, clearModuleCache } from './jitiLoader.js';
export class CybePlugin {
client;
messageHandlers = new Map();
cronJobs = [];
currentContext = null;
enabled = true;
isSystemPlugin = false;
description;
cron;
initialize(client) {
this.client = client;
if (this.enabled) {
this.setup();
this.setupCronJobs();
}
logger.info(`[${this.name} v${this.version}] 插件已初始化${this.description ? `: ${this.description}` : ''} (${this.enabled ? '启用' : '禁用'})`);
}
cleanup() {
if (this.client) {
for (const handler of this.messageHandlers.values()) {
this.client.removeMessageListener(handler);
}
}
this.cleanupCronJobs();
this.messageHandlers.clear();
this.currentContext = null;
this.client = null;
logger.info(`[${this.name}] 插件资源已清理`);
}
enable() {
if (this.enabled) {
return false;
}
this.enabled = true;
if (this.client) {
this.setup();
this.setupCronJobs();
}
logger.info(`[${this.name}] 插件已启用`);
return true;
}
disable() {
if (this.isSystemPlugin) {
logger.warn(`[${this.name}] 系统插件不能被禁用`);
return false;
}
if (!this.enabled) {
return false;
}
this.enabled = false;
if (this.client) {
for (const handler of this.messageHandlers.values()) {
this.client.removeMessageListener(handler);
}
this.messageHandlers.clear();
}
this.cleanupCronJobs();
logger.info(`[${this.name}] 插件已禁用`);
return true;
}
registerMessageHandler(type, handler) {
if (!this.client) {
logger.error('插件未初始化');
return;
}
if (!this.enabled) {
return;
}
if (this.messageHandlers.has(type)) {
const oldHandler = this.messageHandlers.get(type);
if (oldHandler) {
this.client.removeMessageListener(oldHandler);
}
}
const wrappedHandler = (e) => {
if (!this.enabled)
return;
if (type === 'message' ||
(type === 'message.private' && 'user_id' in e && !('group_id' in e) && 'message_type' in e && e.message_type === 'private') ||
(type === 'message.group' && 'group_id' in e && 'message_type' in e && e.message_type === 'group') ||
(type === 'notice' && 'notice_type' in e) ||
(type === 'request' && 'request_type' in e)) {
try {
this.currentContext = e;
this.client.currentContext = e;
handler(e);
}
finally {
this.client.currentContext = null;
}
}
};
this.messageHandlers.set(type, wrappedHandler);
this.client.onMessage(wrappedHandler);
}
setupCronJobs() {
if (!this.cron || !this.client)
return;
for (const [expression, task] of this.cron) {
try {
const job = cron.schedule(expression, () => {
try {
task(this.client);
}
catch (error) {
logger.error(`[${this.name}] 定时任务执行错误:${error}`);
}
});
this.cronJobs.push(job);
logger.info(`[${this.name}] 定时任务已设置: ${expression}`);
}
catch (error) {
logger.error(`[${this.name}] 定时任务设置失败:${error}`);
}
}
}
cleanupCronJobs() {
for (const job of this.cronJobs) {
job.stop();
}
this.cronJobs = [];
}
getClient() {
if (!this.client) {
logger.error('插件未初始化');
return null;
}
return this.client;
}
reply(content, quote = false) {
if (!this.currentContext) {
logger.error(`[${this.name}] 回复消息失败:无法获取消息上下文`);
return Promise.resolve({ message_id: 0 });
}
return this.client.events.reply(this.currentContext, content, quote);
}
}
export class PluginManager {
plugins = new Map();
client;
configPath;
constructor(client, configPath = 'config.json') {
this.client = client;
this.configPath = configPath;
}
loadPlugin(plugin, isSystemPlugin = false) {
if (this.plugins.has(plugin.name)) {
logger.debug(`插件 ${plugin.name} 已经加载`);
return;
}
try {
const cybePlugin = plugin;
if (isSystemPlugin) {
cybePlugin.isSystemPlugin = true;
cybePlugin.enabled = true;
}
plugin.initialize(this.client);
this.plugins.set(plugin.name, cybePlugin);
logger.debug(`插件 ${plugin.name} 加载成功 (${cybePlugin.enabled ? '启用' : '禁用'})`);
}
catch (error) {
logger.error(`插件 ${plugin.name} 加载失败:${error}`);
}
}
unloadPlugin(pluginName) {
const plugin = this.plugins.get(pluginName);
if (plugin) {
try {
plugin.cleanup();
this.plugins.delete(pluginName);
logger.info(`插件 ${pluginName} 卸载成功`);
return true;
}
catch (error) {
logger.error(`插件 ${plugin.name} 卸载失败:${error}`);
}
}
return false;
}
enablePlugin(pluginName) {
const plugin = this.plugins.get(pluginName);
if (!plugin) {
logger.error(`插件 ${pluginName} 不存在`);
return false;
}
if (plugin.enable()) {
this.updatePluginConfig(pluginName, true);
return true;
}
return false;
}
disablePlugin(pluginName) {
const plugin = this.plugins.get(pluginName);
if (!plugin) {
logger.error(`插件 ${pluginName} 不存在`);
return false;
}
if (plugin.isSystemPlugin) {
logger.warn(`系统插件 ${pluginName} 不能被禁用`);
return false;
}
if (plugin.disable()) {
this.updatePluginConfig(pluginName, false);
return true;
}
return false;
}
async reloadPlugin(pluginName) {
const plugin = this.plugins.get(pluginName);
if (!plugin) {
logger.error(`插件 ${pluginName} 不存在`);
return false;
}
try {
const wasEnabled = plugin.enabled;
const isSystem = plugin.isSystemPlugin;
plugin.cleanup();
this.plugins.delete(pluginName);
const success = await this.loadPluginFromFile(pluginName, isSystem, wasEnabled);
if (success) {
logger.info(`插件 ${pluginName} 重载成功`);
return true;
}
else {
logger.error(`插件 ${pluginName} 重载失败`);
return false;
}
}
catch (error) {
logger.error(`插件 ${pluginName} 重载失败:${error}`);
return false;
}
}
async loadPluginFromFile(pluginName, isSystem = false, shouldEnable = true) {
try {
const pluginsDir = path.join(process.cwd(), 'plugins');
const pluginDir = path.join(pluginsDir, pluginName);
if (!fs.existsSync(pluginDir)) {
logger.error(`插件目录 ${pluginDir} 不存在`);
return false;
}
const possibleExtensions = ['.ts', '.js'];
let pluginPath = '';
let found = false;
for (const ext of possibleExtensions) {
const testPath = path.join(pluginDir, `index${ext}`);
if (fs.existsSync(testPath)) {
pluginPath = testPath;
found = true;
break;
}
}
if (!found) {
logger.error(`在插件 ${pluginName} 中未找到任何可用的入口文件`);
return false;
}
clearModuleCache(pluginPath);
logger.info(`正在使用 jiti 加载插件: ${pluginPath}`);
const pluginModule = await globalJitiLoader.import(pluginPath);
const pluginClass = Object.values(pluginModule).find((item) => typeof item === 'function' && item.name.endsWith('Plugin'));
if (pluginClass) {
const newPlugin = new pluginClass();
newPlugin.isSystemPlugin = isSystem;
newPlugin.enabled = shouldEnable;
this.loadPlugin(newPlugin, isSystem);
logger.info(`插件 ${pluginName} 已从文件重新加载`);
return true;
}
else {
logger.error(`在 ${pluginName} 中未找到插件类`);
return false;
}
}
catch (error) {
logger.error(`加载插件 ${pluginName} 失败:${error}`);
return false;
}
}
updatePluginConfig(pluginName, enabled) {
try {
const configPath = this.configPath;
if (!fs.existsSync(configPath)) {
logger.error('配置文件不存在');
return;
}
const configData = fs.readFileSync(configPath, 'utf-8');
const config = JSON.parse(configData);
if (enabled) {
if (!config.plugins.user.includes(pluginName)) {
config.plugins.user.push(pluginName);
}
}
else {
config.plugins.user = config.plugins.user.filter((p) => p !== pluginName);
}
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
logger.info(`插件 ${pluginName} 配置已更新`);
}
catch (error) {
logger.error(`更新插件配置失败:${error}`);
}
}
getLoadedPlugins() {
return Array.from(this.plugins.keys());
}
getPluginInfo() {
const result = [];
for (const [name, plugin] of this.plugins.entries()) {
result.push({
name,
version: plugin.version,
description: plugin.description,
enabled: plugin.enabled,
isSystem: plugin.isSystemPlugin
});
}
return result;
}
unloadAllPlugins() {
for (const [name, plugin] of this.plugins.entries()) {
try {
plugin.cleanup();
logger.info(`插件 ${name} 卸载成功`);
}
catch (error) {
logger.error(`插件 ${name} 卸载失败:${error}`);
}
}
this.plugins.clear();
}
}
//# sourceMappingURL=pluginManager.js.map