176 lines
4.5 KiB
TypeScript
176 lines
4.5 KiB
TypeScript
import { IPlugin } from '../plugin';
|
|
import { IMessage, MessageResolver, Protocol } from '../types';
|
|
import { ScopedEventEmitter } from '../util/events';
|
|
|
|
export interface IChannel {
|
|
name: string;
|
|
plugins: string[];
|
|
enabled: boolean;
|
|
}
|
|
|
|
/**
|
|
* This class is used to direct messages and events from one plugin to many others
|
|
* using a pre-set list of plugins that are allowed to talk to one another.
|
|
*
|
|
* Generally when creating a channel, the first plugin should be the source of messages
|
|
* or events, such as a protocol or other service, and the rest of the plugins in the
|
|
* list are the handlers.
|
|
*/
|
|
export class ChannelManager {
|
|
protected channels: IChannel[] = [];
|
|
|
|
constructor(protected stream: ScopedEventEmitter) {}
|
|
|
|
/**
|
|
* Ensure that the message or event source is a plugin
|
|
* @param source Event source
|
|
* @returns Plugin or null
|
|
*/
|
|
public static determinePlugin(source: any): IPlugin | null {
|
|
if (source != null) {
|
|
if (source.manifest) {
|
|
return source;
|
|
}
|
|
|
|
if (source.plugin && source.plugin.manifest) {
|
|
return source.plugin;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Initialize the event handlers for channels
|
|
* @param configured Initial configuration of channels
|
|
*/
|
|
public initialize(configured: IChannel[]): void {
|
|
this.addPreconfiguredChannels(configured);
|
|
|
|
for (const event of ['message', 'event', 'special']) {
|
|
this.stream.on('channel', event, (data: IMessage) => {
|
|
let msr;
|
|
if (event === 'message') {
|
|
msr = new MessageResolver(data);
|
|
}
|
|
|
|
const plugin = ChannelManager.determinePlugin(data.source);
|
|
if (!plugin) {
|
|
return;
|
|
}
|
|
|
|
const source = plugin.manifest.name;
|
|
const emitTo = this.getChannelsByPluginName(source, data.source);
|
|
|
|
for (const chan of emitTo) {
|
|
if (chan.plugins.length < 2) {
|
|
continue;
|
|
}
|
|
|
|
for (const pl of chan.plugins) {
|
|
if (
|
|
pl !== source &&
|
|
!(pl.indexOf('/') !== -1 && pl.split('/')[0] === source)
|
|
) {
|
|
this.stream.emitTo(pl, event, data, chan, msr);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all the channels a plugin is in
|
|
* @param plugin Plugin name
|
|
* @param source Source protocol of the event
|
|
* @returns List of channels to send to
|
|
*/
|
|
protected getChannelsByPluginName(
|
|
plugin: string,
|
|
source: Protocol
|
|
): IChannel[] {
|
|
const list = [];
|
|
for (const chan of this.channels) {
|
|
if (chan.enabled === false) {
|
|
continue;
|
|
}
|
|
for (const pl of chan.plugins) {
|
|
if (pl.indexOf('/') !== -1) {
|
|
const split = pl.split('/');
|
|
if (split[0] === plugin && split[1] === source.name) {
|
|
list.push(chan);
|
|
}
|
|
} else if (pl === plugin) {
|
|
list.push(chan);
|
|
}
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
/**
|
|
* Validate a preconfigured channel list and add them to the list
|
|
* @param channels Preconfigured channel list
|
|
*/
|
|
protected addPreconfiguredChannels(channels: IChannel[]): void {
|
|
for (const chan of channels) {
|
|
if (!chan.name) {
|
|
throw new Error('Channel name is mandatory.');
|
|
}
|
|
|
|
if (!chan.plugins) {
|
|
throw new Error('Channel plugins list is mandatory.');
|
|
}
|
|
|
|
this.channels.push(chan);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get a channel by name
|
|
* @param name Channel name
|
|
* @returns Channel or undefined
|
|
*/
|
|
public getChannelByName(name: string): IChannel | undefined {
|
|
return this.channels.find((c) => c.name === name);
|
|
}
|
|
|
|
/**
|
|
* Add a new channel to the channels list
|
|
* @param chan Channel configuration
|
|
* @returns Channel
|
|
*/
|
|
public addChannel(chan: IChannel): IChannel {
|
|
const exists = this.getChannelByName(chan.name);
|
|
if (exists) {
|
|
throw new Error('Channel by that name already exists!');
|
|
}
|
|
this.channels.push(chan);
|
|
return chan;
|
|
}
|
|
|
|
/**
|
|
* Remove a channel by name or the channel itself
|
|
* @param chan Name of channel or channel
|
|
*/
|
|
public removeChannel(chan: string | IChannel): void {
|
|
if (typeof chan === 'string') {
|
|
const getchan = this.getChannelByName(chan);
|
|
if (!getchan) {
|
|
throw new Error('Channel by that name doesn\'t exists!');
|
|
}
|
|
chan = getchan;
|
|
}
|
|
this.channels.splice(this.channels.indexOf(chan), 1);
|
|
}
|
|
|
|
/**
|
|
* Get all channels
|
|
* @returns All channels
|
|
*/
|
|
public getAll(): IChannel[] {
|
|
return this.channels;
|
|
}
|
|
}
|