irc-framework
Version:
A better IRC framework for node.js
966 lines (933 loc) • 34.6 kB
JavaScript
;
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
require("core-js/modules/es.symbol.js");
require("core-js/modules/es.symbol.description.js");
require("core-js/modules/es.symbol.iterator.js");
require("core-js/modules/es.symbol.to-primitive.js");
require("core-js/modules/es.array.from.js");
require("core-js/modules/es.array.iterator.js");
require("core-js/modules/es.date.to-primitive.js");
require("core-js/modules/es.function.name.js");
require("core-js/modules/es.number.constructor.js");
require("core-js/modules/es.object.create.js");
require("core-js/modules/es.object.get-prototype-of.js");
require("core-js/modules/es.reflect.construct.js");
require("core-js/modules/es.string.iterator.js");
require("core-js/modules/web.dom-collections.iterator.js");
require("core-js/modules/es.array.concat.js");
require("core-js/modules/es.array.filter.js");
require("core-js/modules/es.array.find.js");
require("core-js/modules/es.array.for-each.js");
require("core-js/modules/es.array.index-of.js");
require("core-js/modules/es.array.is-array.js");
require("core-js/modules/es.array.join.js");
require("core-js/modules/es.array.slice.js");
require("core-js/modules/es.array.splice.js");
require("core-js/modules/es.date.now.js");
require("core-js/modules/es.date.to-string.js");
require("core-js/modules/es.function.bind.js");
require("core-js/modules/es.object.define-property.js");
require("core-js/modules/es.object.keys.js");
require("core-js/modules/es.object.set-prototype-of.js");
require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.regexp.exec.js");
require("core-js/modules/es.regexp.to-string.js");
require("core-js/modules/es.string.match.js");
require("core-js/modules/es.string.split.js");
require("core-js/modules/es.string.trim.js");
require("core-js/modules/web.dom-collections.for-each.js");
function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); }
function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); }
function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); }
function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
var _ = {
extend: require('lodash/extend'),
find: require('lodash/find'),
each: require('lodash/each'),
defer: require('lodash/defer'),
bind: require('lodash/bind')
};
var EventEmitter = require('eventemitter3');
var MiddlewareHandler = require('middleware-handler');
var IrcCommandHandler = require('./commands/').CommandHandler;
var IrcMessage = require('./ircmessage');
var Connection = require('./connection');
var NetworkInfo = require('./networkinfo');
var User = require('./user');
var Channel = require('./channel');
var _require = require('./linebreak'),
lineBreak = _require.lineBreak;
var MessageTags = require('./messagetags');
var default_transport = null;
module.exports = /*#__PURE__*/function (_EventEmitter) {
function IrcClient(options) {
var _this;
_classCallCheck(this, IrcClient);
_this = _callSuper(this, IrcClient);
_this.request_extra_caps = [];
_this.options = options || null;
_this.createStructure();
return _this;
}
_inherits(IrcClient, _EventEmitter);
return _createClass(IrcClient, [{
key: "Message",
get: function get() {
return IrcMessage;
}
}, {
key: "_applyDefaultOptions",
value: function _applyDefaultOptions(user_options) {
var defaults = {
nick: 'ircbot',
username: 'ircbot',
gecos: 'ircbot',
encoding: 'utf8',
version: 'node.js irc-framework',
enable_chghost: false,
enable_setname: false,
enable_echomessage: false,
auto_reconnect: true,
auto_reconnect_max_wait: 300000,
auto_reconnect_max_retries: 3,
ping_interval: 30,
ping_timeout: 120,
message_max_length: 350,
sasl_disconnect_on_fail: false,
transport: default_transport,
websocket_protocol: 'text.ircv3.net'
};
var props = Object.keys(defaults);
for (var i = 0; i < props.length; i++) {
if (typeof user_options[props[i]] === 'undefined') {
user_options[props[i]] = defaults[props[i]];
}
}
return user_options;
}
}, {
key: "createStructure",
value: function createStructure() {
var client = this;
// Provides middleware hooks for either raw IRC commands or the easier to use parsed commands
client.raw_middleware = new MiddlewareHandler();
client.parsed_middleware = new MiddlewareHandler();
client.connection = new Connection(client.options);
client.network = new NetworkInfo();
client.user = new User();
client.command_handler = new IrcCommandHandler(client);
client.addCommandHandlerListeners();
// Proxy some connection events onto this client
['connecting', 'reconnecting', 'close', 'socket close', 'socket error', 'raw socket connected', 'debug', 'raw'].forEach(function (event_name) {
client.connection.on(event_name, function () {
var args = Array.prototype.slice.call(arguments);
client.emit.apply(client, [event_name].concat(args));
});
});
client.connection.on('socket connected', function () {
client.emit('socket connected');
client.registerToNetwork();
client.startPingTimeoutTimer();
});
client.connection.on('connecting', function () {
// Reset cap negotiation on a new connection
// This prevents stale state if a connection gets closed during CAP negotiation
client.network.cap.negotiating = false;
client.network.cap.requested = [];
client.network.cap.enabled = [];
client.network.cap.available.clear();
client.command_handler.resetCache();
});
// IRC command routing
client.connection.on('message', function (message, raw_line) {
client.raw_middleware.handle([message.command, message, raw_line, client], function (err) {
if (err) {
console.log(err.stack);
return;
}
client.command_handler.dispatch(message);
});
});
client.on('registered', function (event) {
// PING is not a valid command until after registration
client.startPeriodicPing();
});
client.on('away', function (event) {
if (client.caseCompare(event.nick, client.user.nick)) {
client.user.away = true;
}
});
client.on('back', function (event) {
if (client.caseCompare(event.nick, client.user.nick)) {
client.user.away = false;
}
});
// Proxy the command handler events onto the client object, with some added sugar
client.proxyIrcEvents();
var whox_token = {
value: 0,
requests: [],
next: function next() {
if (whox_token.value >= 999) {
// whox token is limited to 3 characters
whox_token.value = 0;
}
var token = ++whox_token.value;
whox_token.requests.push(token);
return token;
},
validate: function validate(token) {
var idx = whox_token.requests.indexOf(token);
if (idx !== -1) {
whox_token.requests.splice(idx, 1);
return true;
}
return false;
}
};
client.whox_token = whox_token;
Object.defineProperty(client, 'connected', {
enumerable: true,
get: function get() {
return client.connection && client.connection.connected;
}
});
}
}, {
key: "requestCap",
value: function requestCap(cap) {
this.request_extra_caps = this.request_extra_caps.concat(cap);
}
}, {
key: "use",
value: function use(middleware_fn) {
middleware_fn(this, this.raw_middleware, this.parsed_middleware);
return this;
}
}, {
key: "connect",
value: function connect(options) {
var client = this;
// Use the previous options object if we're calling .connect() again
if (!options && !client.options) {
throw new Error('Options object missing from IrcClient.connect()');
} else if (!options) {
options = client.options;
} else {
client.options = options;
}
client._applyDefaultOptions(options);
if (client.connection && client.connection.connected) {
client.debugOut('connect() called when already connected');
client.connection.end();
}
client.user.nick = options.nick;
client.user.username = options.username;
client.user.gecos = options.gecos;
client.command_handler.requestExtraCaps(client.request_extra_caps);
// Everything is setup and prepared, start connecting
client.connection.connect(options);
}
// Proxy the command handler events onto the client object, with some added sugar
// Events are handled in order:
// 1. Received from the command handler
// 2. Checked if any extra properties/methods are to be added to the event + re-emitted
// 3. Routed through middleware
// 4. Emitted from the client instance
}, {
key: "proxyIrcEvents",
value: function proxyIrcEvents() {
var client = this;
this.command_handler.on('all', function (event_name, event_arg) {
client.resetPingTimeoutTimer();
// Add a reply() function to selected message events
if (['privmsg', 'notice', 'action'].indexOf(event_name) > -1) {
event_arg.reply = function (message) {
var dest = event_arg.target === client.user.nick ? event_arg.nick : event_arg.target;
client.say(dest, message);
};
// These events with .reply() function are all messages. Emit it separately
// TODO: Should this consider a notice a message?
client.command_handler.emit('message', _.extend({
type: event_name
}, event_arg));
}
client.parsed_middleware.handle([event_name, event_arg, client], function (err) {
if (err) {
console.error(err.stack);
return;
}
client.emit(event_name, event_arg);
});
});
}
}, {
key: "addCommandHandlerListeners",
value: function addCommandHandlerListeners() {
var client = this;
var commands = this.command_handler;
commands.on('nick', function (event) {
if (client.user.nick === event.nick) {
// nicks starting with numbers are reserved for uuids
// we dont want to store these as they cannot be used
if (event.new_nick.match(/^\d/)) {
return;
}
client.user.nick = event.new_nick;
}
});
commands.on('mode', function (event) {
if (client.user.nick === event.target) {
event.modes.forEach(function (mode) {
client.user.toggleModes(mode.mode);
});
}
});
commands.on('wholist', function (event) {
var thisUser = _.find(event.users, {
nick: client.user.nick
});
if (thisUser) {
client.user.username = thisUser.ident;
client.user.host = thisUser.hostname;
}
});
commands.on('registered', function (event) {
client.user.nick = event.nick;
client.connection.registeredSuccessfully();
client.emit('connected', event);
});
commands.on('displayed host', function (event) {
if (client.user.nick === event.nick) {
client.user.host = event.hostname;
}
});
// Don't let IRC ERROR command kill the node.js process if unhandled
commands.on('error', function (event) {});
}
}, {
key: "registerToNetwork",
value: function registerToNetwork() {
var webirc = this.options.webirc;
if (webirc) {
var address = String(webirc.address);
// Prepend a zero to addresses that begin with colon (like ::1)
// as colon is using to denote last argument in IRC
if (address[0] === ':') {
address = '0' + address;
}
this.raw('WEBIRC', webirc.password, webirc.username, webirc.hostname, address, MessageTags.encode(webirc.options || {}, ' '));
}
this.raw('CAP LS 302');
if (this.options.password) {
this.raw('PASS', this.options.password);
}
this.raw('NICK', this.user.nick);
this.raw('USER', this.options.username, 0, '*', this.user.gecos);
}
}, {
key: "startPeriodicPing",
value: function startPeriodicPing() {
var client = this;
var ping_timer = null;
if (client.options.ping_interval <= 0) {
return;
}
// Constantly ping the server for lag and time syncing functions
function pingServer() {
client.ping();
}
function resetPingTimer() {
client.connection.clearTimeout(ping_timer);
ping_timer = client.connection.setTimeout(pingServer, client.options.ping_interval * 1000);
}
// Browsers have started throttling looped timeout callbacks
// using the pong event to set the next ping breaks this loop
client.command_handler.on('pong', resetPingTimer);
// Socket has disconnected, remove 'pong' listener until next 'registered' event
client.connection.once('socket close', function () {
client.command_handler.off('pong', resetPingTimer);
});
// Start timer
resetPingTimer();
}
}, {
key: "startPingTimeoutTimer",
value: function startPingTimeoutTimer() {
var client = this;
var timeout_timer = null;
if (client.options.ping_timeout <= 0) {
return;
}
// Data from the server was detected so restart the timeout
function resetPingTimeoutTimer() {
client.connection.clearTimeout(timeout_timer);
timeout_timer = client.connection.setTimeout(pingTimeout, client.options.ping_timeout * 1000);
}
function pingTimeout() {
client.debugOut('Ping timeout (' + client.options.ping_timeout + ' seconds)');
client.emit('ping timeout');
var end_msg = client.rawString('QUIT', 'Ping timeout (' + client.options.ping_timeout + ' seconds)');
client.connection.end(end_msg, true);
}
this.resetPingTimeoutTimer = resetPingTimeoutTimer;
this.resetPingTimeoutTimer();
}
// Gets overridden with a function in startPeriodicPing(). Only set here for completeness.
}, {
key: "resetPingTimeoutTimer",
value: function resetPingTimeoutTimer() {}
}, {
key: "debugOut",
value: function debugOut(out) {
this.emit('debug', 'Client ' + out);
}
/**
* Client API
*/
}, {
key: "raw",
value: function raw(input) {
if (input instanceof IrcMessage) {
this.connection.write(input.to1459());
} else {
this.connection.write(this.rawString.apply(this, arguments));
}
}
}, {
key: "rawString",
value: function rawString(input) {
var args;
if (input.constructor === Array) {
args = input;
} else {
args = Array.prototype.slice.call(arguments, 0);
}
args = args.filter(function (item) {
return typeof item === 'number' || typeof item === 'string';
});
if (args.length > 1 && args[args.length - 1].match(/^:|\s/)) {
args[args.length - 1] = ':' + args[args.length - 1];
}
return args.join(' ');
}
}, {
key: "quit",
value: function quit(message) {
this.connection.end(this.rawString('QUIT', message));
}
}, {
key: "ping",
value: function ping(message) {
this.raw('PING', message || Date.now().toString());
}
}, {
key: "changeNick",
value: function changeNick(nick) {
this.raw('NICK', nick);
}
}, {
key: "sendMessage",
value: function sendMessage(commandName, target, message, tags) {
var _this2 = this;
var lines = message.split(/\r\n|\n|\r/).filter(function (i) {
return i;
});
lines.forEach(function (line) {
// Maximum length of target + message we can send to the IRC server is 500 characters
// but we need to leave extra room for the sender prefix so the entire message can
// be sent from the IRCd to the target without being truncated.
var blocks = _toConsumableArray(lineBreak(line, {
bytes: _this2.options.message_max_length,
allowBreakingWords: true,
allowBreakingGraphemes: true
}));
blocks.forEach(function (block) {
if (tags && Object.keys(tags).length) {
var msg = new IrcMessage(commandName, target, block);
msg.tags = tags;
_this2.raw(msg);
} else {
_this2.raw(commandName, target, block);
}
});
});
}
}, {
key: "say",
value: function say(target, message, tags) {
return this.sendMessage('PRIVMSG', target, message, tags);
}
}, {
key: "notice",
value: function notice(target, message, tags) {
return this.sendMessage('NOTICE', target, message, tags);
}
}, {
key: "tagmsg",
value: function tagmsg(target) {
var tags = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var msg = new IrcMessage('TAGMSG', target);
msg.tags = tags;
this.raw(msg);
}
}, {
key: "join",
value: function join(channel, key) {
var raw = ['JOIN', channel];
if (key) {
raw.push(key);
}
this.raw(raw);
}
}, {
key: "part",
value: function part(channel, message) {
var raw = ['PART', channel];
if (message) {
raw.push(message);
}
this.raw(raw);
}
}, {
key: "mode",
value: function mode(channel, _mode, extra_args) {
var raw = ['MODE', channel, _mode];
if (extra_args) {
if (Array.isArray(extra_args)) {
raw = raw.concat(extra_args);
} else {
raw.push(extra_args);
}
}
this.raw(raw);
}
}, {
key: "inviteList",
value: function inviteList(channel, cb) {
var client = this;
var invex = this.network.supports('INVEX');
var mode = 'I';
if (typeof invex === 'string' && invex) {
mode = invex;
}
function onInviteList(event) {
if (client.caseCompare(event.channel, channel)) {
unbindEvents();
if (typeof cb === 'function') {
cb(event);
}
}
}
function onInviteListErr(event) {
if (event.error === 'chanop_privs_needed') {
unbindEvents();
if (typeof cb === 'function') {
cb(null);
}
}
}
function bindEvents() {
client.on('inviteList', onInviteList);
client.on('irc error', onInviteListErr);
}
function unbindEvents() {
client.removeListener('inviteList', onInviteList);
client.removeListener('irc error', onInviteListErr);
}
bindEvents();
this.raw(['MODE', channel, mode]);
}
}, {
key: "invite",
value: function invite(channel, nick) {
var raw = ['INVITE', nick, channel];
this.raw(raw);
}
}, {
key: "addInvite",
value: function addInvite(channel, mask) {
var mode = 'I';
var invex = this.network.supports('INVEX');
if (typeof invex === 'string') {
mode = invex;
}
var raw = ['MODE', channel, '+' + mode, mask];
this.raw(raw);
}
}, {
key: "removeInvite",
value: function removeInvite(channel, mask) {
var mode = 'I';
var invex = this.network.supports('INVEX');
if (typeof invex === 'string') {
mode = invex;
}
var raw = ['MODE', channel, '-' + mode, mask];
this.raw(raw);
}
}, {
key: "banlist",
value: function banlist(channel, cb) {
var client = this;
var raw = ['MODE', channel, 'b'];
this.on('banlist', function onBanlist(event) {
if (client.caseCompare(event.channel, channel)) {
client.removeListener('banlist', onBanlist);
if (typeof cb === 'function') {
cb(event);
}
}
});
this.raw(raw);
}
}, {
key: "ban",
value: function ban(channel, mask) {
var raw = ['MODE', channel, '+b', mask];
this.raw(raw);
}
}, {
key: "unban",
value: function unban(channel, mask) {
var raw = ['MODE', channel, '-b', mask];
this.raw(raw);
}
}, {
key: "setTopic",
value: function setTopic(channel, newTopic) {
if (!newTopic || !newTopic.trim()) {
// If newTopic is undefined or empty, remove the existing topic
// this check is to prevent unexpectedly requesting the current topic
// when trying to clear the topic
this.clearTopic(channel);
return;
}
this.raw('TOPIC', channel, newTopic);
}
}, {
key: "clearTopic",
value: function clearTopic(channel) {
// The trailing `:` is required otherwise it would be requesting the topic
// and not clearing it
this.raw("TOPIC ".concat(channel, " :"));
}
}, {
key: "ctcpRequest",
value: function ctcpRequest(target, type /*, paramN */) {
var params = Array.prototype.slice.call(arguments, 1);
// make sure the CTCP type is uppercased
params[0] = params[0].toUpperCase();
this.raw('PRIVMSG', target, String.fromCharCode(1) + params.join(' ') + String.fromCharCode(1));
}
}, {
key: "ctcpResponse",
value: function ctcpResponse(target, type /*, paramN */) {
var params = Array.prototype.slice.call(arguments, 1);
// make sure the CTCP type is uppercased
params[0] = params[0].toUpperCase();
this.raw('NOTICE', target, String.fromCharCode(1) + params.join(' ') + String.fromCharCode(1));
}
}, {
key: "action",
value: function action(target, message) {
var that = this;
// Maximum length of target + message we can send to the IRC server is 500 characters
// but we need to leave extra room for the sender prefix so the entire message can
// be sent from the IRCd to the target without being truncated.
// The block length here is the max, but without the non-content characters:
// the command name, the space, and the two SOH chars
var commandName = 'ACTION';
var blockLength = this.options.message_max_length - (commandName.length + 3);
var blocks = _toConsumableArray(lineBreak(message, {
bytes: blockLength,
allowBreakingWords: true,
allowBreakingGraphemes: true
}));
blocks.forEach(function (block) {
that.ctcpRequest(target, commandName, block);
});
return blocks;
}
}, {
key: "whois",
value: function whois(target, _cb) {
var client = this;
var cb;
var irc_args = ['WHOIS'];
// Support whois(target, arg1, arg2, argN, cb)
_.each(arguments, function (arg) {
if (typeof arg === 'function') {
cb = arg;
} else {
irc_args.push(arg);
}
});
this.on('whois', function onWhois(event) {
if (client.caseCompare(event.nick, target)) {
client.removeListener('whois', onWhois);
if (typeof cb === 'function') {
cb(event);
}
}
});
this.raw(irc_args);
}
}, {
key: "whowas",
value: function whowas(target, _cb) {
var client = this;
var cb;
var irc_args = ['WHOWAS'];
// Support whowas(target, arg1, arg2, argN, cb)
_.each(arguments, function (arg) {
if (typeof arg === 'function') {
cb = arg;
} else {
irc_args.push(arg);
}
});
this.on('whowas', function onWhowas(event) {
if (client.caseCompare(event.nick, target)) {
client.removeListener('whowas', onWhowas);
if (typeof cb === 'function') {
cb(event);
}
}
});
this.raw(irc_args);
}
/**
* WHO requests are queued up to run serially.
* This is mostly because networks will only reply serially and it makes
* it easier to include the correct replies to callbacks
*/
}, {
key: "who",
value: function who(target, cb) {
if (!this.who_queue) {
this.who_queue = [];
}
this.who_queue.push([target, cb]);
this.processNextWhoQueue();
}
}, {
key: "monitorlist",
value: function monitorlist(cb) {
var client = this;
var raw = ['MONITOR', 'L'];
this.on('monitorList', function onMonitorlist(event) {
client.removeListener('monitorList', onMonitorlist);
if (typeof cb === 'function') {
cb(event);
}
});
this.raw(raw);
}
}, {
key: "addMonitor",
value: function addMonitor(target) {
var raw = ['MONITOR', '+', target];
this.raw(raw);
}
}, {
key: "removeMonitor",
value: function removeMonitor(target) {
var raw = ['MONITOR', '-', target];
this.raw(raw);
}
}, {
key: "queryMonitor",
value: function queryMonitor() {
var raw = ['MONITOR', 'S'];
this.raw(raw);
}
}, {
key: "clearMonitor",
value: function clearMonitor() {
var raw = ['MONITOR', 'C'];
this.raw(raw);
}
}, {
key: "processNextWhoQueue",
value: function processNextWhoQueue() {
var client = this;
// No items in the queue or the queue is already running?
if (client.who_queue.length === 0 || client.who_queue.is_running) {
return;
}
client.who_queue.is_running = true;
var this_who = client.who_queue.shift();
var target = this_who[0];
var cb = this_who[1];
if (!target || typeof target !== 'string') {
if (typeof cb === 'function') {
_.defer(cb, {
target: target,
users: []
});
}
// Start the next queued WHO request
client.who_queue.is_running = false;
_.defer(_.bind(client.processNextWhoQueue, client));
return;
}
client.on('wholist', function onWho(event) {
client.removeListener('wholist', onWho);
// Start the next queued WHO request
client.who_queue.is_running = false;
_.defer(_.bind(client.processNextWhoQueue, client));
if (typeof cb === 'function') {
cb({
target: target,
users: event.users
});
}
});
if (client.network.supports('whox')) {
var token = client.whox_token.next();
client.raw('WHO', target, "%tcuhsnfdaor,".concat(token));
} else {
client.raw('WHO', target);
}
}
/**
* Explicitely start a channel list, avoiding potential issues with broken IRC servers not sending RPL_LISTSTART
*/
}, {
key: "list",
value: function list(/* paramN */
) {
var args = Array.prototype.slice.call(arguments);
this.command_handler.cache('chanlist').channels = [];
args.unshift('LIST');
this.raw(args);
}
}, {
key: "channel",
value: function channel(channel_name, key) {
return new Channel(this, channel_name, key);
}
}, {
key: "match",
value: function match(match_regex, cb, message_type) {
var client = this;
var onMessage = function onMessage(event) {
if (event.message.match(match_regex)) {
cb(event);
}
};
this.on(message_type || 'message', onMessage);
return {
stop: function stop() {
client.removeListener(message_type || 'message', onMessage);
}
};
}
}, {
key: "matchNotice",
value: function matchNotice(match_regex, cb) {
return this.match(match_regex, cb, 'notice');
}
}, {
key: "matchMessage",
value: function matchMessage(match_regex, cb) {
return this.match(match_regex, cb, 'privmsg');
}
}, {
key: "matchAction",
value: function matchAction(match_regex, cb) {
return this.match(match_regex, cb, 'action');
}
}, {
key: "caseCompare",
value: function caseCompare(string1, string2) {
var length = string1.length;
if (length !== string2.length) {
return false;
}
var upperBound = this._getCaseMappingUpperAsciiBound();
for (var i = 0; i < length; i++) {
var charCode1 = string1.charCodeAt(i);
var charCode2 = string2.charCodeAt(i);
if (charCode1 >= 65 && charCode1 <= upperBound) {
charCode1 += 32;
}
if (charCode2 >= 65 && charCode2 <= upperBound) {
charCode2 += 32;
}
if (charCode1 !== charCode2) {
return false;
}
}
return true;
}
}, {
key: "caseLower",
value: function caseLower(string) {
var upperBound = this._getCaseMappingUpperAsciiBound();
var result = '';
for (var i = 0; i < string.length; i++) {
var charCode = string.charCodeAt(i);
// ASCII character from 'A' to upper bound defined above
if (charCode >= 65 && charCode <= upperBound) {
// All the relevant uppercase characters are exactly
// 32 bytes apart from lowercase ones, so we simply add 32
// and get the equivalent character in lower case
result += String.fromCharCode(charCode + 32);
} else {
result += string[i];
}
}
return result;
}
}, {
key: "caseUpper",
value: function caseUpper(string) {
var upperBound = this._getCaseMappingUpperAsciiBound() + 32;
var result = '';
for (var i = 0; i < string.length; i++) {
var charCode = string.charCodeAt(i);
// ASCII character from 'a' to upper bound defined above
if (charCode >= 97 && charCode <= upperBound) {
// All the relevant lowercase characters are exactly
// 32 bytes apart from lowercase ones, so we simply subtract 32
// and get the equivalent character in upper case
result += String.fromCharCode(charCode - 32);
} else {
result += string[i];
}
}
return result;
}
}, {
key: "_getCaseMappingUpperAsciiBound",
value: function _getCaseMappingUpperAsciiBound() {
if (this.network.options.CASEMAPPING === 'ascii') {
return 90; // 'Z'
} else if (this.network.options.CASEMAPPING === 'strict-rfc1459') {
return 93; // ']'
}
return 94; // '^' - default casemapping=rfc1459
}
}], [{
key: "setDefaultTransport",
value: function setDefaultTransport(transport) {
default_transport = transport;
}
}]);
}(EventEmitter);