cf9c0acac9c1ffc2271ec53e81a3932f58b99500
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 '310': 'RPL_WHOIHELPOP',
24 '311': 'RPL_WHOISUSER',
25 '312': 'RPL_WHOISSERVER',
26 '313': 'RPL_WHOISOPERATOR',
27 '314': 'RPL_WHOWASUSER',
28 '315': 'RPL_ENDOFWHO',
29 '317': 'RPL_WHOISIDLE',
30 '318': 'RPL_ENDOFWHOIS',
31 '319': 'RPL_WHOISCHANNELS',
32 '321': 'RPL_LISTSTART',
35 '324': 'RPL_CHANNELMODEIS',
36 '328': 'RPL_CHANNEL_URL',
37 '329': 'RPL_CREATIONTIME',
38 '330': 'RPL_WHOISACCOUNT',
41 '333': 'RPL_TOPICWHOTIME',
42 '335': 'RPL_WHOISBOT',
43 '341': 'RPL_INVITING',
44 '352': 'RPL_WHOREPLY',
45 '353': 'RPL_NAMEREPLY',
47 '365': 'RPL_ENDOFLINKS',
48 '366': 'RPL_ENDOFNAMES',
50 '368': 'RPL_ENDOFBANLIST',
51 '369': 'RPL_ENDOFWHOWAS',
53 '375': 'RPL_MOTDSTART',
54 '376': 'RPL_ENDOFMOTD',
55 '378': 'RPL_WHOISHOST',
56 '379': 'RPL_WHOISMODES',
57 '396': 'RPL_HOSTCLOACKING',
58 '401': 'ERR_NOSUCHNICK',
59 '404': 'ERR_CANNOTSENDTOCHAN',
60 '405': 'ERR_TOOMANYCHANNELS',
61 '406': 'ERR_WASNOSUCHNICK',
62 '421': 'ERR_UNKNOWNCOMMAND',
64 '432': 'ERR_ERRONEUSNICKNAME',
65 '433': 'ERR_NICKNAMEINUSE',
66 '441': 'ERR_USERNOTINCHANNEL',
67 '442': 'ERR_NOTONCHANNEL',
68 '443': 'ERR_USERONCHANNEL',
69 '451': 'ERR_NOTREGISTERED',
70 '464': 'ERR_PASSWDMISMATCH',
71 '470': 'ERR_LINKCHANNEL',
72 '471': 'ERR_CHANNELISFULL',
73 '473': 'ERR_INVITEONLYCHAN',
74 '474': 'ERR_BANNEDFROMCHAN',
75 '475': 'ERR_BADCHANNELKEY',
76 '481': 'ERR_NOPRIVILEGES',
77 '482': 'ERR_CHANOPRIVSNEEDED',
78 '670': 'RPL_STARTTLS',
79 '671': 'RPL_WHOISSECURE',
80 '900': 'RPL_SASLAUTHENTICATED',
81 '903': 'RPL_SASLLOGGEDIN',
82 '904': 'ERR_SASLNOTAUTHORISED',
83 '906': 'ERR_SASLABORTED',
84 '907': 'ERR_SASLALREADYAUTHED'
88 IrcCommands = function (irc_connection
) {
89 this.irc_connection
= irc_connection
;
91 module
.exports
= IrcCommands
;
93 IrcCommands
.prototype.dispatch = function (command
, data
) {
95 if (irc_numerics
[command
]) {
96 command
= irc_numerics
[command
];
98 if (handlers
[command
]) {
99 handlers
[command
].call(this, data
);
101 unknownCommand
.call(this, command
, data
);
105 IrcCommands
.addHandler = function (command
, handler
) {
106 if (typeof handler
!== 'function') {
109 handlers
[command
] = handler
;
112 IrcCommands
.addNumeric = function (numeric
, handler_name
) {
113 irc_numerics
[numeric
+ ''] = handler_name
+'';
116 unknownCommand = function (command
, data
) {
117 var params
= _
.clone(data
.params
);
119 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' unknown_command', {
127 'RPL_WELCOME': function (command
) {
128 var nick
= command
.params
[0];
129 this.irc_connection
.registered
= true;
130 this.cap_negotiation
= false;
131 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' connect', {
136 'RPL_ISUPPORT': function (command
) {
137 var options
, i
, option
, matches
, j
;
138 options
= command
.params
;
139 for (i
= 1; i
< options
.length
; i
++) {
140 option
= options
[i
].split("=", 2);
141 option
[0] = option
[0].toUpperCase();
142 this.irc_connection
.options
[option
[0]] = (typeof option
[1] !== 'undefined') ? option
[1] : true;
143 if (_
.include(['NETWORK', 'PREFIX', 'CHANTYPES', 'CHANMODES', 'NAMESX'], option
[0])) {
144 if (option
[0] === 'PREFIX') {
145 matches
= /\(([^)]*)\)(.*)/.exec(option
[1]);
146 if ((matches
) && (matches
.length
=== 3)) {
147 this.irc_connection
.options
.PREFIX
= [];
148 for (j
= 0; j
< matches
[2].length
; j
++) {
149 this.irc_connection
.options
.PREFIX
.push({symbol
: matches
[2].charAt(j
), mode
: matches
[1].charAt(j
)});
152 } else if (option
[0] === 'CHANTYPES') {
153 this.irc_connection
.options
.CHANTYPES
= this.irc_connection
.options
.CHANTYPES
.split('');
154 } else if (option
[0] === 'CHANMODES') {
155 this.irc_connection
.options
.CHANMODES
= option
[1].split(',');
156 } else if ((option
[0] === 'NAMESX') && (!_
.contains(this.irc_connection
.cap
.enabled
, 'multi-prefix'))) {
157 this.irc_connection
.write('PROTOCTL NAMESX');
161 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' options', {
162 options
: this.irc_connection
.options
,
163 cap
: this.irc_connection
.cap
.enabled
167 'RPL_ENDOFWHOIS': function (command
) {
168 this.irc_connection
.emit('user ' + command
.params
[1] + ' endofwhois', {
169 nick
: command
.params
[1],
170 msg
: command
.params
[command
.params
.length
- 1]
174 'RPL_AWAY': function (command
) {
175 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoisaway', {
176 nick
: command
.params
[1],
177 reason
: command
.params
[command
.params
.length
- 1]
181 'RPL_WHOISUSER': function (command
) {
182 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoisuser', {
183 nick
: command
.params
[1],
184 ident
: command
.params
[2],
185 host
: command
.params
[3],
186 msg
: command
.params
[command
.params
.length
- 1]
190 'RPL_WHOISHELPOP': function (command
) {
191 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoishelpop', {
192 nick
: command
.params
[1],
193 msg
: command
.params
[command
.params
.length
- 1]
197 'RPL_WHOISBOT': function (command
) {
198 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoisbot', {
199 nick
: command
.params
[1],
200 msg
: command
.params
[command
.params
.length
- 1]
204 'RPL_WHOISSERVER': function (command
) {
205 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoisserver', {
206 nick
: command
.params
[1],
207 irc_server
: command
.params
[2],
208 server_info
: command
.params
[command
.params
.length
- 1]
212 'RPL_WHOISOPERATOR': function (command
) {
213 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoisoperator', {
214 nick
: command
.params
[1],
215 msg
: command
.params
[command
.params
.length
- 1]
219 'RPL_WHOISCHANNELS': function (command
) {
220 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoischannels', {
221 nick
: command
.params
[1],
222 chans
: command
.params
[command
.params
.length
- 1]
226 'RPL_WHOISMODES': function (command
) {
227 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoismodes', {
228 nick
: command
.params
[1],
229 msg
: command
.params
[command
.params
.length
- 1]
233 'RPL_WHOISIDLE': function (command
) {
234 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoisidle', {
235 nick
: command
.params
[1],
236 idle
: command
.params
[2],
237 logon
: command
.params
[3] || undefined
241 'RPL_WHOISREGNICK': function (command
) {
242 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoisregnick', {
243 nick
: command
.params
[1],
244 msg
: command
.params
[command
.params
.length
- 1]
248 'RPL_WHOISHOST': function (command
) {
249 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoishost', {
250 nick
: command
.params
[1],
251 msg
: command
.params
[command
.params
.length
- 1]
255 'RPL_WHOISSECURE': function (command
) {
256 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoissecure', {
257 nick
: command
.params
[1]
261 'RPL_WHOISACCOUNT': function (command
) {
262 this.irc_connection
.emit('user ' + command
.params
[1] + ' whoisaccount', {
263 nick
: command
.params
[1],
264 account
: command
.params
[2]
268 'RPL_WHOWASUSER': function (command
) {
269 this.irc_connection
.emit('user ' + command
.params
[1] + ' whowas', {
270 nick
: command
.params
[1],
271 ident
: command
.params
[2],
272 host
: command
.params
[3],
273 real_name
: command
.params
[command
.params
.length
- 1]
277 'RPL_ENDOFWHOWAS': function (command
) {
278 this.irc_connection
.emit('user ' + command
.params
[1] + ' endofwhowas', {
279 nick
: command
.params
[1]
283 'ERR_WASNOSUCHNICK': function (command
) {
284 this.irc_connection
.emit('user ' + command
.params
[1] + ' wasnosucknick', {
285 nick
: command
.params
[1]
289 'RPL_LISTSTART': function (command
) {
290 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' list_start', {});
293 'RPL_LISTEND': function (command
) {
294 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' list_end', {});
297 'RPL_LIST': function (command
) {
298 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' list_channel', {
299 channel
: command
.params
[1],
300 num_users
: parseInt(command
.params
[2], 10),
301 topic
: command
.params
[3] || ''
305 'RPL_CHANNELMODEIS': function (command
) {
306 var channel
= command
.params
[1],
307 modes
= parseModeList
.call(this, command
.params
[2], command
.params
.slice(3));
309 this.irc_connection
.emit('channel ' + channel
+ ' info', {
315 'RPL_CREATIONTIME': function (command
) {
316 var channel
= command
.params
[1];
318 this.irc_connection
.emit('channel ' + channel
+ ' info', {
320 created_at
: parseInt(command
.params
[2], 10)
324 'RPL_CHANNEL_URL': function (command
) {
325 var channel
= command
.params
[1];
327 this.irc_connection
.emit('channel ' + channel
+ ' info', {
329 url
: command
.params
[command
.params
.length
- 1]
333 'RPL_MOTD': function (command
) {
334 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' motd', {
335 motd
: command
.params
[command
.params
.length
- 1] + '\n'
339 'RPL_MOTDSTART': function (command
) {
340 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' motd_start', {});
343 'RPL_ENDOFMOTD': function (command
) {
344 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' motd_end', {});
347 'RPL_NAMEREPLY': function (command
) {
348 var members
= command
.params
[command
.params
.length
- 1].split(' ');
349 var member_list
= [];
351 _
.each(members
, function (member
) {
356 // Make sure we have some prefixes already
357 if (that
.irc_connection
.options
.PREFIX
) {
358 for (j
= 0; j
< that
.irc_connection
.options
.PREFIX
.length
; j
++) {
359 if (member
.charAt(i
) === that
.irc_connection
.options
.PREFIX
[j
].symbol
) {
360 modes
.push(that
.irc_connection
.options
.PREFIX
[j
].mode
);
366 member_list
.push({nick
: member
, modes
: modes
});
369 this.irc_connection
.emit('channel ' + command
.params
[2] + ' userlist', {
371 channel
: command
.params
[2]
375 'RPL_ENDOFNAMES': function (command
) {
376 this.irc_connection
.emit('channel ' + command
.params
[1] + ' userlist_end', {
377 channel
: command
.params
[1]
381 'RPL_WHOREPLY': function (command
) {
382 // For the time being, NOOP this command so they don't get passed
383 // down to the client. Waste of bandwidth since we do not use it yet
384 // TODO: Impliment RPL_WHOREPLY
387 'RPL_ENDOFWHO': function (command
) {
388 // For the time being, NOOP this command so they don't get passed
389 // down to the client. Waste of bandwidth since we do not use it yet
390 // TODO: Impliment RPL_ENDOFWHO
393 'RPL_BANLIST': function (command
) {
394 this.irc_connection
.emit('channel ' + command
.params
[1] + ' banlist', {
395 channel
: command
.params
[1],
396 banned
: command
.params
[2],
397 banned_by
: command
.params
[3],
398 banned_at
: command
.params
[4]
402 'RPL_ENDOFBANLIST': function (command
) {
403 this.irc_connection
.emit('channel ' + command
.params
[1] + ' banlist_end', {
404 channel
: command
.params
[1]
408 'RPL_TOPIC': function (command
) {
409 this.irc_connection
.emit('channel ' + command
.params
[1] + ' topic', {
410 channel
: command
.params
[1],
411 topic
: command
.params
[command
.params
.length
- 1]
415 'RPL_NOTOPIC': function (command
) {
416 this.irc_connection
.emit('channel ' + command
.params
[1] + ' topic', {
417 channel
: command
.params
[1],
422 'RPL_TOPICWHOTIME': function (command
) {
423 this.irc_connection
.emit('channel ' + command
.params
[1] + ' topicsetby', {
424 nick
: command
.params
[2],
425 channel
: command
.params
[1],
426 when
: command
.params
[3]
430 'RPL_INVITING': function (command
) {
431 this.irc_connection
.emit('channel ' + command
.params
[1] + ' invited', {
432 nick
: command
.params
[0],
433 channel
: command
.params
[1]
437 'PING': function (command
) {
438 this.irc_connection
.write('PONG ' + command
.params
[command
.params
.length
- 1]);
441 'JOIN': function (command
) {
443 if (typeof command
.params
[0] === 'string' && command
.params
[0] !== '') {
444 channel
= command
.params
[0];
447 // Check if we have a server-time
448 time
= getServerTime
.call(this, command
);
450 this.irc_connection
.emit('channel ' + channel
+ ' join', {
452 ident
: command
.ident
,
453 hostname
: command
.hostname
,
459 'PART': function (command
) {
460 var time
, channel
, message
;
462 // Check if we have a server-time
463 time
= getServerTime
.call(this, command
);
465 channel
= command
.params
[0];
466 if (command
.params
.length
> 1) {
467 message
= command
.params
[command
.params
.length
- 1];
470 this.irc_connection
.emit('channel ' + channel
+ ' part', {
472 ident
: command
.ident
,
473 hostname
: command
.hostname
,
480 'KICK': function (command
) {
483 // Check if we have a server-time
484 time
= getServerTime
.call(this, command
);
486 this.irc_connection
.emit('channel ' + command
.params
[0] + ' kick', {
487 kicked
: command
.params
[1],
489 ident
: command
.ident
,
490 hostname
: command
.hostname
,
491 channel
: command
.params
[0],
492 message
: command
.params
[command
.params
.length
- 1],
497 'QUIT': function (command
) {
500 // Check if we have a server-time
501 time
= getServerTime
.call(this, command
);
503 this.irc_connection
.emit('user ' + command
.nick
+ ' quit', {
505 ident
: command
.ident
,
506 hostname
: command
.hostname
,
507 message
: command
.params
[command
.params
.length
- 1],
512 'NOTICE': function (command
) {
517 // Check if we have a server-time
518 time
= getServerTime
.call(this, command
);
520 msg
= command
.params
[command
.params
.length
- 1];
521 if ((msg
.charAt(0) === String
.fromCharCode(1)) && (msg
.charAt(msg
.length
- 1) === String
.fromCharCode(1))) {
522 // It's a CTCP response
523 namespace = (command
.params
[0].toLowerCase() === this.irc_connection
.nick
.toLowerCase()) ? 'user' : 'channel';
524 this.irc_connection
.emit(namespace + ' ' + command
.params
[0] + ' ctcp_response', {
526 ident
: command
.ident
,
527 hostname
: command
.hostname
,
528 channel
: command
.params
[0],
529 msg
: msg
.substring(1, msg
.length
- 1),
533 namespace = (command
.params
[0].toLowerCase() === this.irc_connection
.nick
.toLowerCase() || command
.params
[0] === '*') ?
537 this.irc_connection
.emit(namespace + ' ' + command
.params
[0] + ' notice', {
538 from_server
: command
.prefix
? true : false,
539 nick
: command
.nick
|| command
.prefix
|| undefined,
540 ident
: command
.ident
,
541 hostname
: command
.hostname
,
542 target
: command
.params
[0],
549 'NICK': function (command
) {
552 // Check if we have a server-time
553 time
= getServerTime
.call(this, command
);
555 this.irc_connection
.emit('user ' + command
.nick
+ ' nick', {
557 ident
: command
.ident
,
558 hostname
: command
.hostname
,
559 newnick
: command
.params
[0],
564 'TOPIC': function (command
) {
567 // If we don't have an associated channel, no need to continue
568 if (!command
.params
[0]) {
572 // Check if we have a server-time
573 time
= getServerTime
.call(this, command
);
575 var channel
= command
.params
[0],
576 topic
= command
.params
[command
.params
.length
- 1] || '';
578 this.irc_connection
.emit('channel ' + channel
+ ' topic', {
586 'MODE': function (command
) {
587 var modes
= [], event
, time
;
589 // Check if we have a server-time
590 time
= getServerTime
.call(this, command
);
592 // Get a JSON representation of the modes
593 modes
= parseModeList
.call(this, command
.params
[1], command
.params
.slice(2));
594 event
= (_
.contains(this.irc_connection
.options
.CHANTYPES
, command
.params
[0][0]) ? 'channel ' : 'user ') + command
.params
[0] + ' mode';
596 this.irc_connection
.emit(event
, {
597 target
: command
.params
[0],
598 nick
: command
.nick
|| command
.prefix
|| '',
604 'PRIVMSG': function (command
) {
605 var tmp
, namespace, time
, msg
, version_string
, client_info
;
607 // Check if we have a server-time
608 time
= getServerTime
.call(this, command
);
610 msg
= command
.params
[command
.params
.length
- 1];
611 if ((msg
.charAt(0) === String
.fromCharCode(1)) && (msg
.charAt(msg
.length
- 1) === String
.fromCharCode(1))) {
613 if (msg
.substr(1, 6) === 'ACTION') {
614 this.irc_connection
.clientEvent('action', {
616 ident
: command
.ident
,
617 hostname
: command
.hostname
,
618 channel
: command
.params
[0],
619 msg
: msg
.substring(8, msg
.length
- 1),
622 } else if (msg
.substr(1, 4) === 'KIWI') {
623 tmp
= msg
.substring(6, msg
.length
- 1);
624 namespace = tmp
.split(' ', 1)[0];
625 this.irc_connection
.clientEvent('kiwi', {
626 namespace: namespace,
627 data
: tmp
.substr(namespace.length
+ 1),
630 } else if (msg
.substr(1, 7) === 'VERSION') {
631 client_info
= this.irc_connection
.state
.client
.client_info
;
632 version_string
= global
.build_version
;
634 // If the client build_version differs from the server, add this to the version_string
635 if (client_info
&& client_info
.build_version
!== global
.build_version
) {
636 version_string
+= ', client build: ' + client_info
.build_version
;
639 version_string
= 'KiwiIRC (' + version_string
+ ')';
641 this.irc_connection
.write('NOTICE ' + command
.nick
+ ' :' + String
.fromCharCode(1) + 'VERSION ' + version_string
+ String
.fromCharCode(1));
642 } else if (msg
.substr(1, 6) === 'SOURCE') {
643 this.irc_connection
.write('NOTICE ' + command
.nick
+ ' :' + String
.fromCharCode(1) + 'SOURCE http://www.kiwiirc.com/' + String
.fromCharCode(1));
644 } else if (msg
.substr(1, 10) === 'CLIENTINFO') {
645 this.irc_connection
.write('NOTICE ' + command
.nick
+ ' :' + String
.fromCharCode(1) + 'CLIENTINFO SOURCE VERSION TIME' + String
.fromCharCode(1));
647 namespace = (command
.params
[0].toLowerCase() === this.irc_connection
.nick
.toLowerCase()) ? 'user' : 'channel';
648 this.irc_connection
.emit(namespace + ' ' + command
.nick
+ ' ctcp_request', {
650 ident
: command
.ident
,
651 hostname
: command
.hostname
,
652 target
: command
.params
[0],
653 type
: (msg
.substring(1, msg
.length
- 1).split(' ') || [null])[0],
654 msg
: msg
.substring(1, msg
.length
- 1),
659 // A message to a user (private message) or to a channel?
660 namespace = (command
.params
[0].toLowerCase() === this.irc_connection
.nick
.toLowerCase()) ? 'user ' + command
.nick
: 'channel ' + command
.params
[0];
661 this.irc_connection
.emit(namespace + ' privmsg', {
663 ident
: command
.ident
,
664 hostname
: command
.hostname
,
665 channel
: command
.params
[0],
672 'CAP': function (command
) {
673 // TODO: capability modifiers
674 // i.e. - for disable, ~ for requires ACK, = for sticky
675 var capabilities
= command
.params
[command
.params
.length
- 1].replace(/(?:^| )[\-~=]/, '').split(' ');
678 // Which capabilities we want to enable
679 var want
= ['multi-prefix', 'away-notify', 'server-time', 'znc.in/server-time-iso', 'znc.in/server-time'];
681 if (this.irc_connection
.password
) {
685 switch (command
.params
[1]) {
687 // Compute which of the available capabilities we want and request them
688 request
= _
.intersection(capabilities
, want
);
689 if (request
.length
> 0) {
690 this.irc_connection
.cap
.requested
= request
;
691 this.irc_connection
.write('CAP REQ :' + request
.join(' '));
693 this.irc_connection
.write('CAP END');
694 this.irc_connection
.cap_negotiation
= false;
698 if (capabilities
.length
> 0) {
699 // Update list of enabled capabilities
700 this.irc_connection
.cap
.enabled
= capabilities
;
701 // Update list of capabilities we would like to have but that aren't enabled
702 this.irc_connection
.cap
.requested
= _
.difference(this.irc_connection
.cap
.requested
, capabilities
);
704 if (this.irc_connection
.cap
.enabled
.length
> 0) {
705 if (_
.contains(this.irc_connection
.cap
.enabled
, 'sasl')) {
706 this.irc_connection
.sasl
= true;
707 this.irc_connection
.write('AUTHENTICATE PLAIN');
709 this.irc_connection
.write('CAP END');
710 this.irc_connection
.cap_negotiation
= false;
715 if (capabilities
.length
> 0) {
716 this.irc_connection
.cap
.requested
= _
.difference(this.irc_connection
.cap
.requested
, capabilities
);
718 if (this.irc_connection
.cap
.requested
.length
> 0) {
719 this.irc_connection
.write('CAP END');
720 this.irc_connection
.cap_negotiation
= false;
724 // should we do anything here?
729 'AUTHENTICATE': function (command
) {
730 var b
= new Buffer(this.irc_connection
.nick
+ "\0" + this.irc_connection
.nick
+ "\0" + this.irc_connection
.password
, 'utf8');
731 var b64
= b
.toString('base64');
732 if (command
.params
[0] === '+') {
733 while (b64
.length
>= 400) {
734 this.irc_connection
.write('AUTHENTICATE ' + b64
.slice(0, 399));
735 b64
= b64
.slice(399);
737 if (b64
.length
> 0) {
738 this.irc_connection
.write('AUTHENTICATE ' + b64
);
740 this.irc_connection
.write('AUTHENTICATE +');
743 this.irc_connection
.write('CAP END');
744 this.irc_connection
.cap_negotiation
= false;
748 'AWAY': function (command
) {
751 // Check if we have a server-time
752 time
= getServerTime
.call(this, command
);
754 this.irc_connection
.emit('user ' + command
.nick
+ ' away', {
756 msg
: command
.params
[command
.params
.length
- 1],
761 'RPL_SASLAUTHENTICATED': function (command
) {
762 this.irc_connection
.write('CAP END');
763 this.irc_connection
.cap_negotiation
= false;
764 this.irc_connection
.sasl
= true;
767 'RPL_SASLLOGGEDIN': function (command
) {
768 if (this.irc_connection
.cap_negotiation
=== true) {
769 this.irc_connection
.write('CAP END');
770 this.irc_connection
.cap_negotiation
= false;
774 'ERR_SASLNOTAUTHORISED': function (command
) {
775 this.irc_connection
.write('CAP END');
776 this.irc_connection
.cap_negotiation
= false;
779 'ERR_SASLABORTED': function (command
) {
780 this.irc_connection
.write('CAP END');
781 this.irc_connection
.cap_negotiation
= false;
784 'ERR_SASLALREADYAUTHED': function (command
) {
788 'ERROR': function (command
) {
789 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' error', {
790 reason
: command
.params
[command
.params
.length
- 1]
793 ERR_PASSWDMISMATCH: function (command
) {
794 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' password_mismatch', {});
797 ERR_LINKCHANNEL: function (command
) {
798 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' channel_redirect', {
799 from: command
.params
[1],
800 to
: command
.params
[2]
804 ERR_NOSUCHNICK: function (command
) {
805 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' no_such_nick', {
806 nick
: command
.params
[1],
807 reason
: command
.params
[command
.params
.length
- 1]
811 ERR_CANNOTSENDTOCHAN: function (command
) {
812 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' cannot_send_to_chan', {
813 channel
: command
.params
[1],
814 reason
: command
.params
[command
.params
.length
- 1]
818 ERR_TOOMANYCHANNELS: function (command
) {
819 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' too_many_channels', {
820 channel
: command
.params
[1],
821 reason
: command
.params
[command
.params
.length
- 1]
825 ERR_USERNOTINCHANNEL: function (command
) {
826 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' user_not_in_channel', {
827 nick
: command
.params
[0],
828 channel
: command
.params
[1],
829 reason
: command
.params
[command
.params
.length
- 1]
833 ERR_NOTONCHANNEL: function (command
) {
834 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' not_on_channel', {
835 channel
: command
.params
[1],
836 reason
: command
.params
[command
.params
.length
- 1]
840 ERR_USERONCHANNEL: function (command
) {
841 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' user_on_channel', {
842 nick
: command
.params
[1],
843 channel
: command
.params
[2]
847 ERR_CHANNELISFULL: function (command
) {
848 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' channel_is_full', {
849 channel
: command
.params
[1],
850 reason
: command
.params
[command
.params
.length
- 1]
854 ERR_INVITEONLYCHAN: function (command
) {
855 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' invite_only_channel', {
856 channel
: command
.params
[1],
857 reason
: command
.params
[command
.params
.length
- 1]
861 ERR_BANNEDFROMCHAN: function (command
) {
862 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' banned_from_channel', {
863 channel
: command
.params
[1],
864 reason
: command
.params
[command
.params
.length
- 1]
868 ERR_BADCHANNELKEY: function (command
) {
869 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' bad_channel_key', {
870 channel
: command
.params
[1],
871 reason
: command
.params
[command
.params
.length
- 1]
875 ERR_CHANOPRIVSNEEDED: function (command
) {
876 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' chanop_privs_needed', {
877 channel
: command
.params
[1],
878 reason
: command
.params
[command
.params
.length
- 1]
882 ERR_NICKNAMEINUSE: function (command
) {
883 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' nickname_in_use', {
884 nick
: command
.params
[1],
885 reason
: command
.params
[command
.params
.length
- 1]
889 ERR_ERRONEUSNICKNAME: function(command
) {
890 this.irc_connection
.emit('server ' + this.irc_connection
.irc_host
.hostname
+ ' erroneus_nickname', {
891 nick
: command
.params
[1],
892 reason
: command
.params
[command
.params
.length
- 1]
896 ERR_NOTREGISTERED: function (command
) {
899 RPL_MAPMORE: function (command
) {
900 var params
= _
.clone(command
.params
);
902 genericNotice
.call(this, command
, params
.slice(0, -1).join(', ') + ' ' + command
.params
[command
.params
.length
- 1]);
905 RPL_MAPEND: function (command
) {
906 var params
= _
.clone(command
.params
);
908 genericNotice
.call(this, command
, params
.slice(0, -1).join(', ') + ' ' + command
.params
[command
.params
.length
- 1]);
911 RPL_LINKS: function (command
) {
912 var params
= _
.clone(command
.params
);
914 genericNotice
.call(this, command
, params
.slice(0, -1).join(', ') + ' ' + command
.params
[command
.params
.length
- 1]);
917 RPL_ENDOFLINKS: function (command
) {
918 var params
= _
.clone(command
.params
);
920 genericNotice
.call(this, command
, params
.slice(0, -1).join(', ') + ' ' + command
.params
[command
.params
.length
- 1]);
923 ERR_UNKNOWNCOMMAND: function (command
) {
924 var params
= _
.clone(command
.params
);
926 genericNotice
.call(this, command
, '`' + params
.slice(0, -1).join(', ') + '` ' + command
.params
[command
.params
.length
- 1]);
929 ERR_NOMOTD: function (command
) {
930 var params
= _
.clone(command
.params
);
932 genericNotice
.call(this, command
, command
.params
[command
.params
.length
- 1]);
935 ERR_NOPRIVILEGES: function (command
) {
936 var params
= _
.clone(command
.params
);
938 genericNotice
.call(this, command
, command
.params
[command
.params
.length
- 1]);
941 RPL_STATSCONN: function (command
) {
942 var params
= _
.clone(command
.params
);
944 genericNotice
.call(this, command
, params
.slice(0, -1).join(', ') + ' ' + command
.params
[command
.params
.length
- 1]);
947 RPL_LUSERCLIENT: function (command
) {
948 var params
= _
.clone(command
.params
);
950 genericNotice
.call(this, command
, params
.slice(0, -1).join(', ') + ' ' + command
.params
[command
.params
.length
- 1]);
953 RPL_LUSEROP: function (command
) {
954 var params
= _
.clone(command
.params
);
956 genericNotice
.call(this, command
, params
.slice(0, -1).join(', ') + ' ' + command
.params
[command
.params
.length
- 1]);
959 RPL_LUSERUNKNOWN: function (command
) {
960 var params
= _
.clone(command
.params
);
962 genericNotice
.call(this, command
, params
.slice(0, -1).join(', ') + ' ' + command
.params
[command
.params
.length
- 1]);
965 RPL_LUSERCHANNELS: function (command
) {
966 var params
= _
.clone(command
.params
);
968 genericNotice
.call(this, command
, params
.slice(0, -1).join(', ') + ' ' + command
.params
[command
.params
.length
- 1]);
971 RPL_LUSERME: function (command
) {
972 var params
= _
.clone(command
.params
);
974 genericNotice
.call(this, command
, params
.slice(0, -1).join(', ') + ' ' + command
.params
[command
.params
.length
- 1]);
977 RPL_LOCALUSERS: function (command
) {
978 var params
= _
.clone(command
.params
);
980 genericNotice
.call(this, command
, params
.slice(0, -1).join(', ') + ' ' + command
.params
[command
.params
.length
- 1]);
983 RPL_GLOBALUSERS: function (command
) {
984 var params
= _
.clone(command
.params
);
986 genericNotice
.call(this, command
, params
.slice(0, -1).join(', ') + ' ' + command
.params
[command
.params
.length
- 1]);
989 RPL_HOSTCLOACKING: function (command
) {
990 genericNotice
.call(this, command
, command
.params
[1] + ' ' + command
.params
[command
.params
.length
- 1]);
997 function genericNotice (command
, msg
, is_error
) {
998 // Default to being an error
999 if (typeof is_error
!== 'boolean')
1002 this.irc_connection
.clientEvent('notice', {
1004 nick
: command
.prefix
,
1007 target
: command
.params
[0],
1009 numeric
: parseInt(command
.command
, 10)
1015 * Convert a mode string such as '+k pass', or '-i' to a readable
1017 * [ { mode: '+k', param: 'pass' } ]
1018 * [ { mode: '-i', param: null } ]
1020 function parseModeList(mode_string
, mode_params
) {
1021 var chanmodes
= this.irc_connection
.options
.CHANMODES
|| [],
1022 prefixes
= this.irc_connection
.options
.PREFIX
|| [],
1023 always_param
= (chanmodes
[0] || '').concat((chanmodes
[1] || '')),
1025 has_param
, i
, j
, add
;
1027 prefixes
= _
.reduce(prefixes
, function (list
, prefix
) {
1028 list
.push(prefix
.mode
);
1031 always_param
= always_param
.split('').concat(prefixes
);
1033 has_param = function (mode
, add
) {
1034 if (_
.find(always_param
, function (m
) {
1038 } else if (add
&& _
.find((chanmodes
[2] || '').split(''), function (m
) {
1048 for (i
= 0; i
< mode_string
.length
; i
++) {
1049 switch (mode_string
[i
]) {
1057 if (has_param(mode_string
[i
], add
)) {
1058 modes
.push({mode
: (add
? '+' : '-') + mode_string
[i
], param
: mode_params
[j
]});
1061 modes
.push({mode
: (add
? '+' : '-') + mode_string
[i
], param
: null});
1070 function getServerTime(command
) {
1073 // No tags? No times.
1074 if (!command
.tags
|| command
.tags
.length
=== 0) {
1078 if (capContainsAny
.call(this, ['server-time', 'znc.in/server-time', 'znc.in/server-time-iso'])) {
1079 time
= _
.find(command
.tags
, function (tag
) {
1080 return tag
.tag
=== 'time';
1083 time
= time
? time
.value
: undefined;
1085 // Convert the time value to a unixtimestamp
1086 if (typeof time
=== 'string') {
1087 if (time
.indexOf('T') > -1) {
1088 time
= parseISO8601(time
);
1090 } else if(time
.match(/^[0-9.]+$/)) {
1091 // A string formatted unix timestamp
1092 time
= new Date(time
* 1000);
1095 time
= time
.getTime();
1097 } else if (typeof time
=== 'number') {
1098 time
= new Date(time
* 1000);
1099 time
= time
.getTime();
1107 function capContainsAny (caps
) {
1109 if (!caps
instanceof Array
) {
1112 intersection
= _
.intersection(this.irc_connection
.cap
.enabled
, caps
);
1113 return intersection
.length
> 0;
1117 // Code based on http://anentropic.wordpress.com/2009/06/25/javascript-iso8601-parser-and-pretty-dates/#comment-154
1118 function parseISO8601(str
) {
1119 if (Date
.prototype.toISOString
) {
1120 return new Date(str
);
1122 var parts
= str
.split('T'),
1123 dateParts
= parts
[0].split('-'),
1124 timeParts
= parts
[1].split('Z'),
1125 timeSubParts
= timeParts
[0].split(':'),
1126 timeSecParts
= timeSubParts
[2].split('.'),
1127 timeHours
= Number(timeSubParts
[0]),
1130 _date
.setUTCFullYear(Number(dateParts
[0]));
1131 _date
.setUTCDate(1);
1132 _date
.setUTCMonth(Number(dateParts
[1])-1);
1133 _date
.setUTCDate(Number(dateParts
[2]));
1134 _date
.setUTCHours(Number(timeHours
));
1135 _date
.setUTCMinutes(Number(timeSubParts
[1]));
1136 _date
.setUTCSeconds(Number(timeSecParts
[0]));
1137 if (timeSecParts
[1]) {
1138 _date
.setUTCMilliseconds(Number(timeSecParts
[1]));