UNPKG

irc-framework

Version:
966 lines (933 loc) 34.6 kB
'use strict'; 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);