cli/src/core.ts

151 lines
4.2 KiB
TypeScript
Raw Normal View History

2020-11-21 15:41:31 +00:00
import * as path from 'path';
import { ChannelManager } from '@squeebot/core/lib/channel';
import { PluginManager, PluginMetaLoader } from '@squeebot/core/lib/plugin';
2020-11-28 19:08:56 +00:00
import { RepositoryManager } from '@squeebot/core/lib/plugin/repository';
2020-11-21 15:41:31 +00:00
import { NPMExecutor } from '@squeebot/core/lib/npm';
import { Configuration, IEnvironment } from '@squeebot/core/lib/types';
import { ScopedEventEmitter } from '@squeebot/core/lib/util';
2020-12-03 17:44:53 +00:00
import { ISqueebotCore, logger } from '@squeebot/core/lib/core';
2020-11-21 15:41:31 +00:00
const defaultConfiguration: {[key: string]: any} = {
channels: [],
enabled: [],
name: 'squeebot',
};
2021-09-18 22:20:34 +00:00
/**
* Reference implementation of a Squeebot system using core classes.
*/
2020-12-03 17:44:53 +00:00
export class Squeebot implements ISqueebotCore {
2020-12-06 18:21:09 +00:00
public npm: NPMExecutor = new NPMExecutor(this.environment, '@squeebot/core');
2020-11-21 15:41:31 +00:00
public stream: ScopedEventEmitter = new ScopedEventEmitter();
2020-11-28 19:08:56 +00:00
public pluginManager: PluginManager = new PluginManager(
2020-11-21 15:41:31 +00:00
[],
this.stream,
this.environment,
this.npm);
2020-11-28 20:26:42 +00:00
2020-11-28 19:08:56 +00:00
public repositoryManager: RepositoryManager = new RepositoryManager(
this.environment,
this.pluginManager,
);
2020-11-21 15:41:31 +00:00
2020-11-28 19:08:56 +00:00
public channelManager: ChannelManager = new ChannelManager(this.stream);
2020-11-21 15:41:31 +00:00
2020-11-28 19:08:56 +00:00
public pluginLoader: PluginMetaLoader = new PluginMetaLoader(
2020-11-21 15:41:31 +00:00
this.environment,
);
2020-11-28 19:08:56 +00:00
public config: Configuration = new Configuration(
2020-11-21 15:41:31 +00:00
this.environment,
path.join(this.environment.configurationPath, 'squeebot.json'),
defaultConfiguration,
);
private shuttingDown = false;
2020-11-28 19:08:56 +00:00
constructor(public environment: IEnvironment) {}
2020-11-21 15:41:31 +00:00
2021-12-18 08:36:28 +00:00
public async initialize(autostart = true): Promise<void> {
2020-11-21 15:41:31 +00:00
// Load configuration
await this.config.load();
// Initialize npm executor
await this.npm.loadPackageFile();
// Load all plugins
const plugins = await this.pluginLoader.loadAll();
// Give manager all loaded manifests
this.pluginManager.addAvailable(plugins);
2020-11-28 19:08:56 +00:00
// Load repositories
await this.repositoryManager.loadFromFiles();
2020-11-21 15:41:31 +00:00
// Start channels
this.channelManager.initialize(this.config.get('channels'));
2020-11-29 11:34:47 +00:00
// Send core on request
this.stream.on('core', 'request-core', (pl: string) =>
this.stream.emitTo(pl, 'core', this));
2022-08-25 18:20:36 +00:00
// Handle uncaught exceptions to prevent the whole bot from getting bricked
process.on('uncaughtException', (err) => {
logger.error('Process caught an unhandled exception:', err.stack);
this.stream.emit('error', err);
});
// Handle uncaught rejections to prevent the whole bot from getting bricked
process.on('unhandledRejection', (reason, promise) => {
logger.error('Process caught an unhandled promise rejection:', reason);
logger.error('In promise:', promise);
const error = new Error(reason?.toString());
error.name = 'unhandledRejection';
this.stream.emit('error', error);
});
// Start enabled plugins
if (autostart) {
await this.startPlugins();
}
2020-11-21 15:41:31 +00:00
}
public async startPlugins(): Promise<void> {
for (const pluginName of this.config.get('enabled')) {
const getManifest = this.pluginManager.getAvailableByName(pluginName);
if (!getManifest) {
logger.error(new Error(`Failed to start ${pluginName}: no manifest available. You might not have installed it.`));
2021-09-18 22:20:34 +00:00
continue;
2020-11-21 15:41:31 +00:00
}
try {
await this.pluginManager.load(getManifest);
2021-09-03 17:23:12 +00:00
} catch (e: any) {
2020-11-28 13:33:56 +00:00
logger.error('Failed to start %s:', pluginName, e.stack);
2020-11-21 15:41:31 +00:00
}
}
}
public attachReadline(rl: any): void {
2020-11-28 13:33:56 +00:00
// Pass readline to logger
logger.setReadline(rl);
2020-11-21 15:41:31 +00:00
// Send readline on request
this.stream.on('core', 'request-cli', () =>
this.stream.emit('cli', rl));
// Handle readline events
rl.on('line', (l: string) => this.stream.emit('cli-line', l));
rl.on('SIGINT', () => this.shutdown());
}
public shutdown(): void {
if (this.shuttingDown) {
return;
}
logger.warn('Shutting down..');
this.shuttingDown = true;
2021-09-18 22:20:34 +00:00
// Subsystems confirmed shutdown, go ahead and exit.
2020-11-21 15:41:31 +00:00
this.stream.on('core', 'shutdown', (state: number) => {
if (state > 0) {
2020-11-28 13:33:56 +00:00
this.config.save().then((x) => process.exit(0));
2020-11-21 15:41:31 +00:00
}
});
2021-09-18 22:20:34 +00:00
// Prompt subsystems to prepare for shutting down
2020-11-21 15:41:31 +00:00
this.stream.emitTo('core', 'shutdown', 0);
}
}