From 2a4b1041294fbc44efc968f39daa482a46d79570 Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Sun, 29 Nov 2020 15:16:00 +0200 Subject: [PATCH] Initial commit --- .gitignore | 6 + irc/format.ts | 150 +++++++++++++ irc/irc.ts | 425 ++++++++++++++++++++++++++++++++++++ irc/parser.ts | 77 +++++++ irc/plugin.json | 9 + irc/plugin.ts | 273 +++++++++++++++++++++++ ircprototest/plugin.json | 9 + ircprototest/plugin.ts | 35 +++ package-lock.json | 460 +++++++++++++++++++++++++++++++++++++++ package.json | 17 ++ squeebot.repo.json | 14 ++ tsconfig.json | 1 + tslint.json | 153 +++++++++++++ 13 files changed, 1629 insertions(+) create mode 100644 .gitignore create mode 100644 irc/format.ts create mode 100644 irc/irc.ts create mode 100644 irc/parser.ts create mode 100644 irc/plugin.json create mode 100644 irc/plugin.ts create mode 100644 ircprototest/plugin.json create mode 100644 ircprototest/plugin.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 squeebot.repo.json create mode 100644 tsconfig.json create mode 100644 tslint.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9d09377 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/node_modules/ +/.out/ +deployment.json +*.js +*.d.ts +*.tsbuildinfo \ No newline at end of file diff --git a/irc/format.ts b/irc/format.ts new file mode 100644 index 0000000..47398fa --- /dev/null +++ b/irc/format.ts @@ -0,0 +1,150 @@ +import { thousandsSeparator, toHHMMSS, timeSince } from '@squeebot/core/lib/common'; +import { approximateB16Color, Formatter } from '@squeebot/core/lib/types'; + +export class IRCFormatter extends Formatter { + public formatting = { + bold: {start: '\u0002', end: '\u000F'}, + italic: {start: '\u0016', end: '\u000F'}, + emphasis: {start: '\u0016', end: '\u000F'}, + underline: {start: '\u001F', end: '\u000F'}, + }; + + public colors: {[key: string]: string} = { + black: '\u00031', + darkblue: '\u00032', + green: '\u00033', + red: '\u00034', + brown: '\u00035', + purple: '\u00036', + gold: '\u00037', + yellow: '\u00038', + limegreen: '\u00039', + cyan: '\u000310', + lightblue: '\u000311', + blue: '\u000312', + pink: '\u000313', + darkgray: '\u000314', + gray: '\u000315', + grey: '\u000315', + white: '\u00030', + }; + + public color(color: string, msg: string): string { + // Approximate a hex color to one of IRC colors + if (color.indexOf('#') === 0) { + color = approximateB16Color(color); + } + + if (!this.supportColors) { + return msg; + } + + if (!this.colors[color]) { + return msg; + } + + return this.colors[color] + msg + this.colorEscape; + } + + public strip(msg: string): string { + return msg.replace(/(\x03\d{0,2}(,\d{0,2})?)/g, '').replace(/[\x0F\x02\x16\x1F]/g, ''); + } + + compose(objs: any): any { + const str = []; + + for (const i in objs) { + const elem = objs[i]; + + const elemType = elem[0]; + let elemValue = elem[1]; + const elemParams = elem[2]; + + if (!elemValue) { + continue; + } + + let valueColor = null; + + // Special types + if (elemParams && elemParams.type) { + switch (elemParams.type) { + case 'time': + elemValue = new Date(elemValue).toString(); + break; + case 'metric': + elemValue = thousandsSeparator(elemValue); + break; + case 'timesince': + elemValue = timeSince(elemValue); + break; + case 'duration': + elemValue = toHHMMSS(elemValue); + break; + case 'description': + valueColor = 'blue'; + elemValue = `"${elemValue}"`; + break; + } + } + + // Currently, disregard image fields. + if (elemType === 'image') { + continue; + } + + // Bold value + if (elemType === 'bold' || elemType === 'b' || elemType === 'strong') { + elemValue = this.format('bold', elemValue); + } + + // Italic value + if (elemType === 'italic' || elemType === 'i' || elemType === 'em' || elemType === 'emphasis') { + elemValue = this.format('italic', elemValue); + } + + // Underlined value + if (elemType === 'underline' || elemType === 'ul') { + elemValue = this.format('underline', elemValue); + } + + // Colorize the value + if (elemParams && elemParams.color) { + valueColor = elemParams.color; + } + + // Add the label, if present + if (elemParams && elemParams.label) { + // Set label color to default + let labelColor = 'green'; + if (elemParams.color) { + labelColor = elemParams.color; + } + + if (!valueColor) { + valueColor = 'blue'; + } + + // Handle array label + // Prefer the icon over the text version. + let label = elemParams.label; + if (typeof label === 'object') { + label = label[0]; + } else { + label = label + ':'; + } + + if (valueColor && valueColor === labelColor) { + str.push(this.color(valueColor, label + ' ' + elemValue)); + } else { + str.push(this.color(labelColor, label) + ' ' + (valueColor ? this.color(valueColor, elemValue) : elemValue)); + } + } else { + str.push(valueColor ? this.color(valueColor, elemValue) : elemValue); + } + } + + // May return an object, but your protocol must support it. + return str.join(' '); + } +} diff --git a/irc/irc.ts b/irc/irc.ts new file mode 100644 index 0000000..8c88161 --- /dev/null +++ b/irc/irc.ts @@ -0,0 +1,425 @@ +import util from 'util'; +import tls, { TLSSocket } from 'tls'; +import net, { Socket } from 'net'; +import { IIRCLine, parse } from './parser'; +import { EventEmitter } from 'events'; +const MAXMSGLEN = 512; + +export interface IIRCOptions { + nick: string; + host: string; + username?: string; + hostname?: string; + port?: number; + password?: string | null; + sasl?: boolean; + ssl?: boolean; + channels: string[]; + nickserv: {[key: string]: any}; +} + +export interface IIRCMessage { + message: string; + to: string; + nickname: string; + raw: IIRCLine; +} + +declare type ConnectSocket = TLSSocket | Socket; + +export class IRC extends EventEmitter { + public alive = false; + private authenticated = false; + private serverData: {[key: string]: any} = { + name: '', + supportedModes: {}, + serverSupports: {}, + }; + + private queue: any[] = []; + private channels: string[] = []; + private nickservStore: {[key: string]: any} = {}; + + private socket: ConnectSocket | null = null; + + constructor(public options: IIRCOptions) { + super(); + if (!this.options.username) { + this.options.username = this.options.nick; + } + } + + // Chop message into pieces recursively, splitting them at lenoffset + public static truncate(msg: string, lenoffset: number): string[] { + let pieces: string[] = []; + if (msg.length <= lenoffset) { + pieces.push(msg); + } else { + const m1 = msg.substring(0, lenoffset); + const m2 = msg.substring(lenoffset); + pieces.push(m1); + if (m2.length > lenoffset) { + pieces = pieces.concat(IRC.truncate(m2, lenoffset)); + } else { + pieces.push(m2); + } + } + return pieces; + } + + private authenticate(): void { + if (this.options.sasl) { + this.write('CAP REQ :sasl'); + } + + if (this.options.password && !this.options.sasl) { + this.write('PASS %s', this.options.password); + } + + this.write('USER %s 8 * :Squeebot 3.0 Core', this.options.username); + this.write('NICK %s', this.options.nick); + + this.on('authenticated', () => { + this.joinMissingChannels(this.options.channels); + }); + + this.on('testnick', (data) => { + if (this.nickservStore[data.nickname] != null) { + if (this.nickservStore[data.nickname].result === true) { + data.func(true); + return; + } else { + if (this.nickservStore[data.nickname].checked < Date.now() - 1800000) { // 30 minutes + delete this.nickservStore[data.nickname]; + } + } + } + + if (this.options.nickserv && this.options.nickserv.enabled && this.options.nickserv.command) { + this.queue.push({ + await: 'NOTICE', + from: 'NickServ', + do: (line: IIRCLine) => { + const splitline = line.trailing!.split(' '); + if (splitline![1] !== '0') { + this.nickservStore[data.nickname] = { + result: true, + checked: Date.now(), + }; + data.func(true); + } else { + this.nickservStore[data.nickname] = { + result: false, + checked: Date.now(), + }; + data.func(false); + } + } + }); + this.write('PRIVMSG nickserv :%s %s', this.options.nickserv.command, data.nickname); + } + }); + } + + public disconnect(): void { + if (!this.alive) { + return; + } + this.write('QUIT :%s', 'Squeebot 3.0 Core - IRC Service'); + this.alive = false; + } + + public write(...args: any[]): void { + const data = util.format.apply(null, [args[0], ...args.slice(1)]); + if (!this.alive) { + return; + } + this.socket!.write(data + '\r\n'); + } + + private joinMissingChannels(arr: string[]): void { + if (arr) { + for (const i in arr) { + let chan = arr[i]; + if (chan.indexOf('#') !== 0) { + chan = '#' + chan; + } + + if (this.channels.indexOf(chan) === -1) { + this.write('JOIN %s', chan); + } + } + } + } + + private handleServerLine(line: IIRCLine): void { + if (this.queue.length) { + let skipHandling = false; + for (const i in this.queue) { + const entry = this.queue[i]; + if (entry.await && line.command === entry.await) { + if (entry.from && line.user.nickname.toLowerCase() === entry.from.toLowerCase()) { + if (entry.do) { + skipHandling = true; + this.queue.splice(parseInt(i, 10), 1); + entry.do(line); + } + } + } + } + if (skipHandling) { + return; + } + } + + switch (line.command.toLowerCase()) { + case 'cap': + if (line.trailing === 'sasl' && line.arguments![1] === 'ACK' && !this.authenticated) { + this.write('AUTHENTICATE PLAIN'); + } + break; + case '+': + case ':+': + if (this.authenticated) { + return; + } + + const authline = Buffer.from(this.options.nick + '\x00' + this.options.username + '\x00' + this.options.password) + .toString('base64'); + this.write('AUTHENTICATE %s', authline); + break; + case '904': + this.emit('error', { + error: new Error(line.trailing), + fatal: true + }); + break; + case '903': + this.write('CAP END'); + break; + case 'notice': + case 'privmsg': + if (!line.user.nickname || line.user.nickname === '') { + return; + } + this.emit('message', { + message: line.trailing, + to: line.arguments![0], + nickname: line.user.nickname, + raw: line + }); + break; + case '001': + this.serverData.name = line.user.hostname; + this.authenticated = true; + + // Set nick to what the server actually thinks is our nick + this.options.nick = line.arguments![0]; + this.emit('authenticated', true); + + // Send a whois request for self in order to reliably fetch hostname of self + this.write('WHOIS %s', this.options.nick); + break; + case '005': + const argv = line.arguments!.slice(1); + for (const a in argv) { + let t: any = argv[a]; + if (t.indexOf('=') !== -1) { + t = t.split('='); + if (t[0] === 'PREFIX') { + const d = t[1].match(/\((\w+)\)(.*)/); + const r = d![1].split(''); + const aa = d![2].split(''); + for (const b in r) { + this.serverData.supportedModes[r[b]] = aa[b]; + } + } else if (t[0] === 'NETWORK') { + this.serverData.network = t[1]; + } else if (t[0] === 'CHANNELLEN') { + this.serverData.maxChannelLength = parseInt(t[1], 10); + } + + if (!isNaN(parseInt(t[1], 10))) { + t[1] = parseInt(t[1], 10); + } + this.serverData.serverSupports[t[0]] = t[1]; + } else { + this.serverData.serverSupports[t] = true; + } + } + break; + // Set hostname from 396 (non-standard) + case '396': + this.options.hostname = line.arguments![1]; + break; + // Set hostname from self-whois + case '311': + if (line.arguments![1] !== this.options.nick) { + return; + } + this.options.hostname = line.arguments![3]; + break; + case 'quit': + if (line.user.nickname !== this.options.nick) { + if (this.nickservStore[line.user.nickname]) { + delete this.nickservStore[line.user.nickname]; + } + + this.emit('leave', { + nickname: line.user.nickname + }); + } + break; + case 'nick': + if (line.user.nickname === this.options.nick) { + this.options.nick = line.arguments![0]; + } else if (this.nickservStore[line.user.nickname]) { + delete this.nickservStore[line.user.nickname]; + } + this.emit('nick', { + oldNick: line.user.nickname, + newNick: line.arguments![0] + }); + break; + case 'join': + if (line.user.nickname === this.options.nick && line.trailing) { + this.channels.push(line.trailing); + } + + this.emit('join', { + nickname: line.user.nickname, + channel: line.trailing + }); + break; + case 'part': + case 'kick': + if (line.user.nickname === this.options.nick) { + const indexAt = this.channels.indexOf(line.arguments![0]); + if (indexAt !== -1) { + this.channels.splice(indexAt, 1); + } + } + this.emit('leave', { + nickname: line.user.nickname, + channel: line.arguments![0] + }); + break; + case 'error': + this.emit('error', { fatal: true, error: new Error(line.raw) }); + break; + } + } + + // Send a message with the max bytelength of 512 in mind for trailing + public cmd(command: string, argv: string[], trailing: string): void { + const args = argv.join(' '); + let resolution: string[] = []; + + // Prevent newline messages from being sent as a command + const fs = trailing.split('\n'); + + // Predict the length the server is going to split at + // :nickname!username@hostname command args :trailing\r\n + const header = this.options.nick.length + + this.options.hostname!.length + + this.options.username!.length + 4 + 2; + const offset = command.length + args.length + 3 + header; + + // Split the message up into chunks + for (const i in fs) { + const msg = fs[i]; + if (msg.length > MAXMSGLEN - offset) { + resolution = resolution.concat(IRC.truncate(msg, MAXMSGLEN - offset)); + } else { + resolution.push(msg); + } + } + + for (const i in resolution) { + // Add delay to writes to prevent RecvQ overflow + setTimeout(() => { + this.write('%s %s :%s', command, args, resolution[i]); + }, 1000 * parseInt(i, 10)); + } + } + + public message(target: string, message: string): void { + this.cmd('PRIVMSG', [target], message); + } + + public notice(target: string, message: string): void { + this.cmd('NOTICE', [target], message); + } + + public connect(): void { + if (!this.options.host || !this.options.port) { + this.emit('error', { + error: new Error('No host or port specified!'), + fatal: true + }); + return; + } + + const opts = { + port: this.options.port, + host: this.options.host, + rejectUnauthorized: false + }; + + let connection: ConnectSocket; + const connfn = () => { + this.alive = true; + this.authenticate(); + }; + + // For some reason, tls.connect and net.connect are not + // compatible according to TypeScript.. + if (this.options.ssl) { + connection = tls.connect(opts, connfn); + } else { + connection = net.connect(opts, connfn); + } + + this.socket = connection; + + let buffer: any = ''; + this.socket!.on('data', (chunk) => { + buffer += chunk; + const data = buffer.split('\r\n'); + buffer = data.pop(); + + data.forEach((line: string) => { + if (line.indexOf('PING') === 0) { + this.socket!.write('PONG' + line.substring(4) + '\r\n'); + return; + } + + // Emit line as raw + this.emit('raw', line); + + // Parse the line + const parsed = parse(line); + + // Emit the parsed line + this.emit('line', parsed); + + // Handle the line + this.handleServerLine(parsed); + }); + }); + + this.socket.on('close', (data) => { + this.alive = false; + this.emit('disconnect', { type: 'sock_closed', raw: data, message: 'Connection closed.' }); + + this.authenticated = false; + }); + + this.socket.on('error', (data) => { + this.alive = false; + this.emit('error', { fatal: true, error: new Error(data) }); + + this.authenticated = false; + }); + } +} diff --git a/irc/parser.ts b/irc/parser.ts new file mode 100644 index 0000000..4c69bb9 --- /dev/null +++ b/irc/parser.ts @@ -0,0 +1,77 @@ + +// :nickname!username@hostname command arg ume nts :trailing +// or +// :hostname command arg ume nts :trailing + +export interface IIRCUser { + nickname: string; + username: string; + hostname: string; +} + +export interface IIRCLine { + user: IIRCUser; + command: string; + arguments?: string[]; + trailing?: string; + raw: string; +} + +function parseERROR(line: string[]): IIRCLine { + let final: IIRCLine = { + user: { nickname: '', username: '', hostname: '' }, + command: 'ERROR', + trailing: '', + raw: line.join(' ') + }; + + let pass1 = line.slice(1).join(' '); + if (pass1.indexOf(':') === 0) { + pass1 = pass1.substring(1); + } + + final.trailing = pass1; + + return final; +} + +export function parse(rawline: string): IIRCLine { + let final: IIRCLine = { + user: { + nickname: '', + username: '', + hostname: '' + }, + command: '', + arguments: [], + trailing: '', + raw: rawline + }; + + let pass1 = (rawline.indexOf(':') === 0 ? rawline.substring(1).split(' ') : rawline.split(' ')); + if (pass1[0] === 'ERROR') { + return parseERROR(pass1); + } + + if (pass1[0].indexOf('!') !== -1) { + let nickuser = pass1[0].split('!'); + final.user.nickname = nickuser[0]; + let userhost = nickuser[1].split('@'); + final.user.username = userhost[0]; + final.user.hostname = userhost[1]; + } else { + final.user.hostname = pass1[0]; + } + + final.command = pass1[1]; + + let pass2 = pass1.slice(2).join(' '); + if (pass2.indexOf(':') !== -1) { + final.arguments = pass2.substring(0, pass2.indexOf(' :')).split(' '); + final.trailing = pass2.substring(pass2.indexOf(':') + 1); + } else { + final.arguments = pass2.split(' '); + } + + return final +} diff --git a/irc/plugin.json b/irc/plugin.json new file mode 100644 index 0000000..920bf69 --- /dev/null +++ b/irc/plugin.json @@ -0,0 +1,9 @@ +{ + "main": "plugin.js", + "name": "irc", + "description": "IRC Service for Squeebot 3", + "tags": ["service", "irc"], + "version": "1.0.0", + "dependencies": [], + "npmDependencies": [] +} diff --git a/irc/plugin.ts b/irc/plugin.ts new file mode 100644 index 0000000..5590467 --- /dev/null +++ b/irc/plugin.ts @@ -0,0 +1,273 @@ +import util from 'util'; + +import { + Plugin, + EventListener, + Configurable, + InjectService, + Auto +} from '@squeebot/core/lib/plugin'; + +import { EMessageType, Formatter, IMessage, IMessageTarget, Protocol } from '@squeebot/core/lib/types'; + +import { logger } from '@squeebot/core/lib/core'; +import { IIRCMessage, IRC } from './irc'; + +import { IRCFormatter } from './format'; + +class IRCMessage implements IMessage { + public time: Date = new Date(); + public resolved = false; + public direct = false; + + constructor( + public type: EMessageType, + public data: any, + public source: Protocol, + public sender: IMessageTarget, + public target?: IMessageTarget, + public guest = true) {} + + public resolve(...args: any[]): void { + this.resolved = true; + this.source.resolve(this, ...args); + } +} + +class IRCProtocol extends Protocol { + public format: Formatter = new IRCFormatter(true, true); + public type = 'IRCProtocol'; + + private irc: IRC = new IRC(this.config.irc); + private eventsAttached = false; + + public start(...args: any[]): void { + this.runEvents(); + + this.irc.connect(); + + this.running = true; + this.emit('running'); + } + + public stop(force = false): void { + if (!this.running) { + return; + } + + if (this.irc.alive) { + this.irc.disconnect(); + } + + this.running = false; + this.stopped = true; + + if (force) { + this.failed = true; + } + + this.emit('stopped'); + } + + private validateNick(nickname: string, cb: Function): void { + if (!this.config.nickserv || !this.config.nickserv.enabled) { + return cb(true); // Assume the user is authentic + } + + let stop = false; + const promiseTimeout = setTimeout(() => { + stop = true; + cb(false); + }, 4000); + + this.irc.emit('testnick', { + nickname, + func: (result: boolean) => { + clearTimeout(promiseTimeout); + if (stop) { + return; + } + cb(result); + } + }); + } + + private runEvents(): void { + if (this.eventsAttached) { + return; + } + this.eventsAttached = true; + + this.irc.on('authenticated', () => { + logger.log('[%s] Instance started successfully.', this.fullName); + }); + + this.irc.on('error', (errdat) => { + if (errdat.fatal) { + this.stop(); + } + logger.error('[%s] Instance error:', this.fullName, errdat.error.message); + }); + + this.irc.on('disconnect', (data) => { + logger.warn('[%s] Instance disconnected:', this.fullName, data.message); + this.stop(); + }); + + // Pass events from IRC to the main channel, where it will then be routed + // to the list of handler plugins within a configured channel. + + this.irc.on('leave', (data) => { + const left = data.channel ? { id: data.channel, name: data.channel } : undefined; + const newMessage = new IRCMessage( + EMessageType.roomLeave, + {}, + this, + { id: data.nickname, name: data.nickname }, + left); + this.plugin.stream.emitTo('channel', 'event', newMessage); + }); + + this.irc.on('join', (data) => { + const newMessage = new IRCMessage( + EMessageType.roomJoin, + {}, + this, + { id: data.nickname, name: data.nickname }, + { id: data.channel, name: data.channel }); + this.plugin.stream.emitTo('channel', 'event', newMessage); + }); + + this.irc.on('nick', (data) => { + const newMessage = new IRCMessage( + EMessageType.nameChange, + data.oldNick, + this, + { id: data.newNick, name: data.newNick }); + this.plugin.stream.emitTo('channel', 'event', newMessage); + }); + + this.irc.on('message', (msg: IIRCMessage) => { + this.validateNick(msg.nickname, (valid: boolean) => { + const to = msg.to === this.irc.options.nick ? msg.nickname : msg.to; + const newMessage = new IRCMessage( + EMessageType.message, + msg.message, + this, + { id: msg.nickname, name: msg.nickname }, + { id: to, name: to }, + valid === false); + + if (msg.to === this.irc.options.nick) { + newMessage.direct = true; + } + + this.plugin.stream.emitTo('channel', 'message', newMessage); + }); + }); + } + + public resolve(message: IMessage, ...data: any[]): void { + let response = util.format(data[0], ...data.slice(1)); + if (!response) { + return; + } + + if (Array.isArray(data[0])) { + try { + response = this.format.compose(data[0]); + } catch (e) { + logger.error('[%s] Failed to compose message:', this.fullName, e.message); + return; + } + } + + if (!this.irc.alive) { + return; + } + + this.irc.message(message.target!.id, response); + } +} + +/* + TODO: Control system + Temporary documentation: + { + name: 'service-name', + restart: false, + irc: { + nick: 'Squeebot', + host: 'localhost', + port: 6667, + password: null, + sasl: false, + ssl: false, + channels: [], + nickserv: { + enabled: false, + command: 'STATUS' + } + } + } +*/ + +@InjectService(IRCProtocol) +@Configurable({ instances: [] }) +class IRCServicePlugin extends Plugin { + @Auto() + initialize(): void { + const protoList = this.validateConfiguration(); + this.startAll(protoList); + } + + private startAll(list: any[]): void { + for (const ins of list) { + const newProto = new IRCProtocol(this, ins); + logger.log('[%s] Starting IRC service "%s".', this.name, ins.name); + this.service?.use(newProto, true); + this.monitor(newProto); + } + } + + private monitor(proto: Protocol): void { + // TODO + } + + private validateConfiguration(): any[] { + if (!this.config.config.instances) { + throw new Error('Configuration incomplete!'); + } + + const instances = this.config.config.instances; + const runnables: any[] = []; + for (const ins of instances) { + if (!ins.name || !ins.irc) { + throw new Error('Invalid instance configuration!'); + } + + const irc = ins.irc; + if (!irc.nick || !irc.host) { + logger.warn('[%s] Instance named %s was skipped for invalid configuration.', + this.name, ins.name); + continue; + } + + runnables.push(ins); + } + + return runnables; + } + + @EventListener('pluginUnload') + unloadEventHandler(plugin: string | Plugin): void { + if (plugin === this.name || plugin === this) { + logger.debug('[%s]', this.name, 'shutting down..'); + this.config.save().then(() => + this.service!.stopAll().then(() => + this.emit('pluginUnloaded', this))); + } + } +} + +module.exports = IRCServicePlugin; diff --git a/ircprototest/plugin.json b/ircprototest/plugin.json new file mode 100644 index 0000000..5102af7 --- /dev/null +++ b/ircprototest/plugin.json @@ -0,0 +1,9 @@ +{ + "main": "plugin.js", + "name": "ircprototest", + "description": "IRC Service Test", + "tags": ["service", "irc", "test"], + "version": "1.0.0", + "dependencies": [], + "npmDependencies": [] +} diff --git a/ircprototest/plugin.ts b/ircprototest/plugin.ts new file mode 100644 index 0000000..346dbaf --- /dev/null +++ b/ircprototest/plugin.ts @@ -0,0 +1,35 @@ +import { + Plugin, + EventListener, + Configurable, + InjectService, + Auto +} from '@squeebot/core/lib/plugin'; + +import { EMessageType, IMessage, IMessageTarget, Protocol } from '@squeebot/core/lib/types'; + +import { logger } from '@squeebot/core/lib/core'; + +class MyPlugin extends Plugin { + @Auto() + initialize(): void { + } + + @EventListener('message') + messageHandler(msg: IMessage): void { + if (msg.data.indexOf('Squeebot') !== -1) { + msg.resolve('Hello %s!', msg.sender!.name); + } + } + + @EventListener('pluginUnload') + unloadEventHandler(plugin: string | Plugin): void { + if (plugin === this.name || plugin === this) { + logger.debug('[%s]', this.name, 'shutting down..'); + this.config.save().then(() => + this.emit('pluginUnloaded', this)); + } + } +} + +module.exports = MyPlugin; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..02ed9d3 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,460 @@ +{ + "name": "service-irc", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@squeebot/core": { + "version": "file:../core", + "requires": { + "dateformat": "^4.0.0", + "fs-extra": "^9.0.1", + "semver": "^7.3.2", + "tar": "^6.0.5" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@types/dateformat": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/dateformat/-/dateformat-3.0.1.tgz", + "integrity": "sha512-KlPPdikagvL6ELjWsljbyDIPzNCeliYkqRpI+zea99vBBbCIA5JNshZAwQKTON139c87y9qvTFVgkFd14rtS4g==" + }, + "@types/fs-extra": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.4.tgz", + "integrity": "sha512-50GO5ez44lxK5MDH90DYHFFfqxH7+fTqEEnvguQRzJ/tY9qFrMSHLiYHite+F3SNmf7+LHC1eMXojuD+E3Qcyg==", + "requires": { + "@types/node": "*" + } + }, + "@types/minipass": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/minipass/-/minipass-2.2.0.tgz", + "integrity": "sha512-wuzZksN4w4kyfoOv/dlpov4NOunwutLA/q7uc00xU02ZyUY+aoM5PWIXEKBMnm0NHd4a+N71BMjq+x7+2Af1fg==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "14.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.9.tgz", + "integrity": "sha512-JsoLXFppG62tWTklIoO4knA+oDTYsmqWxHRvd4lpmfQRNhX6osheUOWETP2jMoV/2bEHuMra8Pp3Dmo/stBFcw==" + }, + "@types/semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ==" + }, + "@types/tar": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/tar/-/tar-4.0.4.tgz", + "integrity": "sha512-0Xv+xcmkTsOZdIF4yCnd7RkOOyfyqPaqJ7RZFKnwdxfDbkN3eAAE9sHl8zJFqBz4VhxolW9EErbjR1oyH7jK2A==", + "requires": { + "@types/minipass": "*", + "@types/node": "*" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "dateformat": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.0.0.tgz", + "integrity": "sha512-zpKyDYpeePyYGJp2HhRxLHlA+jZQNjt+MwmcVmLxCIECeC4Ks3TI3yk/CSMKylbnCJ5htonfOugYtRRTpyoHow==" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-core-module": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", + "integrity": "sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==", + "requires": { + "has": "^1.0.3" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + } + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "tar": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz", + "integrity": "sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + } + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tslint": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "requires": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.3", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.13.0", + "tsutils": "^2.29.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "requires": { + "tslib": "^1.8.1" + } + }, + "typescript": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz", + "integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==" + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "typescript": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz", + "integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..e7fb5e7 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "service-irc", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "tsc", + "watch": "tsc -w" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@squeebot/core": "file:../core", + "typescript": "^4.1.2" + } +} diff --git a/squeebot.repo.json b/squeebot.repo.json new file mode 100644 index 0000000..c7eacc2 --- /dev/null +++ b/squeebot.repo.json @@ -0,0 +1,14 @@ +{ + "name": "service-irc", + "plugins": [ + { + "name": "irc", + "version": "1.0.0" + }, + { + "name": "ircprototest", + "version": "1.0.0" + } + ], + "typescript": true +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..f460d0c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1 @@ +{"compilerOptions":{"downlevelIteration":true,"esModuleInterop":true,"experimentalDecorators":true,"forceConsistentCasingInFileNames":true,"skipLibCheck":true,"sourceMap":false,"strict":true,"target":"es5"}} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..a7b6db5 --- /dev/null +++ b/tslint.json @@ -0,0 +1,153 @@ +{ + "extends": "tslint:recommended", + "rules": { + "align": { + "options": [ + "parameters", + "statements" + ] + }, + "array-type": false, + "arrow-return-shorthand": true, + "curly": true, + "deprecation": { + "severity": "warning" + }, + "eofline": true, + "import-blacklist": [ + true, + "rxjs/Rx" + ], + "import-spacing": true, + "indent": { + "options": [ + "spaces" + ] + }, + "max-classes-per-file": false, + "max-line-length": [ + true, + 140 + ], + "member-ordering": [ + true, + { + "order": [ + "static-field", + "instance-field", + "static-method", + "instance-method" + ] + } + ], + "no-console": [ + true, + "debug", + "info", + "time", + "timeEnd", + "trace" + ], + "no-empty": false, + "no-inferrable-types": [ + true, + "ignore-params" + ], + "no-non-null-assertion": false, + "no-redundant-jsdoc": true, + "no-switch-case-fall-through": true, + "no-var-requires": false, + "object-literal-key-quotes": [ + true, + "as-needed" + ], + "quotemark": [ + true, + "single" + ], + "semicolon": { + "options": [ + "always" + ] + }, + "space-before-function-paren": { + "options": { + "anonymous": "never", + "asyncArrow": "always", + "constructor": "never", + "method": "never", + "named": "never" + } + }, + "typedef": [ + true, + "call-signature" + ], + "forin": false, + "ban-types": { + "function": false + }, + "typedef-whitespace": { + "options": [ + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + }, + { + "call-signature": "onespace", + "index-signature": "onespace", + "parameter": "onespace", + "property-declaration": "onespace", + "variable-declaration": "onespace" + } + ] + }, + "variable-name": { + "options": [ + "ban-keywords", + "check-format", + "allow-pascal-case" + ] + }, + "whitespace": { + "options": [ + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type", + "check-typecast" + ] + }, + "component-class-suffix": true, + "contextual-lifecycle": true, + "directive-class-suffix": true, + "no-conflicting-lifecycle": true, + "no-host-metadata-property": true, + "no-input-rename": true, + "no-inputs-metadata-property": true, + "no-output-native": true, + "no-output-on-prefix": true, + "no-output-rename": true, + "no-outputs-metadata-property": true, + "template-banana-in-box": true, + "template-no-negated-async": true, + "use-lifecycle-interface": true, + "use-pipe-transform-interface": true, + "directive-selector": [ + true, + "attribute", + "app", + "camelCase" + ], + "component-selector": [ + true, + "element", + "app", + "kebab-case" + ] + } +}