diff --git a/src/common/time.ts b/src/common/time.ts index 29cad1e..c20bf03 100644 --- a/src/common/time.ts +++ b/src/common/time.ts @@ -37,25 +37,25 @@ export function readableTime(timems: number): string { const time = Math.floor(timems); if (time < 60) { - return zf(time) + 's'; + return zf(Math.floor(time)) + 's'; } else if (time < 3600) { - return zf(time / 60) + - 'm ' + zf(time % 60) + 's'; + return zf(Math.floor(time / 60)) + + 'm ' + zf(Math.floor(time % 60)) + 's'; } else if (time < 86400) { - return zf(time / 3600) + - 'h ' + zf((time % 3600) / 60) + - 'm ' + zf((time % 3600) % 60) + 's'; + return zf(Math.floor(time / 3600)) + + 'h ' + zf(Math.floor((time % 3600) / 60)) + + 'm ' + zf(Math.floor((time % 3600) % 60)) + 's'; } else if (time < 31536000) { - return (time / 86400) + - 'd ' + zf((time % 86400) / 3600) + - 'h ' + zf((time % 3600) / 60) + - 'm ' + zf((time % 3600) % 60) + 's'; + return (Math.floor(time / 86400)) + + 'd ' + zf(Math.floor((time % 86400) / 3600)) + + 'h ' + zf(Math.floor((time % 3600) / 60)) + + 'm ' + zf(Math.floor((time % 3600) % 60)) + 's'; } else { - return (time / 31536000) + - 'y ' + zf((time % 31536000) / 86400) + - 'd ' + zf((time % 86400) / 3600) + - 'h ' + zf((time % 3600) / 60) + - 'm ' + zf((time % 3600) % 60) + 's'; + return (Math.floor(time / 31536000)) + + 'y ' + zf(Math.floor((time % 31536000) / 86400)) + + 'd ' + zf(Math.floor((time % 86400) / 3600)) + + 'h ' + zf(Math.floor((time % 3600) / 60)) + + 'm ' + zf(Math.floor((time % 3600) % 60)) + 's'; } } diff --git a/src/plugin/plugin.ts b/src/plugin/plugin.ts index 682fa15..ea2a841 100644 --- a/src/plugin/plugin.ts +++ b/src/plugin/plugin.ts @@ -11,6 +11,7 @@ export interface IPlugin { export class Plugin implements IPlugin { public service: Service | null = null; + protected on = this.addEventListener; constructor( public manifest: IPluginManifest, diff --git a/src/types/message-format.ts b/src/types/message-format.ts index 8fc1371..655b20d 100644 --- a/src/types/message-format.ts +++ b/src/types/message-format.ts @@ -19,8 +19,11 @@ or any hex value prepended with # Formats: + action + code + multicode (multiline code) bold - italic + italics emphasis strike underline @@ -211,3 +214,74 @@ export class Formatter { } } +export class HTMLFormatter extends Formatter { + public formatting: Method = { + code: {start: '', end: ''}, + action: {start: '', end: ''}, + bold: {start: '', end: ''}, + italics: {start: '', end: ''}, + emphasis: {start: '', end: ''}, + underline: {start: '', end: ''}, + multicode: {start: '
', end: '
'}, + }; + + constructor() { + super(true, true); + } + + public color(color: string, msg: string): string { + return `${msg}`; + } + + public format(method: string, msg: string): string { + if (!this.formatting[method]) { + return msg; + } + + if (this.formatting[method].start && this.formatting[method].end) { + return this.formatting[method].start + msg + this.formatting[method].end; + } else { + return this.formatting[method] + msg + this.formatting[method]; + } + } + + public strip(msg: string): string { + return msg.replace(/<\/?[^>]+(>|$)/g, ''); + } +} + +export class MarkdownFormatter extends Formatter { + public formatting: Method = { + code: {start: '`', end: '`'}, + bold: {start: '**', end: '*'}, + action: {start: '*', end: '*'}, + italics: {start: '*', end: '*'}, + emphasis: {start: '*', end: '*'}, + underline: {start: '__', end: '__'}, + multicode: {start: '```', end: '```'}, + }; + + constructor() { + super(true, false); + } + + public color(color: string, msg: string): string { + return msg; + } + + public format(method: string, msg: string): string { + if (!this.formatting[method]) { + return msg; + } + + if (this.formatting[method].start && this.formatting[method].end) { + return this.formatting[method].start + msg + this.formatting[method].end; + } else { + return this.formatting[method] + msg + this.formatting[method]; + } + } + + public strip(msg: string): string { + return msg; + } +} diff --git a/src/types/message.ts b/src/types/message.ts index 5ceac50..31b1efc 100644 --- a/src/types/message.ts +++ b/src/types/message.ts @@ -1,8 +1,5 @@ -import { IPlugin } from '../plugin/plugin'; import { Protocol } from './protocol'; -// TODO: Source specification to support plugin services. - export enum EMessageType { message = 0, roomJoin = 1, @@ -12,24 +9,89 @@ export enum EMessageType { edit = 5, } +/** + * Message target specification. ID and name are mandatory, + * and they could be the same, depending on the protocol. + */ export interface IMessageTarget { id: string; name: string; server?: string; } +/** + * Message format + */ export interface IMessage { + /** + * Uniquely identify this message + */ id?: any; + /** + * Type of the message. See EMessageType + */ type: EMessageType; + /** + * Data included in the message. Could be an object or a string. + * Use `text` to get text value. + */ data: any; + /** + * Text value of the message. + */ + text: string; + /** + * Source protocol of the message. + */ source: Protocol; + /** + * If the sender has not registered themselves. + * Applicable to, for example, NickServ of IRC. + */ guest?: boolean; + /** + * The room where the message was sent in. + */ target?: IMessageTarget; + /** + * Information about the sender. + */ sender?: IMessageTarget; + /** + * Full ID of the sender (`plugin/protocol/id`). + */ fullSenderID?: string; + /** + * Full ID of the room (`plugin/protocol/(s:server)/id`). + */ fullRoomID?: string; + /** + * The time the message was sent. + */ time: Date; + /** + * True if the message is a direct message to the bot. + */ direct?: boolean; + /** + * True when resolve has been called at least once. + */ resolved: boolean; + /** + * Bot's response to this message. Will be passed to the protocol, + * which usually sends it through the formatter and then to the service + * it is connected to. + * @param args Message data + */ resolve(...args: any[]): void; + + /** + * Insert a mention into the response + * @param target UserTarget to mention (i.e. `msg.sender`) + */ + mention(target: IMessageTarget): string; + + kick?(reason: string): void; + ban?(reason: string): void; + mute?(reason: string): void; } diff --git a/src/types/protocol.ts b/src/types/protocol.ts index 4adb07a..60ce8b1 100644 --- a/src/types/protocol.ts +++ b/src/types/protocol.ts @@ -2,7 +2,7 @@ import { randomBytes } from 'crypto'; import { EventEmitter } from 'events'; import { IPlugin } from '../plugin'; -import { IMessage } from './message'; +import { IMessage, IMessageTarget } from './message'; import { Formatter } from './message-format'; export class Protocol extends EventEmitter { @@ -22,6 +22,12 @@ export class Protocol extends EventEmitter { // This should be set to true when the protocol fails for any reason public failed = false; + // This should identify the bot itself + public me: IMessageTarget = { + id: '', + name: '', + }; + constructor(public plugin: IPlugin, public config: any) { super(); this.passEvents();