implement new sendTo method

This commit is contained in:
Evert Prants 2021-10-01 21:00:03 +03:00
parent bac88841b0
commit 2483a67088
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
7 changed files with 334 additions and 2 deletions

9
cron/plugin.json Normal file
View File

@ -0,0 +1,9 @@
{
"main": "plugin.js",
"name": "cron",
"description": "API for plugin-scoped cron tasks",
"tags": ["timers", "cron", "scheduler", "api"],
"version": "1.0.0",
"dependencies": ["control?"],
"npmDependencies": ["node-cron@3.0.0"]
}

166
cron/plugin.ts Normal file
View File

@ -0,0 +1,166 @@
import { logger } from '@squeebot/core/lib/core';
import {
Plugin,
Configurable,
EventListener,
IPlugin,
} from '@squeebot/core/lib/plugin';
import nodeCron, { ScheduledTask } from 'node-cron';
type ExecutorFn = (...args: any[]) => void;
class CronWrapper {
private cronTask: ScheduledTask | null = null;
public id = Math.random().toString(36).slice(2);
public stopped = true;
public destroyed = false;
constructor(
public expression: string,
public taskFn: ExecutorFn,
public origin: IPlugin,
) {}
assert(): void {
if (!nodeCron.validate(this.expression)) {
throw new Error('Invalid cron pattern!');
}
}
start(): void {
if (this.destroyed) {
logger.warn('[cron] Someone tried to start a destroyed task! This could indicate a memory leak!');
return;
}
if (!this.cronTask) {
this.assert();
this.cronTask = nodeCron.schedule(this.expression, this.execute, {
scheduled: false,
});
}
this.cronTask.start();
this.stopped = false;
}
stop(): void {
if (!this.cronTask) {
return;
}
this.cronTask.stop();
this.stopped = true;
}
destroy(): void {
this.destroyed = true;
if (!this.cronTask) {
return;
}
this.cronTask.destroy();
this.cronTask = null;
}
execute(): void {
this.taskFn.call(this.origin);
}
belongsTo(plugin: IPlugin | string): boolean {
if (typeof plugin === 'string') {
return this.origin.manifest.name === plugin;
}
return plugin === this.origin ||
plugin.manifest.name === this.origin.manifest.name;
}
}
// @Configurable({})
class CronPlugin extends Plugin {
private timers: CronWrapper[] = [];
@EventListener('pluginUnload')
public unloadEventHandler(plugin: string | Plugin): void {
if (plugin === this.name || plugin === this) {
this.timers.forEach((timer) => timer.destroy());
this.timers = [];
this.emit('pluginUnloaded', this);
}
}
@EventListener('pluginUnloaded')
public unloadedEventHandler(plugin: string | Plugin): void {
this.timers.forEach((timer) => {
if (timer.belongsTo(plugin)) {
timer.destroy();
}
});
this.cleanUp();
}
/**
* @returns A list of tasks including id, cron expression and origin plugin.
*/
public getList(): string[][] {
return this.timers.map((timer) => ([
timer.id,
timer.expression,
timer.origin.manifest.name,
timer.destroyed || timer.stopped ? 'stopped' : 'running',
]));
}
/**
* Register a new cron task
* @param plugin Plugin registering this task
* @param expression Cron expression
* @param taskFn Function to execute
* @param autostart Start the scheduler on add
* @returns The task
*/
public registerTimer(
plugin: IPlugin,
expression: string,
taskFn: ExecutorFn,
autostart = true,
): CronWrapper {
const newTimer = new CronWrapper(expression, taskFn, plugin);
newTimer.assert();
if (autostart) {
newTimer.start();
}
return newTimer;
}
/**
* Remove a scheduled timer by ID or by the object itself.
* Always use this to ensure that memory is properly cleared.
* @param timer Timer or timer ID
*/
public removeTimer(timer: string | CronWrapper): void {
if (typeof timer === 'string') {
const find = this.timers.find((item) => item.id === timer);
if (find) {
find.destroy();
}
} else {
timer.destroy();
}
this.cleanUp();
}
/**
* Remove destroyed timers from the cache
*/
private cleanUp(): void {
this.timers = this.timers.filter((timer) => !timer.destroyed);
}
}
module.exports = CronPlugin;

85
package-lock.json generated
View File

@ -10,10 +10,12 @@
"license": "ISC",
"dependencies": {
"@squeebot/core": "^3.3.1",
"node-cron": "^3.0.0",
"typescript": "^4.4.2"
},
"devDependencies": {
"@types/node": "^16.7.10"
"@types/node": "^16.7.10",
"@types/node-cron": "^2.0.4"
}
},
"../core": {
@ -54,6 +56,21 @@
"integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==",
"dev": true
},
"node_modules/@types/node-cron": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/node-cron/-/node-cron-2.0.4.tgz",
"integrity": "sha512-vXzgDRWCZpuut5wJVZtluEnkNhzGojYlyMch2c4kMj7H74L8xTLytVlgQzj+/17wfcjs49aJDFBDglFSGt7GeA==",
"dev": true,
"dependencies": {
"@types/tz-offset": "*"
}
},
"node_modules/@types/tz-offset": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/@types/tz-offset/-/tz-offset-0.0.0.tgz",
"integrity": "sha512-XLD/llTSB6EBe3thkN+/I0L+yCTB6sjrcVovQdx2Cnl6N6bTzHmwe/J8mWnsXFgxLrj/emzdv8IR4evKYG2qxQ==",
"dev": true
},
"node_modules/at-least-node": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
@ -164,6 +181,36 @@
"node": ">=10"
}
},
"node_modules/moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==",
"engines": {
"node": "*"
}
},
"node_modules/moment-timezone": {
"version": "0.5.33",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz",
"integrity": "sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==",
"dependencies": {
"moment": ">= 2.9.0"
},
"engines": {
"node": "*"
}
},
"node_modules/node-cron": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.0.tgz",
"integrity": "sha512-DDwIvvuCwrNiaU7HEivFDULcaQualDv7KoNlB/UU1wPW0n1tDEmBJKhEIE6DlF2FuoOHcNbLJ8ITL2Iv/3AWmA==",
"dependencies": {
"moment-timezone": "^0.5.31"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
@ -238,6 +285,21 @@
"integrity": "sha512-S63Dlv4zIPb8x6MMTgDq5WWRJQe56iBEY0O3SOFA9JrRienkOVDXSXBjjJw6HTNQYSE2JI6GMCR6LVbIMHJVvA==",
"dev": true
},
"@types/node-cron": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/node-cron/-/node-cron-2.0.4.tgz",
"integrity": "sha512-vXzgDRWCZpuut5wJVZtluEnkNhzGojYlyMch2c4kMj7H74L8xTLytVlgQzj+/17wfcjs49aJDFBDglFSGt7GeA==",
"dev": true,
"requires": {
"@types/tz-offset": "*"
}
},
"@types/tz-offset": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/@types/tz-offset/-/tz-offset-0.0.0.tgz",
"integrity": "sha512-XLD/llTSB6EBe3thkN+/I0L+yCTB6sjrcVovQdx2Cnl6N6bTzHmwe/J8mWnsXFgxLrj/emzdv8IR4evKYG2qxQ==",
"dev": true
},
"at-least-node": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
@ -316,6 +378,27 @@
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
},
"moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
},
"moment-timezone": {
"version": "0.5.33",
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz",
"integrity": "sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==",
"requires": {
"moment": ">= 2.9.0"
}
},
"node-cron": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.0.tgz",
"integrity": "sha512-DDwIvvuCwrNiaU7HEivFDULcaQualDv7KoNlB/UU1wPW0n1tDEmBJKhEIE6DlF2FuoOHcNbLJ8ITL2Iv/3AWmA==",
"requires": {
"moment-timezone": "^0.5.31"
}
},
"semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",

View File

@ -12,9 +12,11 @@
"license": "ISC",
"dependencies": {
"@squeebot/core": "^3.3.1",
"node-cron": "^3.0.0",
"typescript": "^4.4.2"
},
"devDependencies": {
"@types/node": "^16.7.10"
"@types/node": "^16.7.10",
"@types/node-cron": "^2.0.4"
}
}

View File

@ -5,6 +5,10 @@
"name": "control",
"version": "0.1.1"
},
{
"name": "cron",
"version": "1.0.0"
},
{
"name": "permissions",
"version": "0.1.0"
@ -12,6 +16,10 @@
{
"name": "simplecommands",
"version": "1.1.1"
},
{
"name": "xprotocol",
"version": "1.0.0"
}
],
"typescript": true

9
xprotocol/plugin.json Normal file
View File

@ -0,0 +1,9 @@
{
"main": "plugin.js",
"name": "xprotocol",
"description": "API for sending messages to other protocols",
"tags": ["messages", "relaying", "api"],
"version": "1.0.0",
"dependencies": ["control?"],
"npmDependencies": []
}

55
xprotocol/plugin.ts Normal file
View File

@ -0,0 +1,55 @@
import { ISqueebotCore, logger } from '@squeebot/core/lib/core';
import {
Plugin,
EventListener,
} from '@squeebot/core/lib/plugin';
class XProtocolPlugin extends Plugin {
private core: ISqueebotCore | null = null;
@EventListener('pluginUnload')
public unloadEventHandler(plugin: string | Plugin): void {
if (plugin === this.name || plugin === this) {
this.core = null;
this.emit('pluginUnloaded', this);
}
}
public async sendTo(target: string, ...data: any[]): Promise<boolean> {
if (!this.core) {
return false;
}
// Find target plugin
const rxSplit = target.split('/');
const plugin = this.core.pluginManager.getLoadedByName(rxSplit[0]);
if (!plugin || !plugin.service) {
return false;
}
// Find target protocol
const protocol = plugin.service.getProtocolByName(rxSplit[1]);
if (!protocol || !protocol.running) {
return false;
}
return protocol.sendTo(target, ...data);
}
initialize(): void {
this.on('core', (core: ISqueebotCore) => {
this.core = core;
});
this.emitTo('core', 'request-core', this.name);
this.on('send', (data: any[]) => {
const target = data[0];
this.sendTo(target, ...data.slice(1)).catch((error: Error) => {
logger.error(`[sendto] Sending to protocol from event failed:`, error.message ?? error);
});
});
}
}
module.exports = XProtocolPlugin;