1 var _
= require('lodash'),
10 '005': 'RPL_ISUPPORT',
13 '250': 'RPL_STATSCONN',
14 '251': 'RPL_LUSERCLIENT',
16 '253': 'RPL_LUSERUNKNOWN',
17 '254': 'RPL_LUSERCHANNELS',
19 '265': 'RPL_LOCALUSERS',
20 '266': 'RPL_GLOBALUSERS',
22 '307': 'RPL_WHOISREGNICK',
23 '311': 'RPL_WHOISUSER',
24 '312': 'RPL_WHOISSERVER',
25 '313': 'RPL_WHOISOPERATOR',
26 '314': 'RPL_WHOWASUSER',
27 '317': 'RPL_WHOISIDLE',
28 '318': 'RPL_ENDOFWHOIS',
29 '319': 'RPL_WHOISCHANNELS',
30 '321': 'RPL_LISTSTART',
33 '330': 'RPL_WHOISACCOUNT',
36 '333': 'RPL_TOPICWHOTIME',
37 '353': 'RPL_NAMEREPLY',
39 '365': 'RPL_ENDOFLINKS',
40 '366': 'RPL_ENDOFNAMES',
42 '368': 'RPL_ENDOFBANLIST',
43 '369': 'RPL_ENDOFWHOWAS',
45 '375': 'RPL_MOTDSTART',
46 '376': 'RPL_ENDOFMOTD',
47 '378': 'RPL_WHOISHOST',
48 '379': 'RPL_WHOISMODES',
49 '401': 'ERR_NOSUCHNICK',
50 '404': 'ERR_CANNOTSENDTOCHAN',
51 '405': 'ERR_TOOMANYCHANNELS',
52 '406': 'ERR_WASNOSUCHNICK',
53 '421': 'ERR_UNKNOWNCOMMAND',
55 '432': 'ERR_ERRONEUSNICKNAME',
56 '433': 'ERR_NICKNAMEINUSE',
57 '441': 'ERR_USERNOTINCHANNEL',
58 '442': 'ERR_NOTONCHANNEL',
59 '443': 'ERR_USERONCHANNEL',
60 '451': 'ERR_NOTREGISTERED',
61 '464': 'ERR_PASSWDMISMATCH',
62 '470': 'ERR_LINKCHANNEL',
63 '471': 'ERR_CHANNELISFULL',
64 '473': 'ERR_INVITEONLYCHAN',
65 '474': 'ERR_BANNEDFROMCHAN',
66 '475': 'ERR_BADCHANNELKEY',
67 '481': 'ERR_NOPRIVILEGES',
68 '482': 'ERR_CHANOPRIVSNEEDED',
69 '670': 'RPL_STARTTLS',
70 '671': 'RPL_WHOISSECURE',
71 '900': 'RPL_SASLAUTHENTICATED',
72 '903': 'RPL_SASLLOGGEDIN',
73 '904': 'ERR_SASLNOTAUTHORISED',
74 '906': 'ERR_SASLABORTED',
75 '907': 'ERR_SASLALREADYAUTHED'
79 IrcCommands = function (irc_connection
) {
80 this.irc_connection
= irc_connection
;
82 module
.exports
= IrcCommands
;
84 IrcCommands
.prototype.dispatch = function (command
, data
) {
86 if (irc_numerics
[command
]) {
87 command
= irc_numerics
[command
];
89 if (handlers
[command
]) {
90 handlers
[command
].call(this, data
);
92 unknownCommand
.call(this, command
, data
);
96 IrcCommands
.addHandler = function (command
, handler
) {
97 if (typeof handler
!== 'function') {
100 handlers
[command
] = handler
;
103 IrcCommands
.addNumeric = function (numeric
, handler_name
) {
104 irc_numerics
[numeric
+ ''] = handler_name
+'';
107 unknownCommand = function (command
, data
) {
108 var params
= _
.clone(data
.params
);
110 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' unknown_command', {
113 trailing
: data
.trailing
118 this.irc_connection.emit(namespace + ' ' + command.params[0] + ' notice', {
119 from_server: command.prefix ? true : false,
120 nick: command.nick || command.prefix || undefined,
121 ident: command.ident,
122 hostname: command.hostname,
123 target: command.params[0],
124 msg: command.trailing
131 'RPL_WELCOME': function (command
) {
132 var nick
= command
.params
[0];
133 this.irc_connection
.registered
= true;
134 this.cap_negotation
= false;
135 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' connect', {
140 'RPL_ISUPPORT': function (command
) {
141 var options
, i
, option
, matches
, j
;
142 options
= command
.params
;
143 for (i
= 1; i
< options
.length
; i
++) {
144 option
= options
[i
].split("=", 2);
145 option
[0] = option
[0].toUpperCase();
146 this.irc_connection
.options
[option
[0]] = (typeof option
[1] !== 'undefined') ? option
[1] : true;
147 if (_
.include(['NETWORK', 'PREFIX', 'CHANTYPES', 'CHANMODES', 'NAMESX'], option
[0])) {
148 if (option
[0] === 'PREFIX') {
149 matches
= /\(([^)]*)\)(.*)/.exec(option
[1]);
150 if ((matches
) && (matches
.length
=== 3)) {
151 this.irc_connection
.options
.PREFIX
= [];
152 for (j
= 0; j
< matches
[2].length
; j
++) {
153 this.irc_connection
.options
.PREFIX
.push({symbol
: matches
[2].charAt(j
), mode
: matches
[1].charAt(j
)});
156 } else if (option
[0] === 'CHANTYPES') {
157 this.irc_connection
.options
.CHANTYPES
= this.irc_connection
.options
.CHANTYPES
.split('');
158 } else if (option
[0] === 'CHANMODES') {
159 this.irc_connection
.options
.CHANMODES
= option
[1].split(',');
160 } else if ((option
[0] === 'NAMESX') && (!_
.contains(this.irc_connection
.cap
.enabled
, 'multi-prefix'))) {
161 this.irc_connection
.write('PROTOCTL NAMESX');
165 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' options', {
166 options
: this.irc_connection
.options
,
167 cap
: this.irc_connection
.cap
.enabled
171 'RPL_ENDOFWHOIS': function (command
) {
172 this.irc_connection
.emit('user ' + command
.params
[1] + ' endofwhois', {
173 nick
: command
.params
[1],
174 msg
: command
.trailing
178 'RPL_AWAY': function (command
) {
179 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoisaway', {
180 nick
: command
.params
[1],
181 reason
: command
.trailing
185 'RPL_WHOISUSER': function (command
) {
186 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoisuser', {
187 nick
: command
.params
[1],
188 ident
: command
.params
[2],
189 host
: command
.params
[3],
190 msg
: command
.trailing
194 'RPL_WHOISSERVER': function (command
) {
195 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoisserver', {
196 nick
: command
.params
[1],
197 irc_server
: command
.params
[2],
198 server_info
: command
.trailing
202 'RPL_WHOISOPERATOR': function (command
) {
203 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoisoperator', {
204 nick
: command
.params
[1],
205 msg
: command
.trailing
209 'RPL_WHOISCHANNELS': function (command
) {
210 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoischannels', {
211 nick
: command
.params
[1],
212 chans
: command
.trailing
216 'RPL_WHOISMODES': function (command
) {
217 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoismodes', {
218 nick
: command
.params
[1],
219 msg
: command
.trailing
223 'RPL_WHOISIDLE': function (command
) {
224 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoisidle', {
225 nick
: command
.params
[1],
226 idle
: command
.params
[2],
227 logon
: command
.params
[3] || undefined
231 'RPL_WHOISREGNICK': function (command
) {
232 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoisregnick', {
233 nick
: command
.params
[1],
234 msg
: command
.trailing
238 'RPL_WHOISHOST': function (command
) {
239 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoishost', {
240 nick
: command
.params
[1],
241 msg
: command
.trailing
245 'RPL_WHOISSECURE': function (command
) {
246 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoissecure', {
247 nick
: command
.params
[1]
251 'RPL_WHOISACCOUNT': function (command
) {
252 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoisaccount', {
253 nick
: command
.params
[1],
254 account
: command
.params
[2]
258 'RPL_WHOWASUSER': function (command
) {
259 this.irc_connection
.emit('user ' + command
.params
[1] + ' whowas', {
260 nick
: command
.params
[1],
261 ident
: command
.params
[2],
262 host
: command
.params
[3],
263 real_name
: command
.trailing
267 'RPL_ENDOFWHOWAS': function (command
) {
268 this.irc_connection
.emit('user ' + command
.params
[1] + ' endofwhowas', {
269 nick
: command
.params
[1]
273 'ERR_WASNOSUCHNICK': function (command
) {
274 this.irc_connection
.emit('user ' + command
.params
[1] + ' wasnosucknick', {
275 nick
: command
.params
[1]
279 'RPL_LISTSTART': function (command
) {
280 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' list_start', {});
283 'RPL_LISTEND': function (command
) {
284 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' list_end', {});
287 'RPL_LIST': function (command
) {
288 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' list_channel', {
289 channel
: command
.params
[1],
290 num_users
: parseInt(command
.params
[2], 10),
291 topic
: command
.trailing
295 'RPL_MOTD': function (command
) {
296 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' motd', {
297 motd
: command
.trailing
+ '\n'
301 'RPL_MOTDSTART': function (command
) {
302 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' motd_start', {});
305 'RPL_ENDOFMOTD': function (command
) {
306 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' motd_end', {});
309 'RPL_NAMEREPLY': function (command
) {
310 var members
= command
.trailing
.split(' ');
311 var member_list
= [];
313 _
.each(members
, function (member
) {
318 // Make sure we have some prefixes already
319 if (that
.irc_connection
.options
.PREFIX
) {
320 for (j
= 0; j
< that
.irc_connection
.options
.PREFIX
.length
; j
++) {
321 if (member
.charAt(i
) === that
.irc_connection
.options
.PREFIX
[j
].symbol
) {
322 modes
.push(that
.irc_connection
.options
.PREFIX
[j
].mode
);
328 member_list
.push({nick
: member
, modes
: modes
});
331 this.irc_connection
.emit('channel ' + command
.params
[2] + ' userlist', {
333 channel
: command
.params
[2]
337 'RPL_ENDOFNAMES': function (command
) {
338 this.irc_connection
.emit('channel ' + command
.params
[1] + ' userlist_end', {
339 channel
: command
.params
[1]
343 'RPL_BANLIST': function (command
) {
344 this.irc_connection
.emit('channel ' + command
.params
[1] + ' banlist', {
345 channel
: command
.params
[1],
346 banned
: command
.params
[2],
347 banned_by
: command
.params
[3],
348 banned_at
: command
.params
[4]
352 'RPL_ENDOFBANLIST': function (command
) {
353 this.irc_connection
.emit('channel ' + command
.params
[1] + ' banlist_end', {
354 channel
: command
.params
[1]
358 'RPL_TOPIC': function (command
) {
359 this.irc_connection
.emit('channel ' + command
.params
[1] + ' topic', {
360 channel
: command
.params
[1],
361 topic
: command
.trailing
365 'RPL_NOTOPIC': function (command
) {
366 this.irc_connection
.emit('channel ' + command
.params
[1] + ' topic', {
367 channel
: command
.params
[1],
372 'RPL_TOPICWHOTIME': function (command
) {
373 this.irc_connection
.emit('channel ' + command
.params
[1] + ' topicsetby', {
374 nick
: command
.params
[2],
375 channel
: command
.params
[1],
376 when
: command
.params
[3]
380 'PING': function (command
) {
381 this.irc_connection
.write('PONG ' + command
.trailing
);
384 'JOIN': function (command
) {
386 if (typeof command
.trailing
=== 'string' && command
.trailing
!== '') {
387 channel
= command
.trailing
;
388 } else if (typeof command
.params
[0] === 'string' && command
.params
[0] !== '') {
389 channel
= command
.params
[0];
392 // Check if we have a server-time
393 time
= getServerTime
.call(this, command
);
395 this.irc_connection
.emit('channel ' + channel
+ ' join', {
397 ident
: command
.ident
,
398 hostname
: command
.hostname
,
404 'PART': function (command
) {
407 // Check if we have a server-time
408 time
= getServerTime
.call(this, command
);
410 this.irc_connection
.emit('channel ' + command
.params
[0] + ' part', {
412 ident
: command
.ident
,
413 hostname
: command
.hostname
,
414 channel
: command
.params
[0],
415 message
: command
.trailing
,
420 'KICK': function (command
) {
423 // Check if we have a server-time
424 time
= getServerTime
.call(this, command
);
426 this.irc_connection
.emit('channel ' + command
.params
[0] + ' kick', {
427 kicked
: command
.params
[1],
429 ident
: command
.ident
,
430 hostname
: command
.hostname
,
431 channel
: command
.params
[0],
432 message
: command
.trailing
,
437 'QUIT': function (command
) {
440 // Check if we have a server-time
441 time
= getServerTime
.call(this, command
);
443 this.irc_connection
.emit('user ' + command
.nick
+ ' quit', {
445 ident
: command
.ident
,
446 hostname
: command
.hostname
,
447 message
: command
.trailing
,
452 'NOTICE': function (command
) {
456 // Check if we have a server-time
457 time
= getServerTime
.call(this, command
);
460 if ((command
.trailing
.charAt(0) === String
.fromCharCode(1)) && (command
.trailing
.charAt(command
.trailing
.length
- 1) === String
.fromCharCode(1))) {
461 // It's a CTCP response
462 namespace = (command
.params
[0].toLowerCase() == this.irc_connection
.nick
.toLowerCase()) ? 'user' : 'channel';
463 this.irc_connection
.emit(namespace + ' ' + command
.params
[0] + ' ctcp_response', {
465 ident
: command
.ident
,
466 hostname
: command
.hostname
,
467 channel
: command
.params
[0],
468 msg
: command
.trailing
.substring(1, command
.trailing
.length
- 1),
472 namespace = (command
.params
[0].toLowerCase() == this.irc_connection
.nick
.toLowerCase() || command
.params
[0] == '*') ?
476 this.irc_connection
.emit(namespace + ' ' + command
.params
[0] + ' notice', {
477 from_server
: command
.prefix
? true : false,
478 nick
: command
.nick
|| command
.prefix
|| undefined,
479 ident
: command
.ident
,
480 hostname
: command
.hostname
,
481 target
: command
.params
[0],
482 msg
: command
.trailing
,
488 'NICK': function (command
) {
491 // Check if we have a server-time
492 time
= getServerTime
.call(this, command
);
494 this.irc_connection
.emit('user ' + command
.nick
+ ' nick', {
496 ident
: command
.ident
,
497 hostname
: command
.hostname
,
498 newnick
: command
.trailing
|| command
.params
[0],
503 'TOPIC': function (command
) {
506 // If we don't have an associated channel, no need to continue
507 if (!command
.params
[0]) return;
509 // Check if we have a server-time
510 time
= getServerTime
.call(this, command
);
512 var channel
= command
.params
[0],
513 topic
= command
.trailing
|| '';
515 this.irc_connection
.emit('channel ' + channel
+ ' topic', {
523 'MODE': function (command
) {
524 var chanmodes
= this.irc_connection
.options
.CHANMODES
|| [],
525 prefixes
= this.irc_connection
.options
.PREFIX
|| [],
526 always_param
= (chanmodes
[0] || '').concat((chanmodes
[1] || '')),
528 has_param
, i
, j
, add
, event
, time
;
530 // Check if we have a server-time
531 time
= getServerTime
.call(this, command
);
533 prefixes
= _
.reduce(prefixes
, function (list
, prefix
) {
534 list
.push(prefix
.mode
);
537 always_param
= always_param
.split('').concat(prefixes
);
539 has_param = function (mode
, add
) {
540 if (_
.find(always_param
, function (m
) {
544 } else if (add
&& _
.find((chanmodes
[2] || '').split(''), function (m
) {
553 if (!command
.params
[1]) {
554 command
.params
[1] = command
.trailing
;
558 for (i
= 0; i
< command
.params
[1].length
; i
++) {
559 switch (command
.params
[1][i
]) {
567 if (has_param(command
.params
[1][i
], add
)) {
568 modes
.push({mode
: (add
? '+' : '-') + command
.params
[1][i
], param
: command
.params
[2 + j
]});
571 modes
.push({mode
: (add
? '+' : '-') + command
.params
[1][i
], param
: null});
576 event
= (_
.contains(this.irc_connection
.options
.CHANTYPES
, command
.params
[0][0]) ? 'channel ' : 'user ') + command
.params
[0] + ' mode';
578 this.irc_connection
.emit(event
, {
579 target
: command
.params
[0],
580 nick
: command
.nick
|| command
.prefix
|| '',
586 'PRIVMSG': function (command
) {
587 var tmp
, namespace, time
;
589 // Check if we have a server-time
590 time
= getServerTime
.call(this, command
);
592 if ((command
.trailing
.charAt(0) === String
.fromCharCode(1)) && (command
.trailing
.charAt(command
.trailing
.length
- 1) === String
.fromCharCode(1))) {
594 if (command
.trailing
.substr(1, 6) === 'ACTION') {
595 this.irc_connection
.clientEvent('action', {
597 ident
: command
.ident
,
598 hostname
: command
.hostname
,
599 channel
: command
.params
[0],
600 msg
: command
.trailing
.substring(8, command
.trailing
.length
- 1),
603 } else if (command
.trailing
.substr(1, 4) === 'KIWI') {
604 tmp
= command
.trailing
.substring(6, command
.trailing
.length
- 1);
605 namespace = tmp
.split(' ', 1)[0];
606 this.irc_connection
.clientEvent('kiwi', {
607 namespace: namespace,
608 data
: tmp
.substr(namespace.length
+ 1),
611 } else if (command
.trailing
.substr(1, 7) === 'VERSION') {
612 this.irc_connection
.write('NOTICE ' + command
.nick
+ ' :' + String
.fromCharCode(1) + 'VERSION KiwiIRC' + String
.fromCharCode(1));
613 } else if (command
.trailing
.substr(1, 6) === 'SOURCE') {
614 this.irc_connection
.write('NOTICE ' + command
.nick
+ ' :' + String
.fromCharCode(1) + 'SOURCE http://www.kiwiirc.com/' + String
.fromCharCode(1));
615 } else if (command
.trailing
.substr(1, 10) === 'CLIENTINFO') {
616 this.irc_connection
.write('NOTICE ' + command
.nick
+ ' :' + String
.fromCharCode(1) + 'CLIENTINFO SOURCE VERSION TIME' + String
.fromCharCode(1));
618 namespace = (command
.params
[0].toLowerCase() == this.irc_connection
.nick
.toLowerCase()) ? 'user' : 'channel';
619 this.irc_connection
.emit(namespace + ' ' + command
.nick
+ ' ctcp_request', {
621 ident
: command
.ident
,
622 hostname
: command
.hostname
,
623 target
: command
.params
[0],
624 type
: (command
.trailing
.substring(1, command
.trailing
.length
- 1).split(' ') || [null])[0],
625 msg
: command
.trailing
.substring(1, command
.trailing
.length
- 1),
630 // A message to a user (private message) or to a channel?
631 namespace = (command
.params
[0].toLowerCase() == this.irc_connection
.nick
.toLowerCase()) ? 'user ' + command
.nick
: 'channel ' + command
.params
[0];
632 this.irc_connection
.emit(namespace + ' privmsg', {
634 ident
: command
.ident
,
635 hostname
: command
.hostname
,
636 channel
: command
.params
[0],
637 msg
: command
.trailing
,
643 'CAP': function (command
) {
644 // TODO: capability modifiers
645 // i.e. - for disable, ~ for requires ACK, = for sticky
646 var capabilities
= command
.trailing
.replace(/(?:^| )[\-~=]/, '').split(' ');
649 // Which capabilities we want to enable
650 var want
= ['multi-prefix', 'away-notify', 'server-time', 'znc.in/server-time-iso', 'znc.in/server-time'];
652 if (this.irc_connection
.password
) {
656 switch (command
.params
[1]) {
658 // Compute which of the available capabilities we want and request them
659 request
= _
.intersection(capabilities
, want
);
660 if (request
.length
> 0) {
661 this.irc_connection
.cap
.requested
= request
;
662 this.irc_connection
.write('CAP REQ :' + request
.join(' '));
664 this.irc_connection
.write('CAP END');
665 this.irc_connection
.cap_negotation
= false;
669 if (capabilities
.length
> 0) {
670 // Update list of enabled capabilities
671 this.irc_connection
.cap
.enabled
= capabilities
;
672 // Update list of capabilities we would like to have but that aren't enabled
673 this.irc_connection
.cap
.requested
= _
.difference(this.irc_connection
.cap
.requested
, capabilities
);
675 if (this.irc_connection
.cap
.enabled
.length
> 0) {
676 if (_
.contains(this.irc_connection
.cap
.enabled
, 'sasl')) {
677 this.irc_connection
.sasl
= true;
678 this.irc_connection
.write('AUTHENTICATE PLAIN');
680 this.irc_connection
.write('CAP END');
681 this.irc_connection
.cap_negotation
= false;
686 if (capabilities
.length
> 0) {
687 this.irc_connection
.cap
.requested
= _
.difference(this.irc_connection
.cap
.requested
, capabilities
);
689 if (this.irc_connection
.cap
.requested
.length
> 0) {
690 this.irc_connection
.write('CAP END');
691 this.irc_connection
.cap_negotation
= false;
695 // should we do anything here?
700 'AUTHENTICATE': function (command
) {
701 var b
= new Buffer(this.irc_connection
.nick
+ "\0" + this.irc_connection
.nick
+ "\0" + this.irc_connection
.password
, 'utf8');
702 var b64
= b
.toString('base64');
703 if (command
.params
[0] === '+') {
704 while (b64
.length
>= 400) {
705 this.irc_connection
.write('AUTHENTICATE ' + b64
.slice(0, 399));
706 b64
= b64
.slice(399);
708 if (b64
.length
> 0) {
709 this.irc_connection
.write('AUTHENTICATE ' + b64
);
711 this.irc_connection
.write('AUTHENTICATE +');
714 this.irc_connection
.write('CAP END');
715 this.irc_connection
.cap_negotation
= false;
719 'AWAY': function (command
) {
722 // Check if we have a server-time
723 time
= getServerTime
.call(this, command
);
725 this.irc_connection
.emit('user ' + command
.nick
+ ' away', {
727 msg
: command
.trailing
,
732 'RPL_SASLAUTHENTICATED': function (command
) {
733 this.irc_connection
.write('CAP END');
734 this.irc_connection
.cap_negotation
= false;
735 this.irc_connection
.sasl
= true;
738 'RPL_SASLLOGGEDIN': function (command
) {
739 if (this.irc_connection
.cap_negotation
=== false) {
740 this.irc_connection
.write('CAP END');
744 'ERR_SASLNOTAUTHORISED': function (command
) {
745 this.irc_connection
.write('CAP END');
746 this.irc_connection
.cap_negotation
= false;
749 'ERR_SASLABORTED': function (command
) {
750 this.irc_connection
.write('CAP END');
751 this.irc_connection
.cap_negotation
= false;
754 'ERR_SASLALREADYAUTHED': function (command
) {
758 'ERROR': function (command
) {
759 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' error', {
760 reason
: command
.trailing
763 ERR_PASSWDMISMATCH: function (command
) {
764 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' password_mismatch', {});
767 ERR_LINKCHANNEL: function (command
) {
768 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' channel_redirect', {
769 from: command
.params
[1],
770 to
: command
.params
[2]
774 ERR_NOSUCHNICK: function (command
) {
775 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' no_such_nick', {
776 nick
: command
.params
[1],
777 reason
: command
.trailing
781 ERR_CANNOTSENDTOCHAN: function (command
) {
782 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' cannot_send_to_chan', {
783 channel
: command
.params
[1],
784 reason
: command
.trailing
788 ERR_TOOMANYCHANNELS: function (command
) {
789 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' too_many_channels', {
790 channel
: command
.params
[1],
791 reason
: command
.trailing
795 ERR_USERNOTINCHANNEL: function (command
) {
796 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' user_not_in_channel', {
797 nick
: command
.params
[0],
798 channel
: command
.params
[1],
799 reason
: command
.trailing
803 ERR_NOTONCHANNEL: function (command
) {
804 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' not_on_channel', {
805 channel
: command
.params
[1],
806 reason
: command
.trailing
810 ERR_USERONCHANNEL: function (command
) {
811 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' user_on_channel', {
812 nick
: command
.params
[1],
813 channel
: command
.params
[2]
817 ERR_CHANNELISFULL: function (command
) {
818 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' channel_is_full', {
819 channel
: command
.params
[1],
820 reason
: command
.trailing
824 ERR_INVITEONLYCHAN: function (command
) {
825 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' invite_only_channel', {
826 channel
: command
.params
[1],
827 reason
: command
.trailing
831 ERR_BANNEDFROMCHAN: function (command
) {
832 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' banned_from_channel', {
833 channel
: command
.params
[1],
834 reason
: command
.trailing
838 ERR_BADCHANNELKEY: function (command
) {
839 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' bad_channel_key', {
840 channel
: command
.params
[1],
841 reason
: command
.trailing
845 ERR_CHANOPRIVSNEEDED: function (command
) {
846 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' chanop_privs_needed', {
847 channel
: command
.params
[1],
848 reason
: command
.trailing
852 ERR_NICKNAMEINUSE: function (command
) {
853 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' nickname_in_use', {
854 nick
: command
.params
[1],
855 reason
: command
.trailing
859 ERR_ERRONEUSNICKNAME: function(command
) {
860 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' erroneus_nickname', {
861 nick
: command
.params
[1],
862 reason
: command
.trailing
866 ERR_NOTREGISTERED: function (command
) {
869 RPL_MAPMORE: function (command
) {
870 var params
= _
.clone(command
.params
);
872 genericNotice
.call(this, command
, params
.join(', ') + ' ' + command
.trailing
);
875 RPL_MAPEND: function (command
) {
876 var params
= _
.clone(command
.params
);
878 genericNotice
.call(this, command
, params
.join(', ') + ' ' + command
.trailing
);
881 RPL_LINKS: function (command
) {
882 var params
= _
.clone(command
.params
);
884 genericNotice
.call(this, command
, params
.join(', ') + ' ' + command
.trailing
);
887 RPL_ENDOFLINKS: function (command
) {
888 var params
= _
.clone(command
.params
);
890 genericNotice
.call(this, command
, params
.join(', ') + ' ' + command
.trailing
);
893 ERR_UNKNOWNCOMMAND: function (command
) {
894 var params
= _
.clone(command
.params
);
896 genericNotice
.call(this, command
, '`' + params
.join(', ') + '` ' + command
.trailing
);
899 ERR_NOMOTD: function (command
) {
900 var params
= _
.clone(command
.params
);
902 genericNotice
.call(this, command
, command
.trailing
);
905 ERR_NOPRIVILEGES: function (command
) {
906 var params
= _
.clone(command
.params
);
908 genericNotice
.call(this, command
, command
.trailing
);
911 RPL_STATSCONN: function (command
) {
912 var params
= _
.clone(command
.params
);
914 genericNotice
.call(this, command
, params
.join(', ') + ' ' + command
.trailing
);
917 RPL_LUSERCLIENT: function (command
) {
918 var params
= _
.clone(command
.params
);
920 genericNotice
.call(this, command
, params
.join(', ') + ' ' + command
.trailing
);
923 RPL_LUSEROP: function (command
) {
924 var params
= _
.clone(command
.params
);
926 genericNotice
.call(this, command
, params
.join(', ') + ' ' + command
.trailing
);
929 RPL_LUSERUNKNOWN: function (command
) {
930 var params
= _
.clone(command
.params
);
932 genericNotice
.call(this, command
, params
.join(', ') + ' ' + command
.trailing
);
935 RPL_LUSERCHANNELS: function (command
) {
936 var params
= _
.clone(command
.params
);
938 genericNotice
.call(this, command
, params
.join(', ') + ' ' + command
.trailing
);
941 RPL_LUSERME: function (command
) {
942 var params
= _
.clone(command
.params
);
944 genericNotice
.call(this, command
, params
.join(', ') + ' ' + command
.trailing
);
947 RPL_LOCALUSERS: function (command
) {
948 var params
= _
.clone(command
.params
);
950 genericNotice
.call(this, command
, params
.join(', ') + ' ' + command
.trailing
);
953 RPL_GLOBALUSERS: function (command
) {
954 var params
= _
.clone(command
.params
);
956 genericNotice
.call(this, command
, params
.join(', ') + ' ' + command
.trailing
);
963 function genericNotice (command
, msg
, is_error
) {
964 // Default to being an error
965 if (typeof is_error
!== 'boolean')
968 this.irc_connection
.clientEvent('notice', {
970 nick
: command
.prefix
,
973 target
: command
.params
[0],
975 numeric
: parseInt(command
.command
, 10)
980 function getServerTime(command
) {
983 // No tags? No times.
984 if (!command
.tags
|| command
.tags
.length
=== 0) {
988 if (capContainsAny
.call(this, ['server-time', 'znc.in/server-time', 'znc.in/server-time-iso'])) {
989 time
= _
.find(command
.tags
, function (tag
) {
990 return tag
.tag
=== 'time';
993 time
= time
? time
.value
: undefined;
995 // Convert the time value to a unixtimestamp
996 if (typeof time
=== 'string') {
997 if (time
.indexOf('T') > -1) {
998 time
= parseISO8601(opts
.time
);
1000 } else if(time
.match(/^[0-9.]+$/)) {
1001 // A string formatted unix timestamp
1002 time
= new Date(time
* 1000);
1005 time
= time
.getTime();
1007 } else if (typeof time
=== 'number') {
1008 time
= new Date(time
* 1000);
1009 time
= time
.getTime();
1017 function capContainsAny (caps
) {
1019 if (!caps
instanceof Array
) {
1022 intersection
= _
.intersection(this.irc_connection
.cap
.enabled
, caps
);
1023 return intersection
.length
> 0;
1027 // Code based on http://anentropic.wordpress.com/2009/06/25/javascript-iso8601-parser-and-pretty-dates/#comment-154
1028 function parseISO8601(str
) {
1029 if (Date
.prototype.toISOString
) {
1030 return new Date(str
);
1032 var parts
= str
.split('T'),
1033 dateParts
= parts
[0].split('-'),
1034 timeParts
= parts
[1].split('Z'),
1035 timeSubParts
= timeParts
[0].split(':'),
1036 timeSecParts
= timeSubParts
[2].split('.'),
1037 timeHours
= Number(timeSubParts
[0]),
1040 _date
.setUTCFullYear(Number(dateParts
[0]));
1041 _date
.setUTCDate(1);
1042 _date
.setUTCMonth(Number(dateParts
[1])-1);
1043 _date
.setUTCDate(Number(dateParts
[2]));
1044 _date
.setUTCHours(Number(timeHours
));
1045 _date
.setUTCMinutes(Number(timeSubParts
[1]));
1046 _date
.setUTCSeconds(Number(timeSecParts
[0]));
1047 if (timeSecParts
[1]) {
1048 _date
.setUTCMilliseconds(Number(timeSecParts
[1]));