This commit is contained in:
Evert Prants 2023-08-02 19:25:20 +03:00
parent 320e4cef84
commit 0f31e22f88
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
6 changed files with 1280 additions and 1033 deletions

1959
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@squeebot/core", "name": "@squeebot/core",
"version": "3.5.0", "version": "3.6.0",
"description": "Squeebot v3 core for the execution environment", "description": "Squeebot v3 core for the execution environment",
"main": "lib/index.js", "main": "lib/index.js",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",
@ -18,20 +18,20 @@
"author": "Evert \"Diamond\" Prants <evert@lunasqu.ee>", "author": "Evert \"Diamond\" Prants <evert@lunasqu.ee>",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@types/dateformat": "^3.0.1", "@types/dateformat": "^5.0.0",
"@types/fs-extra": "^9.0.12", "@types/fs-extra": "^11.0.1",
"@types/node": "^16.7.10", "@types/node": "^20.4.5",
"@types/semver": "^7.3.8", "@types/semver": "^7.5.0",
"@types/tar": "^4.0.5", "@types/tar": "^6.1.5",
"@typescript-eslint/eslint-plugin": "^5.7.0", "@typescript-eslint/eslint-plugin": "^6.2.1",
"eslint": "^8.4.1", "eslint": "^8.46.0",
"typescript": "^4.4.2" "typescript": "^5.1.6"
}, },
"dependencies": { "dependencies": {
"dateformat": "^4.5.1", "dateformat": "^5.0.3",
"fs-extra": "^10.0.0", "fs-extra": "^11.1.1",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"semver": "^7.3.5", "semver": "^7.5.4",
"tar": "^6.1.11" "tar": "^6.1.15"
} }
} }

View File

@ -4,5 +4,6 @@ export * from './plugin-config';
export * from './message'; export * from './message';
export * from './message-format'; export * from './message-format';
export * from './protocol'; export * from './protocol';
export * from './protocol-flags';
export * from './service'; export * from './service';
export * from './staged-handler'; export * from './staged-handler';

View File

@ -30,6 +30,7 @@
*/ */
import { thousandsSeparator, timeSince, toHHMMSS } from '../common'; import { thousandsSeparator, timeSince, toHHMMSS } from '../common';
import { ProtocolFeatureFlag } from './protocol-flags';
// Color Utilities // Color Utilities
@ -73,15 +74,18 @@ export function b16toHex(name: string): string {
return b16Colors[name]; return b16Colors[name];
} }
interface IMethod { export interface IMethod {
start: string; start: string;
end: string; end: string;
} }
declare type Keyed = {[key: string]: string}; export declare type Keyed = {[key: string]: string};
declare type Method = {[key: string]: IMethod}; export declare type Method = {[key: string]: IMethod};
const b16Colors: Keyed = { /**
* Color-hex relations
*/
export const b16Colors: Keyed = {
black: '#000000', black: '#000000',
darkblue: '#00007f', darkblue: '#00007f',
green: '#009300', green: '#009300',
@ -104,20 +108,51 @@ const b16Colors: Keyed = {
* Base class for message formatting * Base class for message formatting
*/ */
export class Formatter { export class Formatter {
/**
* Available colors.
*/
public colors: Keyed = {}; public colors: Keyed = {};
/**
* Available formatting methods.
*/
public formatting: Method = {}; public formatting: Method = {};
/**
* Color escape character, if available.
*/
public colorEscape = ''; public colorEscape = '';
constructor( constructor(
/**
* @deprecated Use feature flags
*/
public supportFormatting = false, public supportFormatting = false,
public supportColors = false /**
* @deprecated Use feature flags
*/
public supportColors = false,
public flags: ProtocolFeatureFlag[] = []
) {} ) {}
/**
* Wrap a string with code which renders colors.
* @param color Color
* @param msg Message
* @returns Wrapped string
*/
public color(color: string, msg: string): string { public color(color: string, msg: string): string {
return msg; return msg;
} }
/**
* Wrap a string with code which renders message formatting.
* @param method Format to use
* @param msg Message
* @returns Wrapped string
*/
public format(method: string, msg: string): string { public format(method: string, msg: string): string {
if (!this.supportFormatting) { if (!this.supports(ProtocolFeatureFlag.FORMATTING) && !this.supportFormatting) {
return msg; return msg;
} }
@ -136,37 +171,41 @@ export class Formatter {
return msg; return msg;
} }
// Object compositor. /**
// This default function turns objects into plain text without any formatting. * Object compositor.
// Override this in your protocol for the desired effect. *
/* * This default function turns objects into plain text without any formatting.
[ * Override this in your protocol for the desired effect.
['element type', 'text', { param: value }], *
... * ```js
] * [
* ['element type', 'text', { param: value }],
Element types: * ...
field - A field type * ]
Parameters: * ```
* label - Label for this field. If an array, the first item is considered an "icon" and wont have ':' appended to it. *
* type - The type of the field. * Element types:
title - A title field * * field - A field type
description - Descriptive field * * Parameters:
metric - A Number value. Requires integer, will be transformed into xxx,xxx,xxx * * label - Label for this field.
time - Time value. Requires UNIX timestamp. * If an array, the first item is considered an "icon" and wont have ':' appended to it.
timesince - Time since value. Requires UNIX timestamp, will be transformed into x seconds/minutes/hours/days ago * * type - The type of the field.
duration - Duration value. Requires integer, will be transformed into HH:MM:SS * * title - A title field
content - Full message body. * * description - Descriptive field
* color - The color of the field. Not always supported. * * metric - A Number value. Requires integer, will be transformed into xxx,xxx,xxx
bold/b/strong - Bold text * * time - Time value. Requires UNIX timestamp.
i/italic - Italic text * * timesince - Time since value. Requires UNIX timestamp, will be transformed into x seconds/minutes/hours/days ago
color - A colored text. Not always supported. * * duration - Duration value. Requires integer, will be transformed into HH:MM:SS
url - An URL. * * content - Full message body.
Parameters: * * color - The color of the field. Not always supported.
* label - Label for this URL * * bold/b/strong - Bold text
image - An Image. * * i/italic - Italic text
* * color - A colored text. Not always supported.
* * url - An URL.
* * Parameters:
* * label - Label for this URL
* * image - An Image.
*/ */
public compose(objs: any): any { public compose(objs: any): any {
const str = []; const str = [];
@ -209,7 +248,7 @@ export class Formatter {
label = elemParams.label[elemParams.label.length - 1]; label = elemParams.label[elemParams.label.length - 1];
} }
str.push(elemParams.label + ': ' + elemValue); str.push(label + ': ' + elemValue);
} else { } else {
str.push(elemValue); str.push(elemValue);
} }
@ -218,6 +257,17 @@ export class Formatter {
// May return an object, but your protocol must support it. // May return an object, but your protocol must support it.
return str.join(' '); return str.join(' ');
} }
/**
* Check if this protocol supports a feature flag
* @param flag Feature flag to check for
* @returns Boolean
*/
public supports(flag: ProtocolFeatureFlag | ProtocolFeatureFlag[]) {
return Array.isArray(flag)
? flag.every((entry) => this.flags.includes(entry))
: this.flags.includes(flag);
}
} }
/** /**
@ -234,8 +284,8 @@ export class HTMLFormatter extends Formatter {
multicode: {start: '<pre><code>', end: '</code></pre>'}, multicode: {start: '<pre><code>', end: '</code></pre>'},
}; };
constructor() { constructor(flags?: ProtocolFeatureFlag[]) {
super(true, true); super(true, true, flags);
} }
public color(color: string, msg: string): string { public color(color: string, msg: string): string {
@ -273,8 +323,8 @@ export class MarkdownFormatter extends Formatter {
multicode: {start: '```', end: '```'}, multicode: {start: '```', end: '```'},
}; };
constructor() { constructor(flags?: ProtocolFeatureFlag[]) {
super(true, false); super(true, false, flags);
} }
public color(color: string, msg: string): string { public color(color: string, msg: string): string {

123
src/types/protocol-flags.ts Normal file
View File

@ -0,0 +1,123 @@
export enum ProtocolFeatureFlag {
/**
* Protocol is primarily short-form messaging.
* Small chatrooms like IRC, syncplay chat, etc..
*/
SHORT_FORM_MESSAGING,
/**
* Protocol supports images.
*/
IMAGES,
/**
* Protocol supports colored messages.
*/
COLORS,
/**
* Protocol supports formatted.
*/
FORMATTING,
/**
* Protocol supports plain messaging.
*/
PLAIN,
/**
* Protocol supports HTML messages.
*/
HTML,
/**
* Protocol supports Markdown messages.
*/
MARKDOWN,
/**
* Protocol supports custom embeds.
*/
CUSTOM_EMBEDS,
/**
* Protocol is entirely one-to-one communication.
*/
SINGLE_RECIPIENT,
/**
* Protocol is communication with multiple recipients through one message target.
*/
SINGLE_TO_MULTI_RECIPIENT,
/**
* Protocol has optional (end-to-end) encryption.
*/
OPTIONAL_ENCRYPTION,
/**
* Protocol is encrypted (use for end-to-end encrypted systems)
*/
ENCRYPTED,
/**
* Protocol (natively) supports emoji.
*/
EMOJI,
/**
* Protocol supports reactions.
*/
REACTIONS,
/**
* Protocol supports mentions.
*/
MENTION,
/**
* Protocol connection manages multiple "servers"
*/
MULTISERVER,
/**
* Protocol supports voice chat.
*/
VOICE,
/**
* Protocol supports video chat.
*/
VIDEO,
/**
* Protocol supports threads.
*/
THREADS,
/**
* Protocol supports kicking.
*/
KICK,
/**
* Protocol supports banning.
*/
BAN,
/**
* Protocol supports muting.
*/
MUTE,
/**
* Protocol has a separate method of account verification.
*/
ACCOUNT_VERIFICATION,
EVENT_MESSAGE,
EVENT_ROOM_JOIN,
EVENT_ROOM_LEAVE,
EVENT_ROOM_NAME_CHANGE
}

View File

@ -4,51 +4,95 @@ import { EventEmitter } from 'events';
import { IPlugin } from '../plugin'; import { IPlugin } from '../plugin';
import { IMessage, IMessageTarget } from './message'; import { IMessage, IMessageTarget } from './message';
import { Formatter } from './message-format'; import { Formatter } from './message-format';
import { ProtocolFeatureFlag } from './protocol-flags';
/** /**
* The base class for a protocol handler. * The base class for a protocol handler.
*/ */
export class Protocol extends EventEmitter { export class Protocol extends EventEmitter {
public format: Formatter = new Formatter(false, false); /**
* Protocol message formatter.
*/
public format: Formatter = new Formatter(false, false, this.flags);
/**
* Connection ID
*/
public id = randomBytes(4).toString('hex'); public id = randomBytes(4).toString('hex');
// override this! /**
* Protocol type name.
*/
public type = 'GenericProtocol'; public type = 'GenericProtocol';
/**
* Indicates that the protocol is currently running.
*/
public running = false; public running = false;
// This should be set to true when the protocol was stopped for any reason /**
// at any time. * This should be set to true when the protocol was stopped for any reason
* at any time.
*/
public stopped = false; public stopped = false;
// This should be set to true when the protocol fails for any reason /**
* This will be set to true when the protocol fails for any reason
*/
public failed = false; public failed = false;
// This should identify the bot itself /**
* This identifies the bot itself.
*/
public me: IMessageTarget = { public me: IMessageTarget = {
id: '', id: '',
name: '', name: '',
}; };
constructor(public plugin: IPlugin, public config: any) { /**
* @param plugin Reference to the plugin which provides this protocol.
* @param config Protocol connection instance configuration.
* @param flags Feature flags which describe this protocol.
*/
constructor(
public plugin: IPlugin,
public config: any,
public flags: ProtocolFeatureFlag[] = [
ProtocolFeatureFlag.EVENT_MESSAGE,
ProtocolFeatureFlag.PLAIN,
]
) {
super(); super();
this.passEvents(); this.passEvents();
} }
/**
* Protocol connection's configured name.
*/
public get name(): string { public get name(): string {
return this.config.name; return this.config.name;
} }
/**
* Protocol's running status.
*/
public get status(): boolean { public get status(): boolean {
return this.running; return this.running;
} }
/**
* Start the protocol. Create connections here.
* @param args Start arguments
*/
public start(...args: any[]): void { public start(...args: any[]): void {
this.running = true; this.running = true;
this.emit('running'); this.emit('running');
} }
/**
* Stop the protocol. This should stop any connections.
* @param force Force stop
*/
public stop(force = false): void { public stop(force = false): void {
if (!this.running) { if (!this.running) {
return; return;
@ -72,10 +116,24 @@ export class Protocol extends EventEmitter {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
/**
* Full name of this protocol (`<plugin name>/<protocol connection name>`)
*/
public get fullName(): string { public get fullName(): string {
return this.plugin.manifest.name + '/' + this.name; return this.plugin.manifest.name + '/' + this.name;
} }
/**
* Check if this protocol supports a feature flag
* @param flag Feature flag to check for
* @returns Boolean
*/
public supports(flag: ProtocolFeatureFlag | ProtocolFeatureFlag[]) {
return Array.isArray(flag)
? flag.every((entry) => this.flags.includes(entry))
: this.flags.includes(flag);
}
protected passEvents(): void { protected passEvents(): void {
this.on('stop', (force) => this.stop(force)); this.on('stop', (force) => this.stop(force));
this.on('start', (...args: any[]) => { this.on('start', (...args: any[]) => {