core/src/types/service.ts

171 lines
4.0 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-empty-function */
import { EMessageType, IMessage } from './message';
import { Protocol } from './protocol';
/**
* Service is used to run, keep track of and kill Protocols.
*/
export class Service {
private protocols: Map<string, Protocol> = new Map();
private stopped = false;
constructor(private protoType: any) {}
public setExternalEvents(pto: Protocol): void {
const eventMessage: IMessage = {
type: EMessageType.protocolStart,
resolve: () => {},
reject: () => {},
mention: () => '',
data: pto,
source: pto,
text: `Protocol ${pto.fullName} running`,
time: new Date(),
resolved: true,
};
pto.on('running', () => {
pto.plugin.stream.emitTo('channel', 'event', eventMessage);
});
pto.on('stopped', () => {
pto.plugin.stream.emitTo('channel', 'event', {
...eventMessage,
type: EMessageType.protocolExit,
text: `Protocol ${pto.fullName} stopped`,
});
// TODO: restart handling
});
}
/**
* Add a new protocol to this service.
* @param pto Protocol instance
* @param autostart Automatically start the protocol
* @returns Protocol instance
*/
public use(pto: Protocol, autostart = true): Protocol {
// This service is no longer accepting new protocols
if (this.stopped) {
return pto;
}
const n = pto.name;
if (n == null || n === '') {
throw new Error('Invalid Protocol configuration: Needs an unique name!');
}
if (this.protocols.has(n)) {
return this.protocols.get(n) as Protocol;
}
this.protocols.set(n, pto);
this.setExternalEvents(pto);
if (autostart) {
pto.start();
}
return pto;
}
/**
* Stop a protocol running in this service
* @param pto Protocol instance or name
* @param force Force stop
*/
public stop(pto: string | Protocol, force = false): void {
let proto: Protocol;
if (typeof pto === 'string') {
if (!this.protocols.get(pto)) {
return;
}
proto = this.protocols.get(pto) as Protocol;
} else {
proto = pto;
if (!this.protocols.get(pto.name)) {
// Not part of this service!
return;
}
}
proto.stop(force);
this.protocols.delete(proto.name);
}
/**
* Find a protocol by name.
* @param name Protocol name
* @returns Protocol instance or undefined
*/
public getProtocolByName(name: string): Protocol | undefined {
return this.protocols.get(name);
}
/**
* Get all protocol instances running in this service.
* @returns List of all protocol instances running in this service
*/
public getAll(): Protocol[] {
return Array.from(this.protocols.values());
}
/**
* Gracefully stops everything running in this service
*/
public stopAll(): Promise<void> {
return new Promise((resolve, reject) => {
if (this.stopped) {
return resolve();
}
let toStop = +this.protocols.size;
let killed = false;
// Nothing to stop
if (toStop === 0) {
return resolve();
}
// If the protocols fail to stop within a time frame, we have to kill
// them in order to prevent lingering connections.
const killTimeout = setTimeout(() => {
killed = true;
this.die();
resolve();
}, 10000);
// Stop everything and wait for them to announce the fact
// that they've stopped.
this.stopped = true;
for (const [name, proto] of this.protocols) {
proto.once('stopped', () => {
if (killed) {
return;
}
toStop--;
if (toStop <= 0) {
this.protocols.clear();
clearTimeout(killTimeout);
resolve();
}
});
proto.stop();
}
});
}
/**
* Forcefully kills everything running in this service,
*/
public die(): void {
this.stopped = true;
for (const [name, proto] of this.protocols) {
proto.stop(true);
}
this.protocols.clear();
}
}