UNPKG

squelch-client

Version:
494 lines (456 loc) 16.9 kB
// Generated by CoffeeScript 1.10.0 (function() { var Client, Emitter, Promise, color, debug, debugError, defaultOpt, getReplyCode, getReplyName, getSender, ircMsg, net, path, ref, streamMap, tls, bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, hasProp = {}.hasOwnProperty, slice = [].slice; net = require('net'); tls = require('tls'); path = require('path'); Emitter = require('@rahatarmanahmed/event-kit').Emitter; ircMsg = require('irc-message'); Promise = require('bluebird'); streamMap = require('through2-map'); color = require('irc-colors'); debug = require('debug')('squelch-client'); debugError = require('debug')('squelch-client:error'); getSender = require('./util').getSender; ref = require('./replies'), getReplyCode = ref.getReplyCode, getReplyName = ref.getReplyName; defaultOpt = { port: 6667, nick: 'NodeIRCClient', username: 'NodeIRCClient', realname: 'NodeIRCClient', channels: [], autoNickChange: true, autoRejoin: false, autoConnect: true, autoSplitMessage: true, messageDelay: 1000, stripColors: true, stripStyles: true, autoReconnect: true, autoReconnectTries: 3, reconnectDelay: 5000, ssl: false, selfSigned: false, certificateExpired: false, timeout: 120000, triggerEventsForOwnMessages: false }; Client = (function(superClass) { extend(Client, superClass); function Client(opt) { this.dequeue = bind(this.dequeue, this); var key, value; Client.__super__.constructor.call(this); this._ = { internalEmitter: new Emitter(), numRetries: 0, connected: false, connecting: false, disconnecting: false, messageQueue: [], iSupport: { CHANTYPES: '&#' }, greeting: {}, prefix: { o: '@', v: '+' }, chanmodes: ['beI', 'k', 'l', 'aimnpqsrt'] }; if (opt == null) { throw new Error('No options argument given.'); } if (typeof opt === 'string') { opt = require(path.resolve(opt)); } this.opt = {}; for (key in defaultOpt) { value = defaultOpt[key]; this.opt[key] = value; } for (key in opt) { value = opt[key]; this.opt[key] = value; } if (this.opt.server == null) { throw new Error('No server specified.'); } this.use(require('./plugins/core/msg')()); this.use(require('./plugins/core/notice')()); this.use(require('./plugins/core/nick')()); this.use(require('./plugins/core/join')()); this.use(require('./plugins/core/part')()); this.use(require('./plugins/core/kick')()); this.use(require('./plugins/core/invite')()); this.use(require('./plugins/core/mode')()); this.use(require('./plugins/core/motd')()); this.use(require('./plugins/channel')()); if (this.opt.autoConnect) { this.connect(); } } Client.prototype.emit = function() { var args, ref1; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; if (args[0] !== 'error') { (ref1 = this._.internalEmitter).emit.apply(ref1, args); } return Client.__super__.emit.apply(this, args); }; Client.prototype.cbNoop = function(err) { if (err) { return debugError(err); } }; Client.prototype.connect = function(tries, cb) { if (tries == null) { tries = 1; } return new Promise((function(_this) { return function(resolve, reject) { var errorListener, onConnect, tlsOptions; _this._.connecting = true; _this.emit('connecting', { port: _this.opt.port, server: _this.opt.server }); debug('Connecting...'); if (tries instanceof Function) { cb = tries; tries = 1; } tries--; errorListener = function(err) { _this._.connecting = false; debugError('Unable to connect.'); debugError(err); if (tries > 0 || tries === -1) { _this.emit('reconnecting', { port: _this.opt.port, server: _this.opt.server, delay: _this.opt.reconnectDelay, triesLeft: tries }); debugError("Reconnecting in " + (_this.opt.reconnectDelay / 1000) + " seconds... (" + tries + " remaining tries)"); return setTimeout(function() { return _this.connect(tries, cb); }, _this.opt.reconnectDelay); } else { return reject(err); } }; onConnect = function() { var stream; _this.conn.setEncoding('utf8'); _this.conn.removeListener('error', errorListener); _this._.internalEmitter.once('connect', function(arg) { var nick; nick = arg.nick; return resolve({ nick: nick }); }); debug('Connected'); stream = _this.conn; if (_this.opt.stripColors) { stream = stream.pipe(streamMap({ wantStrings: true }, color.stripColors)); } if (_this.opt.stripStyles) { stream = stream.pipe(streamMap({ wantStrings: true }, color.stripStyle)); } stream = stream.pipe(ircMsg.createStream({ parsePrefix: true })); stream.on('data', function(data) { if (_this._.timeout) { clearTimeout(_this._.timeout); } _this._.timeout = setTimeout(function() { var pingTime; _this.raw('PING :ruthere'); pingTime = new Date().getTime(); return _this._.timeout = setTimeout(function() { var seconds; seconds = (new Date().getTime() - pingTime) / 1000; _this.emit('timeout', { seconds: seconds }); return _this.handleReply(ircMsg.parse("ERROR :Ping Timeout (" + seconds + " seconds)")); }, _this.opt.timeout); }, _this.opt.timeout); if (data != null) { _this.handleReply(data); return _this.emit('raw', data); } }); _this.conn.on('error', function(e) { debugError('Disconnected by network error.'); _this.handleReply(ircMsg.parse("ERROR :Connection error (" + e.message + ")")); if (_this.opt.autoReconnect && _this.opt.autoReconnectTries > 0) { debug("Reconnecting in " + (_this.opt.reconnectDelay / 1000) + " seconds... (" + _this.opt.autoReconnectTries + " remaining tries)"); return setTimeout(function() { return _this.connect(_this.opt.autoReconnectTries); }, _this.opt.reconnectDelay); } }); _this.emit('connection-established', { port: _this.opt.port, server: _this.opt.server }); if (_this.opt.password != null) { _this.raw("PASS " + _this.opt.password, false); } _this.raw("NICK " + _this.opt.nick, false); return _this.raw("USER " + _this.opt.username + " 8 * :" + _this.opt.realname, false); }; if (_this.opt.ssl) { tlsOptions = _this.opt.ssl instanceof Object ? _this.opt.ssl : {}; if (_this.opt.selfSigned) { tlsOptions.rejectUnauthorized = false; } _this.conn = tls.connect(_this.opt.port, _this.opt.server, tlsOptions, function() { if (!_this.conn.authorized) { if (_this.opt.selfSigned && (_this.conn.authorizationError === 'DEPTH_ZERO_SELF_SIGNED_CERT' || _this.conn.authorizationError === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' || _this.conn.authorizationError === 'SELF_SIGNED_CERT_IN_CHAIN')) { debug('Connecting to server with self signed certificate'); } else if (_this.opt.certificateExpired && _this.conn.authorizationError === 'CERT_HAS_EXPIRED') { debug('Connecting to server with expired certificate'); } else { debug("Authorization error: " + _this.conn.authorizationError); return; } } return onConnect(); }); } else { _this.conn = net.connect(_this.opt.port, _this.opt.server, onConnect); } return _this.conn.once('error', errorListener); }; })(this)).nodeify(cb || this.cbNoop); }; Client.prototype.disconnect = function(reason, cb) { return new Promise((function(_this) { return function(resolve) { if (reason instanceof Function) { cb = reason; reason = void 0; } _this._.disconnecting = true; if (reason != null) { _this.raw("QUIT :" + reason, false); } else { _this.raw('QUIT', false); } return _this._.internalEmitter.once('disconnect', function() { return resolve(); }); }; })(this)).nodeify(cb || this.cbNoop); }; Client.prototype.forceQuit = function(reason) { if (this.isConnected()) { this.raw('QUIT' + (reason != null ? " :" + reason : ''), false); } this._.disconnecting = true; return this.handleReply(ircMsg.parse('ERROR :Force Quit' + (reason ? " (" + reason + ")" : ''))); }; Client.prototype.raw = function(msg, delay) { if (delay == null) { delay = true; } if (msg == null) { throw new Error(); } if (this.conn == null) { return; } if (!delay || this.opt.messageDelay === 0) { debug("-> " + msg); return this.conn.write(msg + '\r\n'); } else { if (this._.messageQueue.length === 0) { setTimeout(this.dequeue, 0); } return this._.messageQueue.push(msg); } }; Client.prototype.dequeue = function() { var msg; msg = this._.messageQueue.shift(); if (this.conn != null) { debug("-> " + msg); this.conn.write(msg + '\r\n'); } if (this._.messageQueue.length !== 0) { return this._.messageQueueTimeout = setTimeout(this.dequeue, this.opt.messageDelay); } }; Client.prototype.splitText = function(command, msg, extra) { var i, limit; if (extra == null) { extra = 0; } limit = 512 - 3 - this._.nick.length - 9 - 65 - command.length - 2 - 2 - extra; return (function() { var j, ref1, ref2, results; results = []; for (i = j = 0, ref1 = msg.length, ref2 = limit; ref2 > 0 ? j <= ref1 : j >= ref1; i = j += ref2) { results.push(msg.slice(i, i + limit)); } return results; })(); }; Client.prototype.use = function(plugin) { plugin(this); return this; }; Client.prototype.isConnected = function() { return this._.connected; }; Client.prototype.isConnecting = function() { return this._.connecting; }; Client.prototype.messageDelay = function(value) { if (value == null) { return this.opt.messageDelay; } return this.opt.messageDelay = value; }; Client.prototype.autoSplitMessage = function(enabled) { if (enabled == null) { return this.opt.autoSplitMessage; } return this.opt.autoSplitMessage = enabled; }; Client.prototype.autoRejoin = function(enabled) { if (enabled == null) { return this.opt.autoRejoin; } return this.opt.autoRejoin = enabled; }; Client.prototype.triggerEventsForOwnMessages = function(enabled) { if (enabled == null) { return this.opt.triggerEventsForOwnMessages; } return this.opt.triggerEventsForOwnMessages = enabled; }; Client.prototype.isChannel = function(chan) { if (chan == null) { return false; } return this._.iSupport['CHANTYPES'].indexOf(chan[0]) !== -1; }; Client.prototype.modeToPrefix = function(mode) { return this._.prefix[mode]; }; Client.prototype.handleReply = function(parsedReply) { var i, item, j, k, len, match, nick, reason, ref1, ref2, results, split; if (parsedReply == null) { return; } debug('<-' + parsedReply.raw); switch (parsedReply.command) { case 'QUIT': nick = getSender(parsedReply); reason = parsedReply.params[0]; return this.emit('quit', { nick: nick, reason: reason }); case 'PING': return this.raw("PONG :" + parsedReply.params[0], false); case 'ERROR': this.conn.destroy(); this._.channels = {}; this._.messageQueue = []; clearTimeout(this._.messageQueueTimeout); clearTimeout(this._.timeout); this.conn = null; this._.connecting = false; this._.connected = false; if (!this._.disconnecting) { this.emit('error', parsedReply); } debug('Disconnected from server'); if (!this._.disconnecting && this.opt.autoReconnect && this.opt.autoReconnectTries > 0) { debug("Reconnecting in " + (this.opt.reconnectDelay / 1000) + " seconds... (" + this.opt.autoReconnectTries + " remaining tries)"); setTimeout((function(_this) { return function() { return _this.connect(_this.opt.autoReconnectTries); }; })(this), this.opt.reconnectDelay); } this._.disconnecting = false; return this.emit('disconnect', { reason: parsedReply.params[0] }); case getReplyCode('RPL_WELCOME'): this._.connecting = false; this._.connected = true; this._.nick = parsedReply.params[0]; this.emit('connect', { nick: this._.nick, server: this.opt.server, port: this.opt.port }); return this.join(this.opt.channels); case getReplyCode('RPL_YOURHOST'): return this._.greeting.yourHost = parsedReply.params[1]; case getReplyCode('RPL_CREATED'): return this._.greeting.created = parsedReply.params[1]; case getReplyCode('RPL_MYINFO'): return this._.greeting.myInfo = parsedReply.params.slice(1).join(' '); case getReplyCode('RPL_ISUPPORT'): ref1 = parsedReply.params.slice(1); results = []; for (j = 0, len = ref1.length; j < len; j++) { item = ref1[j]; if (item.indexOf(' ') !== -1) { continue; } split = item.split('='); if (split.length === 1) { this._.iSupport[item] = true; } else { this._.iSupport[split[0]] = split[1]; } switch (split[0]) { case 'PREFIX': match = /\((.+)\)(.+)/.exec(split[1]); this._.prefix = {}; for (i = k = 0, ref2 = match[1].length; 0 <= ref2 ? k < ref2 : k > ref2; i = 0 <= ref2 ? ++k : --k) { this._.prefix[match[1][i]] = match[2][i]; } this._.reversePrefix = {}; results.push((function() { var l, ref3, results1; results1 = []; for (i = l = 0, ref3 = match[1].length; 0 <= ref3 ? l < ref3 : l > ref3; i = 0 <= ref3 ? ++l : --l) { results1.push(this._.reversePrefix[match[2][i]] = match[1][i]); } return results1; }).call(this)); break; case 'CHANMODES': results.push(this._.chanmodes = split[1].split(',')); break; default: results.push(void 0); } } return results; } }; return Client; })(Emitter); module.exports = Client; }).call(this);