47 lines
2.6 KiB
TypeScript
47 lines
2.6 KiB
TypeScript
import type {Socket} from 'socket.io-client';
|
|
import {get_bot_context} from '../bot/instance/index.ts';
|
|
import {PluginError, type PluginErrorReason} from '../bot/plugins/error.ts';
|
|
import {plain_plugin_object, valid_plugin_config, valid_plugin_name, valid_plugin_path} from './plugin_validation.ts';
|
|
import {validate_code_map} from './validate_code_map.ts';
|
|
|
|
const codes = validate_code_map.plugin;
|
|
const errors: Record<PluginErrorReason, number> = {
|
|
INVALID_CONFIG: codes.invalid_config, ALREADY_INSTALLED: codes.already_installed,
|
|
NOT_INSTALLED: codes.not_installed, MANAGER_DISPOSED: codes.manager_disposed,
|
|
LOAD_FAILED: codes.load_failed, INIT_FAILED: codes.init_failed, DISPOSE_FAILED: codes.dispose_failed,
|
|
};
|
|
|
|
export async function plugin_event_handler(operation: 'inject' | 'uninject' | 'update_config', socket: Socket, data: unknown, callback: unknown) {
|
|
if (typeof callback !== 'function') {
|
|
socket.emit('error', {error: validate_code_map.web.method_not_allowed});
|
|
return;
|
|
}
|
|
let code: number = validate_code_map.web.invalid_parameter;
|
|
try {
|
|
const fields = operation === 'inject' ? ['bot_id', 'path', 'name', 'config']
|
|
: operation === 'update_config' ? ['bot_id', 'name', 'config'] : ['bot_id', 'name'];
|
|
if (plain_plugin_object(data) && Object.keys(data).length === fields.length
|
|
&& fields.every(field => Object.hasOwn(data, field))
|
|
&& typeof data.bot_id === 'number' && Number.isSafeInteger(data.bot_id) && data.bot_id > 0
|
|
&& valid_plugin_name(data.name)
|
|
&& (operation !== 'inject' || valid_plugin_path(data.path))
|
|
&& (operation === 'uninject' || valid_plugin_config(data.config))) {
|
|
const context = get_bot_context(data.bot_id);
|
|
const manager = context?.plugin_manager;
|
|
if (!context) code = codes.context_missing;
|
|
else if (!manager) code = codes.manager_missing;
|
|
else {
|
|
if (operation === 'inject') await manager.inject({name: data.name, path: data.path as string, config: data.config});
|
|
else if (operation === 'uninject') await manager.uninject(data.name);
|
|
else await manager.update_config(data.name, data.config as Record<string, unknown>);
|
|
code = codes.ok;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
code = error instanceof PluginError ? errors[error.reason] ?? codes.error : codes.error;
|
|
}
|
|
// A transport callback can throw; never retry it or expose its exception to the dispatcher.
|
|
try { callback({status: code === codes.ok ? 'success' : 'error', data: code}); }
|
|
catch {}
|
|
}
|