diff --git a/lib/irc.js b/lib/irc.js index db7fa06e..dfb5241e 100644 --- a/lib/irc.js +++ b/lib/irc.js @@ -154,9 +154,11 @@ function Client(server, nick, opt) { match[1] = match[1].split(''); match[2] = match[2].split(''); while ( match[1].length ) { - self.modeForPrefix[match[2][0]] = match[1][0]; - self.supported.channel.modes.b += match[1][0]; - self.prefixForMode[match[1].shift()] = match[2].shift(); + var prefix = match[1].shift(); + var mode = match[2].shift(); + self.modeForPrefix[mode] = prefix; + self.supported.channel.modes.b += prefix; + self.prefixForMode[prefix] = mode; } } break; @@ -219,51 +221,11 @@ function Client(server, nick, opt) { break; case "MODE": if ( self.opt.debug ) - util.log("MODE: " + message.args[0] + " sets mode: " + message.args[1]); + util.log("MODE:" + message.args[0] + " sets mode: " + message.args.slice(1).join(' ')); var channel = self.chanData(message.args[0]); if ( !channel ) break; - var modeList = message.args[1].split(''); - var adding = true; - var modeArgs = message.args.slice(2); - modeList.forEach(function(mode) { - if ( mode == '+' ) { adding = true; return; } - if ( mode == '-' ) { adding = false; return; } - if ( mode in self.prefixForMode ) { - // channel user modes - var user = modeArgs.shift(); - if ( adding ) { - if ( channel.users[user].indexOf(self.prefixForMode[mode]) === -1 ) - channel.users[user] += self.prefixForMode[mode]; - - self.emit('+mode', message.args[0], message.nick, mode, user, message); - } - else { - channel.users[user] = channel.users[user].replace(self.prefixForMode[mode], ''); - self.emit('-mode', message.args[0], message.nick, mode, user, message); - } - } - else { - var modeArg; - // channel modes - if ( mode.match(/^[bkl]$/) ) { - modeArg = modeArgs.shift(); - if ( modeArg.length === 0 ) - modeArg = undefined; - } - // TODO - deal nicely with channel modes that take args - if ( adding ) { - if ( channel.mode.indexOf(mode) === -1 ) - channel.mode += mode; - - self.emit('+mode', message.args[0], message.nick, mode, modeArg, message); - } - else { - channel.mode = channel.mode.replace(mode, ''); - self.emit('-mode', message.args[0], message.nick, mode, modeArg, message); - } - } - }); + channel.setModes(self, message.args.slice(1), message); break; case "NICK": if ( message.nick == self.nick ) @@ -393,7 +355,7 @@ function Client(server, nick, opt) { case "rpl_channelmodeis": var channel = self.chanData(message.args[1]); if ( channel ) { - channel.mode = message.args[2]; + channel.setModes(self, message.args.slice(2)); } break; case "rpl_creationtime": @@ -579,19 +541,15 @@ Client.prototype.prefixForMode = {}; Client.prototype.modeForPrefix = {}; Client.prototype.chans = {}; Client.prototype._whoisData = {}; + Client.prototype.chanData = function( name, create ) { // {{{ var key = name.toLowerCase(); if ( create ) { - this.chans[key] = this.chans[key] || { - key: key, - serverName: name, - users: {}, - mode: '', - }; + this.chans[key] = this.chans[key] || new Channel(name); } - return this.chans[key]; } // }}} + Client.prototype.connect = function ( retryCount, callback ) { // {{{ if ( typeof(retryCount) === 'function' ) { callback = retryCount; @@ -811,9 +769,9 @@ Client.prototype.part = function(channel, message, callback) { // {{{ } if (message) { - this.send('PART', channel, message); + this.send('PART', channel, message); } else { - this.send('PART', channel); + this.send('PART', channel); } } // }}} Client.prototype.say = function(target, text) { // {{{ @@ -889,6 +847,100 @@ Client.prototype.ctcp = function(to, type, text) { return this[type === 'privmsg' ? 'say' : 'notice'](to, '\1'+text+'\1'); } +function Channel(name, users, mode){ + this.key = name.toLowerCase(); + this.serverName = name; + this.users = users || {}; + this.mode = mode || ''; + this.setting = {}; +} +exports.Channel = Channel; +Channel.prototype.key = ''; +Channel.prototype.serverName = ''; +Channel.prototype.users = {}; +Channel.prototype.mode = ''; +Channel.prototype.setting = {}; +Channel.prototype.setModes = function setModes(connection, modes, message){ + var self = this; + var modeList = modes[0].split(''); + var modeArgs = modes.slice(1); + var adding = true; + modeList.forEach(function(mode) { + if ( mode == '+' ) { adding = true; return; } + if ( mode == '-' ) { adding = false; return; } + self.setMode(connection, adding, mode, modeArgs, message); + }); +} +Channel.prototype.setMode = function setModes(connection, adding, mode, modeArgs, message){ + var channel = this; + var supports = connection.supported.channel.modes; + if(connection.prefixForMode[mode]) { + // channel user modes + var user = modeArgs.shift(); + if(adding) { + if(channel.users[user] && channel.users[user].indexOf(connection.prefixForMode[mode]) === -1) { + channel.users[user] += connection.prefixForMode[mode]; + if(message) connection.emit('+mode', message.args[0], message.nick, mode, user, message); + } else { + connection.emit('error', new Error('Unable to apply mode +'+mode+' to nickname '+user+ ' on channel '+channel.key)); + } + } else { + if(channel.users[user] && channel.users[user].indexOf(connection.prefixForMode[mode]) === -1) { + channel.users[user] = channel.users[user].replace(connection.prefixForMode[mode], ''); + if(message) connection.emit('-mode', message.args[0], message.nick, mode, user, message); + } else { + connection.emit('error', new Error('Unable to apply mode -'+mode+' to nickname '+user+ ' on channel '+channel.key)); + } + } + } else if(supports.a.indexOf(mode)>=0 || supports.b.indexOf(mode)>=0) { + // Add or remove a nick/address from a list + // Any nicknames should already have been handled so just patterns now? + // If it is a nickname list, then it'll just end up being a list of nicknames which is acceptable + var list = channel.setting[mode]; + if(!(list instanceof Array)) list = channel.setting[mode] = []; + var arg = modeArgs.shift(); + if(adding){ + if(list.indexOf(arg)>=0) return; + list.push(arg); + if(message) connection.emit('+mode', message.args[0], message.nick, mode, arg, message); + } else { + channel.setting[mode] = list.filter(function(v){ return v!=arg; }); + if(message) connection.emit('-mode', message.args[0], message.nick, mode, arg, message); + } + } else if(supports.c.indexOf(mode) >= 0) { + // These set a value only when the mode is set + if(adding){ + if ( channel.mode.indexOf(mode) === -1 ){ + channel.mode += mode; + } + var arg = modeArgs.shift(); + channel.setting[mode] = arg; + if(message) connection.emit('+mode', message.args[0], message.nick, mode, arg, message); + } else { + channel.mode = channel.mode.replace(mode, ''); + delete channel.setting[mode]; + if(message) connection.emit('-mode', message.args[0], message.nick, mode, arg, message); + } + } else if(supports.d.indexOf(mode) >= 0) { + if ( adding ) { + if ( channel.mode.indexOf(mode) === -1 ){ + // FIXME - Some users may expect a particular order of channel modes + channel.mode += mode; + } + channel.setting[mode] = true; + if(message) connection.emit('+mode', message.args[0], message.nick, mode, undefined, message); + } else { + channel.mode = channel.mode.replace(mode, ''); + delete channel.setting[mode]; + if(message) connection.emit('-mode', message.args[0], message.nick, mode, undefined, message); + } + } else { + // Don't handle this, but emit an error. + // It may be in a fifth CHANMODES group which we were supposed to ignore. + connection.emit('error', new Error('Unhandled mode '+(adding?'+':'-')+mode)); + } +} + /* * parseMessage(line, stripColors) *