diff --git a/src/index.ts b/src/index.ts index 1b64183..0050ea5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ export * from './utility/message-formatting'; export * from './utility/mode-from-prefix'; export * from './utility/nickserv-validator'; export * from './utility/parser'; +export * from './utility/platform-base64'; export * from './utility/typed-event-emitter'; export * from './utility/truncate'; export * from './utility/user-mapper'; diff --git a/src/irc.ts b/src/irc.ts index b53dfc1..7e14312 100644 --- a/src/irc.ts +++ b/src/irc.ts @@ -4,7 +4,12 @@ import { IRCConnector, IRCConnectorConstructor, } from './types/impl.interface'; -import { IIRCLine, IIRCOptions, IQueue } from './types/irc.interfaces'; +import { + IIRCLine, + IIRCOptions, + IIRCServerData, + IQueue, +} from './types/irc.interfaces'; import { Collector, MultiLineCollector, @@ -12,39 +17,36 @@ import { WhoisCollector, } from './utility/collector'; import { parse } from './utility/parser'; +import { encodeBase64 } from './utility/platform-base64'; import { TypedEventEmitter } from './utility/typed-event-emitter'; import { parseWho, WhoResponse } from './utility/who-parser'; import { parseWhois, WhoisResponse } from './utility/whois-parser'; -const encodeBase64 = (input: string) => { - if (window !== undefined && btoa !== undefined) { - return btoa(input); - } else if (Buffer !== undefined) { - return Buffer.from(input).toString('base64'); - } - return input; -}; - -// TODO: typed emitter export class IRCConnectionWrapper extends TypedEventEmitter implements IRCCommunicator { + /** + * Channels the bot is currently in. + */ public channels: string[] = []; + + /** + * Current collectors waiting for their reply. + */ public queue: IQueue[] = []; + + /** + * Login success status. + */ public authenticated = false; - public serverData: { [key: string]: any } = { - /** - * (true host)Name of this server. - */ + + /** + * Information about the IRC server gathered during runtime + */ + public serverData: IIRCServerData = { name: '', - /** - * Supported channel user modes from the server (e.g. `ohv: @%+`) - */ supportedModes: {}, - /** - * Everything this server supports. See IRC documentation for command `005` or `RPL_ISUPPORT` for more info. - */ serverSupports: {}, }; @@ -64,6 +66,14 @@ export class IRCConnectionWrapper this.handlers(); } + /** + * Send a raw command to the server. + * + * **WARNING:** Line break characters could have an unintended side-effect! + * Filter user-generated content! + * @param format Command + * @param args Command arguments + */ write(format: string, ...args: any[]): void { this.connection?.write(format, ...args); } @@ -166,7 +176,7 @@ export class IRCConnectionWrapper if ( this.options.bot && this.serverData.serverSupports.USERMODES && - this.serverData.serverSupports.USERMODES.includes('B') + (this.serverData.serverSupports.USERMODES as string).includes('B') ) { this.write('MODE %s +B', this.options.nick); } @@ -302,8 +312,6 @@ export class IRCConnectionWrapper } } else if (t[0] === 'NETWORK') { this.serverData.network = t[1]; - } else if (t[0] === 'CHANNELLEN') { - this.serverData.maxChannelLength = parseInt(t[1], 10); } let numeral: string | number = t[1]; @@ -451,6 +459,9 @@ export class IRCConnectionWrapper } } + /** + * Create a new connection to the configured IRC server. + */ public async connect(): Promise { if (this.connection) { await this.connection.destroy(); @@ -490,6 +501,10 @@ export class IRCConnectionWrapper this.authenticate(); } + /** + * Disconnect from the IRC server gracefully, sends `QUIT`. + * @param reason Reason for disconnection + */ public async disconnect(reason?: string): Promise { if (!this.connected) { if (this.connection) { @@ -511,10 +526,18 @@ export class IRCConnectionWrapper }); } + /** + * Get connection status. `authenticated` is a more reliable indicator + * of a successful connection. + */ public get connected() { return this.connection?.connected ?? false; } + /** + * Asynchronously ping the server. + * @returns Ping in milliseconds + */ public async getPing(): Promise { return new Promise((resolve) => { const sendTime = Date.now(); @@ -605,6 +628,14 @@ export class IRCConnectionWrapper }); } + /** + * Add a collector to the queue. + * This is used to grab lines from the server, wait for them + * and return them in a callback. + * @param collector IRC line collector + * @param line Command to send to the server + * @param args Arguments to the command + */ public useCollector(collector: IQueue, line: string, ...args: any[]) { this.queue.push(collector); this.write(line, ...args); diff --git a/src/types/irc.interfaces.ts b/src/types/irc.interfaces.ts index cbc3096..cfc9456 100644 --- a/src/types/irc.interfaces.ts +++ b/src/types/irc.interfaces.ts @@ -147,3 +147,19 @@ export interface IIRCOptions { */ connOpts?: Record; } + +export interface IIRCServerData { + /** + * (true host)Name of this server. + */ + name: string; + /** + * Everything this server supports. See IRC documentation for command `005` or `RPL_ISUPPORT` for more info. + */ + serverSupports: Record; + /** + * Supported channel user modes from the server (e.g. `ohv: @%+`) + */ + supportedModes: Record; + network?: string; +} diff --git a/src/utility/platform-base64.ts b/src/utility/platform-base64.ts new file mode 100644 index 0000000..1330bce --- /dev/null +++ b/src/utility/platform-base64.ts @@ -0,0 +1,13 @@ +/** + * Encode a string in Base64, cross-platform (no dependencies) + * @param input String to encode + * @returns Base64-encoded string + */ +export const encodeBase64 = (input: string) => { + if (window !== undefined && btoa !== undefined) { + return btoa(input); + } else if (Buffer !== undefined) { + return Buffer.from(input).toString('base64'); + } + return input; +};