control plugin loads schemas

This commit is contained in:
Evert Prants 2021-10-09 13:14:12 +03:00
parent ab57ca89f6
commit 49b0660f95
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
3 changed files with 124 additions and 64 deletions

View File

@ -3,7 +3,7 @@
"name": "control", "name": "control",
"description": "Squeebot Plugin Management API and sockets", "description": "Squeebot Plugin Management API and sockets",
"tags": ["api", "control", "management"], "tags": ["api", "control", "management"],
"version": "0.1.2", "version": "0.1.3",
"dependencies": [], "dependencies": [],
"npmDependencies": [] "npmDependencies": []
} }

View File

@ -23,7 +23,7 @@ interface ControlCommand {
let controlCommands: ControlCommand[] = [ let controlCommands: ControlCommand[] = [
{ {
name: 'execute', name: 'loadPlugin',
plugin: 'control', plugin: 'control',
execute: async (p: ControlPlugin, plugin: string): Promise<void> => { execute: async (p: ControlPlugin, plugin: string): Promise<void> => {
if (!plugin) { if (!plugin) {
@ -325,11 +325,7 @@ let controlCommands: ControlCommand[] = [
if (!name) { if (!name) {
throw new Error('This function takes 1 argument.'); throw new Error('This function takes 1 argument.');
} }
const plugin = p.plugins.get(name); return p.getPluginSchema(name);
if (!plugin) {
throw new Error('This plugin has not registered a schema in control.');
}
return plugin;
}, },
}, },
{ {
@ -405,6 +401,126 @@ class ControlPlugin extends Plugin {
this.addEventListener('core', (core: ISqueebotCore) => this.core = core); this.addEventListener('core', (core: ISqueebotCore) => this.core = core);
this.emitTo('core', 'request-core', this.name); this.emitTo('core', 'request-core', this.name);
this.createSocket(); this.createSocket();
this.getPluginSchema(this.name).catch(
() => logger.error('[control] How embarrasing! control could not load it\'s own schema!')
);
}
/**
* Load a plugin schema from it's schema file, if it exists.
* @param name Plugin name
* @returns Plugin schema
*/
public async loadPluginSchema(name: string): Promise<any> {
if (!this.core) {
throw new Error('control could not access the core.');
}
const { pluginsPath } = this.core.environment;
const schemaPath = path.join(pluginsPath, name, 'schema.json');
let schema: any;
try {
const fileRead = await fs.readFile(schemaPath, { encoding: 'utf8' });
schema = JSON.parse(fileRead);
} catch (e: any) {
throw new Error('No schema file found, it is not accessible or is not valid JSON.');
}
if (!schema.type) {
throw new Error('Schema does not specify what type of object it is referencing.');
}
this.plugins.set(name, schema);
return schema;
}
/**
* Register a plugin's configuration schema directly instead of reading from file.
* @param name Plugin name
* @param confspec Static schema
*/
public registerPluginConfigSchema(name: string, confspec?: any): void {
this.plugins.set(name, confspec);
}
/**
* Load a plugin's configuration schema from memory or from file.
* @param name Plugin name
* @returns Schema
* @throws Error if schema is not found or is invalid
*/
public async getPluginSchema(name: string): Promise<any> {
if (this.plugins.has(name)) {
return this.plugins.get(name);
}
return this.loadPluginSchema(name);
}
/**
* Execute a registered control command.
* @param command Control command
* @param args Control command arguments
* @returns Control command response
*/
public async executeControlCommand(command: string, args: string[]): Promise<any> {
if (!this.core) {
throw new Error('The control plugin cannot control the bot right now.');
}
const cmdobj = controlCommands.find(k => k.name === command);
if (!cmdobj || !cmdobj.execute) {
throw new Error('No such command');
}
return cmdobj.execute.call(this, this, ...args);
}
/**
* Register a new custom control command.
* @param obj ControlCommand object
*/
public registerControlCommand(obj: ControlCommand): void {
if (!obj.execute || !obj.name || !obj.plugin) {
throw new Error('Invalid command object.');
}
const exists = controlCommands.find(k => k.name === obj.name);
if (exists) {
throw new Error('Control commands should not be overwritten.');
}
controlCommands.push(obj);
logger.log('[%s] registered control command', this.name, obj.name);
}
public listControlCommands(): string[] {
return controlCommands.map((command) => command.name);
}
@EventListener('pluginUnload')
public unloadEventHandler(plugin: string | Plugin): void {
if (plugin === this.name || plugin === this) {
logger.debug('[%s]', this.name, 'shutting down..');
if (this.server) {
logger.log('[%s] Stopping socket server..', this.name);
this.server.close();
for (const sock of this.sockets) {
sock.destroy();
}
this.sockets.clear();
}
this.plugins.clear();
this.emit('pluginUnloaded', this);
}
}
@EventListener('pluginUnloaded')
public unloadedEventHandler(plugin: string | Plugin): void {
if (typeof plugin !== 'string') {
plugin = plugin.manifest.name;
}
this.plugins.delete(plugin);
controlCommands = controlCommands.filter(k => k.plugin !== plugin);
} }
private errorToClient(socket: TLSSocket | Socket, error: Error): void { private errorToClient(socket: TLSSocket | Socket, error: Error): void {
@ -551,62 +667,6 @@ class ControlPlugin extends Plugin {
this.name, c.bind.toString()); this.name, c.bind.toString());
}); });
} }
public registerPluginConfigSchema(name: string, confspec?: any): void {
this.plugins.set(name, confspec);
}
public async executeControlCommand(command: string, args: string[]): Promise<any> {
if (!this.core) {
throw new Error('The control plugin cannot control the bot right now.');
}
const cmdobj = controlCommands.find(k => k.name === command);
if (!cmdobj || !cmdobj.execute) {
throw new Error('No such command');
}
return cmdobj.execute.call(this, this, ...args);
}
public registerControlCommand(obj: ControlCommand): void {
if (!obj.execute || !obj.name || !obj.plugin) {
throw new Error('Invalid command object.');
}
const exists = controlCommands.find(k => k.name === obj.name);
if (exists) {
throw new Error('Control commands should not be overwritten.');
}
controlCommands.push(obj);
logger.log('[%s] registered control command', this.name, obj.name);
}
@EventListener('pluginUnload')
public unloadEventHandler(plugin: string | Plugin): void {
if (plugin === this.name || plugin === this) {
logger.debug('[%s]', this.name, 'shutting down..');
if (this.server) {
logger.log('[%s] Stopping socket server..', this.name);
this.server.close();
for (const sock of this.sockets) {
sock.destroy();
}
this.sockets.clear();
}
this.plugins.clear();
this.config.save().then(() =>
this.emit('pluginUnloaded', this));
}
}
@EventListener('pluginUnloaded')
public unloadedEventHandler(plugin: string | Plugin): void {
if (typeof plugin !== 'string') {
plugin = plugin.manifest.name;
}
this.plugins.delete(plugin);
controlCommands = controlCommands.filter(k => k.plugin !== plugin);
}
} }
module.exports = ControlPlugin; module.exports = ControlPlugin;

View File

@ -3,7 +3,7 @@
"plugins": [ "plugins": [
{ {
"name": "control", "name": "control",
"version": "0.1.2" "version": "0.1.3"
}, },
{ {
"name": "cron", "name": "cron",