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

View File

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

View File

@ -30,6 +30,7 @@
*/
import { thousandsSeparator, timeSince, toHHMMSS } from '../common';
import { ProtocolFeatureFlag } from './protocol-flags';
// Color Utilities
@ -73,15 +74,18 @@ export function b16toHex(name: string): string {
return b16Colors[name];
}
interface IMethod {
export interface IMethod {
start: string;
end: string;
}
declare type Keyed = {[key: string]: string};
declare type Method = {[key: string]: IMethod};
export declare type Keyed = {[key: string]: string};
export declare type Method = {[key: string]: IMethod};
const b16Colors: Keyed = {
/**
* Color-hex relations
*/
export const b16Colors: Keyed = {
black: '#000000',
darkblue: '#00007f',
green: '#009300',
@ -104,20 +108,51 @@ const b16Colors: Keyed = {
* Base class for message formatting
*/
export class Formatter {
/**
* Available colors.
*/
public colors: Keyed = {};
/**
* Available formatting methods.
*/
public formatting: Method = {};
/**
* Color escape character, if available.
*/
public colorEscape = '';
constructor(
/**
* @deprecated Use feature flags
*/
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 {
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 {
if (!this.supportFormatting) {
if (!this.supports(ProtocolFeatureFlag.FORMATTING) && !this.supportFormatting) {
return msg;
}
@ -136,37 +171,41 @@ export class Formatter {
return msg;
}
// Object compositor.
// 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 }],
...
]
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.
title - A title field
description - Descriptive field
metric - A Number value. Requires integer, will be transformed into xxx,xxx,xxx
time - Time value. Requires UNIX timestamp.
timesince - Time since value. Requires UNIX timestamp, will be transformed into x seconds/minutes/hours/days ago
duration - Duration value. Requires integer, will be transformed into HH:MM:SS
content - Full message body.
* color - The color of the field. Not always supported.
bold/b/strong - Bold text
i/italic - Italic text
color - A colored text. Not always supported.
url - An URL.
Parameters:
* label - Label for this URL
image - An Image.
/**
* Object compositor.
*
* This default function turns objects into plain text without any formatting.
* Override this in your protocol for the desired effect.
*
* ```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.
* * title - A title field
* * description - Descriptive field
* * metric - A Number value. Requires integer, will be transformed into xxx,xxx,xxx
* * time - Time value. Requires UNIX timestamp.
* * timesince - Time since value. Requires UNIX timestamp, will be transformed into x seconds/minutes/hours/days ago
* * duration - Duration value. Requires integer, will be transformed into HH:MM:SS
* * content - Full message body.
* * color - The color of the field. Not always supported.
* * bold/b/strong - Bold text
* * 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 {
const str = [];
@ -209,7 +248,7 @@ export class Formatter {
label = elemParams.label[elemParams.label.length - 1];
}
str.push(elemParams.label + ': ' + elemValue);
str.push(label + ': ' + elemValue);
} else {
str.push(elemValue);
}
@ -218,6 +257,17 @@ export class Formatter {
// May return an object, but your protocol must support it.
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>'},
};
constructor() {
super(true, true);
constructor(flags?: ProtocolFeatureFlag[]) {
super(true, true, flags);
}
public color(color: string, msg: string): string {
@ -273,8 +323,8 @@ export class MarkdownFormatter extends Formatter {
multicode: {start: '```', end: '```'},
};
constructor() {
super(true, false);
constructor(flags?: ProtocolFeatureFlag[]) {
super(true, false, flags);
}
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 { IMessage, IMessageTarget } from './message';
import { Formatter } from './message-format';
import { ProtocolFeatureFlag } from './protocol-flags';
/**
* The base class for a protocol handler.
*/
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');
// override this!
/**
* Protocol type name.
*/
public type = 'GenericProtocol';
/**
* Indicates that the protocol is currently running.
*/
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;
// 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;
// This should identify the bot itself
/**
* This identifies the bot itself.
*/
public me: IMessageTarget = {
id: '',
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();
this.passEvents();
}
/**
* Protocol connection's configured name.
*/
public get name(): string {
return this.config.name;
}
/**
* Protocol's running status.
*/
public get status(): boolean {
return this.running;
}
/**
* Start the protocol. Create connections here.
* @param args Start arguments
*/
public start(...args: any[]): void {
this.running = true;
this.emit('running');
}
/**
* Stop the protocol. This should stop any connections.
* @param force Force stop
*/
public stop(force = false): void {
if (!this.running) {
return;
@ -72,10 +116,24 @@ export class Protocol extends EventEmitter {
throw new Error('Method not implemented.');
}
/**
* Full name of this protocol (`<plugin name>/<protocol connection name>`)
*/
public get fullName(): string {
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 {
this.on('stop', (force) => this.stop(force));
this.on('start', (...args: any[]) => {