Files
OpenParty/core/classes/PluginManager.js

150 lines
6.3 KiB
JavaScript

/**
* Plugin Manager for OpenParty
* Handles loading and managing plugins
*/
const fs = require('fs');
const path = require('path');
const Plugin = require('./Plugin');
const Logger = require('../utils/logger');
class PluginManager {
/**
* Create a new plugin manager
*/
constructor() {
this.plugins = new Map();
this.logger = new Logger('PluginManager');
}
/**
* Load plugins from settings
* @returns {Map} The loaded plugins
*/
loadPlugins() {
this.logger.info('Loading plugins from plugins directory...');
this.plugins.clear(); // Clear existing plugins before reloading
const pluginsDir = path.resolve(__dirname, '../../plugins');
if (!fs.existsSync(pluginsDir)) {
this.logger.warn(`Plugins directory not found: ${pluginsDir}`);
return this.plugins;
}
const pluginFolders = fs.readdirSync(pluginsDir, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name);
pluginFolders.forEach(folderName => {
const pluginFolderPath = path.join(pluginsDir, folderName);
const manifestPath = path.join(pluginFolderPath, 'manifest.json');
if (!fs.existsSync(manifestPath)) {
this.logger.warn(`Manifest.json not found in plugin folder: ${folderName}. Skipping.`);
return;
}
try {
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
if (!manifest.name || !manifest.main || !manifest.execution) {
this.logger.error(`Invalid manifest.json in ${folderName}: Missing name, main, or execution. Skipping.`);
return;
}
const mainPluginFile = path.join(pluginFolderPath, manifest.main);
if (!fs.existsSync(mainPluginFile)) {
this.logger.error(`Main plugin file '${manifest.main}' not found in ${folderName} at ${mainPluginFile}. Skipping.`);
return;
}
const pluginInstance = require(mainPluginFile);
if (pluginInstance instanceof Plugin) {
const originalPluginName = pluginInstance.name;
pluginInstance.manifest = manifest;
pluginInstance.name = manifest.name; // Override name from manifest
pluginInstance.description = manifest.description || pluginInstance.description; // Override description
// If the name was overridden by the manifest, update the logger instance to use the new name
if (pluginInstance.logger.moduleName !== manifest.name) {
this.logger.info(`Plugin class-defined name ('${originalPluginName}') differs from manifest ('${manifest.name}') for plugin in folder '${folderName}'. Updating logger to use manifest name '${manifest.name}'.`);
pluginInstance.logger = new Logger(manifest.name); // Re-initialize logger with manifest name
}
this.plugins.set(manifest.name, pluginInstance);
this.logger.info(`Loaded plugin: ${manifest.name} (v${manifest.version || 'N/A'}) from ${folderName}`);
} else {
this.logger.error(`Error: ${mainPluginFile} from ${folderName} is not a valid plugin. It does not extend the 'Plugin' class.`);
}
} catch (error) {
this.logger.error(`Error loading plugin from ${folderName}: ${error.message}\n${error.stack}`);
}
});
// Process overrides
const pluginsToOverride = new Set();
this.plugins.forEach(pInstance => {
if (pInstance.manifest && Array.isArray(pInstance.manifest.override)) {
pInstance.manifest.override.forEach(pluginNameToOverride => {
pluginsToOverride.add(pluginNameToOverride);
});
}
});
pluginsToOverride.forEach(pluginNameToOverride => {
if (this.plugins.has(pluginNameToOverride) && this.plugins.get(pluginNameToOverride).isEnabled()) {
this.logger.info(`Plugin '${pluginNameToOverride}' is being overridden and will be disabled by another plugin.`);
this.plugins.get(pluginNameToOverride).disable();
}
});
return this.plugins;
}
/**
* Initialize plugins based on execution type
* @param {Express} app - The Express application instance
* @param {string} executionType - The execution type (pre-load, init, etc.)
*/
initializePlugins(app, executionType) {
this.logger.info(`Initializing ${executionType} plugins...`);
this.plugins.forEach((pluginInstance) => {
if (pluginInstance.manifest && pluginInstance.isEnabled && pluginInstance.isEnabled()) {
try {
if (pluginInstance.manifest.execution === executionType) {
this.logger.info(`Calling initroute for plugin: ${pluginInstance.name} (Execution Type: ${executionType})`);
pluginInstance.initroute(app);
} else {
// This log can be verbose, uncomment if needed for debugging
// this.logger.info(`Skipping plugin ${pluginInstance.name}: Execution type mismatch (Plugin: ${pluginInstance.manifest.execution}, Required: ${executionType}).`);
}
} catch (error) {
this.logger.error(`Error initializing plugin ${pluginInstance.name}: ${error.message}\n${error.stack}`);
}
} else if (pluginInstance.manifest && (!pluginInstance.isEnabled || !pluginInstance.isEnabled())) {
this.logger.info(`Skipping disabled plugin: ${pluginInstance.name}`);
}
});
}
/**
* Get a plugin by name
* @param {string} name - The name of the plugin
* @returns {Plugin|null} The plugin or null if not found
*/
getPlugin(name) {
return this.plugins.get(name) || null;
}
/**
* Get all loaded plugins
* @returns {Map} The loaded plugins
*/
getPlugins() {
return this.plugins;
}
}
module.exports = PluginManager;