X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=server%2Firc%2Fcommands.js;h=03899aa66e537cebb8e1fb0d66fc62508334b95a;hb=96ecb5e70d7d36c529940c61b2a542857e0e956f;hp=445cd5a5796eacaf8745dbf2d89438082b57f6ed;hpb=6cf14aa29eacdb58274652b5d9d1ae81f591260d;p=KiwiIRC.git diff --git a/server/irc/commands.js b/server/irc/commands.js index 445cd5a..03899aa 100644 --- a/server/irc/commands.js +++ b/server/irc/commands.js @@ -24,16 +24,22 @@ irc_numerics = { '312': 'RPL_WHOISSERVER', '313': 'RPL_WHOISOPERATOR', '314': 'RPL_WHOWASUSER', + '315': 'RPL_ENDOFWHO', '317': 'RPL_WHOISIDLE', '318': 'RPL_ENDOFWHOIS', '319': 'RPL_WHOISCHANNELS', '321': 'RPL_LISTSTART', '322': 'RPL_LIST', '323': 'RPL_LISTEND', + '324': 'RPL_CHANNELMODEIS', + '328': 'RPL_CHANNEL_URL', + '329': 'RPL_CREATIONTIME', '330': 'RPL_WHOISACCOUNT', '331': 'RPL_NOTOPIC', '332': 'RPL_TOPIC', '333': 'RPL_TOPICWHOTIME', + '341': 'RPL_INVITING', + '352': 'RPL_WHOREPLY', '353': 'RPL_NAMEREPLY', '364': 'RPL_LINKS', '365': 'RPL_ENDOFLINKS', @@ -292,6 +298,34 @@ handlers = { }); }, + 'RPL_CHANNELMODEIS': function (command) { + var channel = command.params[1], + modes = parseModeList.call(this, command.params[2], command.params.slice(3)); + + this.irc_connection.emit('channel ' + channel + ' info', { + channel: channel, + modes: modes + }); + }, + + 'RPL_CREATIONTIME': function (command) { + var channel = command.params[1]; + + this.irc_connection.emit('channel ' + channel + ' info', { + channel: channel, + created_at: parseInt(command.params[2], 10) + }); + }, + + 'RPL_CHANNEL_URL': function (command) { + var channel = command.params[1]; + + this.irc_connection.emit('channel ' + channel + ' info', { + channel: channel, + url: command.trailing + }); + }, + 'RPL_MOTD': function (command) { this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' motd', { motd: command.trailing + '\n' @@ -340,6 +374,18 @@ handlers = { }); }, + 'RPL_WHOREPLY': function (command) { + // For the time being, NOOP this command so they don't get passed + // down to the client. Waste of bandwidth since we do not use it yet + // TODO: Impliment RPL_WHOREPLY + }, + + 'RPL_ENDOFWHO': function (command) { + // For the time being, NOOP this command so they don't get passed + // down to the client. Waste of bandwidth since we do not use it yet + // TODO: Impliment RPL_ENDOFWHO + }, + 'RPL_BANLIST': function (command) { this.irc_connection.emit('channel ' + command.params[1] + ' banlist', { channel: command.params[1], @@ -377,6 +423,13 @@ handlers = { }); }, + 'RPL_INVITING': function (command) { + this.irc_connection.emit('channel ' + command.params[1] + ' invited', { + nick: command.params[0], + channel: command.params[1] + }); + }, + 'PING': function (command) { this.irc_connection.write('PONG ' + command.trailing); }, @@ -389,12 +442,8 @@ handlers = { channel = command.params[0]; } - if (_.contains(this.irc_connection.cap.enabled, 'server-time') && command.tags && command.tags.length > 0) { - time = _.find(command.tags, function (tag) { - return tag.tag === 'time'; - }); - time = time ? time.value : undefined; - } + // Check if we have a server-time + time = getServerTime.call(this, command); this.irc_connection.emit('channel ' + channel + ' join', { nick: command.nick, @@ -408,12 +457,8 @@ handlers = { 'PART': function (command) { var time; - if (_.contains(this.irc_connection.cap.enabled, 'server-time') && command.tags && command.tags.length > 0) { - time = _.find(command.tags, function (tag) { - return tag.tag === 'time'; - }); - time = time ? time.value : undefined; - } + // Check if we have a server-time + time = getServerTime.call(this, command); this.irc_connection.emit('channel ' + command.params[0] + ' part', { nick: command.nick, @@ -428,12 +473,8 @@ handlers = { 'KICK': function (command) { var time; - if (_.contains(this.irc_connection.cap.enabled, 'server-time') && command.tags && command.tags.length > 0) { - time = _.find(command.tags, function (tag) { - return tag.tag === 'time'; - }); - time = time ? time.value : undefined; - } + // Check if we have a server-time + time = getServerTime.call(this, command); this.irc_connection.emit('channel ' + command.params[0] + ' kick', { kicked: command.params[1], @@ -449,12 +490,8 @@ handlers = { 'QUIT': function (command) { var time; - if (_.contains(this.irc_connection.cap.enabled, 'server-time') && command.tags && command.tags.length > 0) { - time = _.find(command.tags, function (tag) { - return tag.tag === 'time'; - }); - time = time ? time.value : undefined; - } + // Check if we have a server-time + time = getServerTime.call(this, command); this.irc_connection.emit('user ' + command.nick + ' quit', { nick: command.nick, @@ -469,12 +506,8 @@ handlers = { var namespace, time; - if (_.contains(this.irc_connection.cap.enabled, 'server-time') && command.tags && command.tags.length > 0) { - time = _.find(command.tags, function (tag) { - return tag.tag === 'time'; - }); - time = time ? time.value : undefined; - } + // Check if we have a server-time + time = getServerTime.call(this, command); if ((command.trailing.charAt(0) === String.fromCharCode(1)) && (command.trailing.charAt(command.trailing.length - 1) === String.fromCharCode(1))) { @@ -508,12 +541,8 @@ handlers = { 'NICK': function (command) { var time; - if (_.contains(this.irc_connection.cap.enabled, 'server-time') && command.tags && command.tags.length > 0) { - time = _.find(command.tags, function (tag) { - return tag.tag === 'time'; - }); - time = time ? time.value : undefined; - } + // Check if we have a server-time + time = getServerTime.call(this, command); this.irc_connection.emit('user ' + command.nick + ' nick', { nick: command.nick, @@ -530,12 +559,8 @@ handlers = { // If we don't have an associated channel, no need to continue if (!command.params[0]) return; - if (_.contains(this.irc_connection.cap.enabled, 'server-time') && command.tags && command.tags.length > 0) { - time = _.find(command.tags, function (tag) { - return tag.tag === 'time'; - }); - time = time ? time.value : undefined; - } + // Check if we have a server-time + time = getServerTime.call(this, command); var channel = command.params[0], topic = command.trailing || ''; @@ -549,62 +574,13 @@ handlers = { }, 'MODE': function (command) { - var chanmodes = this.irc_connection.options.CHANMODES || [], - prefixes = this.irc_connection.options.PREFIX || [], - always_param = (chanmodes[0] || '').concat((chanmodes[1] || '')), - modes = [], - has_param, i, j, add, event, time; - - if (_.contains(this.irc_connection.cap.enabled, 'server-time') && command.tags && command.tags.length > 0) { - time = _.find(command.tags, function (tag) { - return tag.tag === 'time'; - }); - time = time ? time.value : undefined; - } + var modes = [], event, time; - prefixes = _.reduce(prefixes, function (list, prefix) { - list.push(prefix.mode); - return list; - }, []); - always_param = always_param.split('').concat(prefixes); - - has_param = function (mode, add) { - if (_.find(always_param, function (m) { - return m === mode; - })) { - return true; - } else if (add && _.find((chanmodes[2] || '').split(''), function (m) { - return m === mode; - })) { - return true; - } else { - return false; - } - }; - - if (!command.params[1]) { - command.params[1] = command.trailing; - } - - j = 0; - for (i = 0; i < command.params[1].length; i++) { - switch (command.params[1][i]) { - case '+': - add = true; - break; - case '-': - add = false; - break; - default: - if (has_param(command.params[1][i], add)) { - modes.push({mode: (add ? '+' : '-') + command.params[1][i], param: command.params[2 + j]}); - j++; - } else { - modes.push({mode: (add ? '+' : '-') + command.params[1][i], param: null}); - } - } - } + // Check if we have a server-time + time = getServerTime.call(this, command); + // Get a JSON representation of the modes + modes = parseModeList.call(this, command.params[1] || command.trailing, command.params.slice(2)); event = (_.contains(this.irc_connection.options.CHANTYPES, command.params[0][0]) ? 'channel ' : 'user ') + command.params[0] + ' mode'; this.irc_connection.emit(event, { @@ -618,12 +594,8 @@ handlers = { 'PRIVMSG': function (command) { var tmp, namespace, time; - if (_.contains(this.irc_connection.cap.enabled, 'server-time') && command.tags && command.tags.length > 0) { - time = _.find(command.tags, function (tag) { - return tag.tag === 'time'; - }); - time = time ? time.value : undefined; - } + // Check if we have a server-time + time = getServerTime.call(this, command); if ((command.trailing.charAt(0) === String.fromCharCode(1)) && (command.trailing.charAt(command.trailing.length - 1) === String.fromCharCode(1))) { //CTCP request @@ -683,7 +655,7 @@ handlers = { var request; // Which capabilities we want to enable - var want = ['multi-prefix', 'away-notify', 'server-time']; + var want = ['multi-prefix', 'away-notify', 'server-time', 'znc.in/server-time-iso', 'znc.in/server-time']; if (this.irc_connection.password) { want.push('sasl'); @@ -755,12 +727,8 @@ handlers = { 'AWAY': function (command) { var time; - if (_.contains(this.irc_connection.cap.enabled, 'server-time') && command.tags && command.tags.length > 0) { - time = _.find(command.tags, function (tag) { - return tag.tag === 'time'; - }); - time = time ? time.value : undefined; - } + // Check if we have a server-time + time = getServerTime.call(this, command); this.irc_connection.emit('user ' + command.nick + ' away', { nick: command.nick, @@ -1015,3 +983,135 @@ function genericNotice (command, msg, is_error) { numeric: parseInt(command.command, 10) }); } + + +/** + * Convert a mode string such as '+k pass', or '-i' to a readable + * format. + * [ { mode: '+k', param: 'pass' } ] + * [ { mode: '-i', param: null } ] + */ +function parseModeList(mode_string, mode_params) { + var chanmodes = this.irc_connection.options.CHANMODES || [], + prefixes = this.irc_connection.options.PREFIX || [], + always_param = (chanmodes[0] || '').concat((chanmodes[1] || '')), + modes = [], + has_param, i, j, add; + + prefixes = _.reduce(prefixes, function (list, prefix) { + list.push(prefix.mode); + return list; + }, []); + always_param = always_param.split('').concat(prefixes); + + has_param = function (mode, add) { + if (_.find(always_param, function (m) { + return m === mode; + })) { + return true; + } else if (add && _.find((chanmodes[2] || '').split(''), function (m) { + return m === mode; + })) { + return true; + } else { + return false; + } + }; + + j = 0; + for (i = 0; i < mode_string.length; i++) { + switch (mode_string[i]) { + case '+': + add = true; + break; + case '-': + add = false; + break; + default: + if (has_param(mode_string[i], add)) { + modes.push({mode: (add ? '+' : '-') + mode_string[i], param: mode_params[j]}); + j++; + } else { + modes.push({mode: (add ? '+' : '-') + mode_string[i], param: null}); + } + } + } + + return modes; +} + + +function getServerTime(command) { + var time; + + // No tags? No times. + if (!command.tags || command.tags.length === 0) { + return time; + } + + if (capContainsAny.call(this, ['server-time', 'znc.in/server-time', 'znc.in/server-time-iso'])) { + time = _.find(command.tags, function (tag) { + return tag.tag === 'time'; + }); + + time = time ? time.value : undefined; + + // Convert the time value to a unixtimestamp + if (typeof time === 'string') { + if (time.indexOf('T') > -1) { + time = parseISO8601(time); + + } else if(time.match(/^[0-9.]+$/)) { + // A string formatted unix timestamp + time = new Date(time * 1000); + } + + time = time.getTime(); + + } else if (typeof time === 'number') { + time = new Date(time * 1000); + time = time.getTime(); + } + } + + return time; +} + + +function capContainsAny (caps) { + var intersection; + if (!caps instanceof Array) { + caps = [caps]; + } + intersection = _.intersection(this.irc_connection.cap.enabled, caps); + return intersection.length > 0; +} + + +// Code based on http://anentropic.wordpress.com/2009/06/25/javascript-iso8601-parser-and-pretty-dates/#comment-154 +function parseISO8601(str) { + if (Date.prototype.toISOString) { + return new Date(str); + } else { + var parts = str.split('T'), + dateParts = parts[0].split('-'), + timeParts = parts[1].split('Z'), + timeSubParts = timeParts[0].split(':'), + timeSecParts = timeSubParts[2].split('.'), + timeHours = Number(timeSubParts[0]), + _date = new Date(); + + _date.setUTCFullYear(Number(dateParts[0])); + _date.setUTCDate(1); + _date.setUTCMonth(Number(dateParts[1])-1); + _date.setUTCDate(Number(dateParts[2])); + _date.setUTCHours(Number(timeHours)); + _date.setUTCMinutes(Number(timeSubParts[1])); + _date.setUTCSeconds(Number(timeSecParts[0])); + if (timeSecParts[1]) { + _date.setUTCMilliseconds(Number(timeSecParts[1])); + } + + return _date; + } +}