]>
jfr.im git - irc/quakenet/qwebirc.git/blob - js/irc/ircclient.js
b8d59676d830e6e41ecc087174b6c8dd02bc6d15
1 qwebirc
.irc
.IRCClient
= new Class({
2 Extends: qwebirc
.irc
.BaseIRCClient
,
8 initialize: function(options
, ui
) {
14 this.modeprefixes
= "ov";
17 this.commandparser
= new qwebirc
.irc
.Commands(this);
18 this.exec
= this.commandparser
.dispatch
.bind(this.commandparser
);
20 this.statusWindow
= this.ui
.newClient(this);
23 this.inviteChanList
= [];
24 this.activeTimers
= {};
26 this.loginRegex
= new RegExp(this.ui
.options
.loginRegex
);
27 this.tracker
= new qwebirc
.irc
.IRCTracker(this);
29 newLine: function(window
, type
, data
) {
33 var w
= this.getWindow(window
);
35 w
.addLine(type
, data
);
37 this.statusWindow
.addLine(type
, data
);
40 newChanLine: function(channel
, type
, user
, extra
) {
45 extra
["n"] = user
.hostToNick();
46 extra
["h"] = user
.hostToHost();
49 extra
["-"] = this.nickname
;
51 if(!(this.ui
.uiOptions
.NICK_OV_STATUS
))
54 this.newLine(channel
, type
, extra
);
56 newServerLine: function(type
, data
) {
57 this.statusWindow
.addLine(type
, data
);
59 newActiveLine: function(type
, data
) {
60 this.getActiveWindow().addLine(type
, data
);
62 newTargetOrActiveLine: function(target
, type
, data
) {
63 if(this.getWindow(target
)) {
64 this.newLine(target
, type
, data
);
66 this.newActiveLine(type
, data
);
69 updateNickList: function(channel
) {
70 var n1
= this.tracker
.getChannel(channel
);
71 var names
= new Array();
72 var tff
= String
.fromCharCode(255);
80 if(nc
.prefixes
.length
> 0) {
81 var c
= nc
.prefixes
.charAt(0);
82 nx
= String
.fromCharCode(this.prefixes
.indexOf(c
)) + this.toIRCLower(n
);
85 nx
= tff
+ this.toIRCLower(n
);
93 var sortednames
= new Array();
94 names
.each(function(name
) {
95 sortednames
.push(nh
[name
]);
98 var w
= this.getWindow(channel
);
100 w
.updateNickList(sortednames
);
102 getWindow: function(name
) {
103 return this.windows
[this.toIRCLower(name
)];
105 newWindow: function(name
, type
, select
) {
106 var w
= this.getWindow(name
);
108 w
= this.windows
[this.toIRCLower(name
)] = this.ui
.newWindow(this, type
, name
);
110 w
.addEvent("close", function(w
) {
111 delete this.windows
[this.toIRCLower(name
)];
116 this.ui
.selectWindow(w
);
120 getQueryWindow: function(name
) {
121 return this.ui
.getWindow(this, qwebirc
.ui
.WINDOW_QUERY
, name
);
123 newQueryWindow: function(name
, privmsg
) {
126 if(this.getQueryWindow(name
))
130 return this.newPrivmsgQueryWindow(name
);
131 return this.newNoticeQueryWindow(name
);
133 newPrivmsgQueryWindow: function(name
) {
134 if(this.ui
.uiOptions
.DEDICATED_MSG_WINDOW
) {
135 if(!this.ui
.getWindow(this, qwebirc
.ui
.WINDOW_MESSAGES
))
136 return this.ui
.newWindow(this, qwebirc
.ui
.WINDOW_MESSAGES
, "Messages");
138 return this.newWindow(name
, qwebirc
.ui
.WINDOW_QUERY
, false);
141 newNoticeQueryWindow: function(name
) {
142 if(this.ui
.uiOptions
.DEDICATED_NOTICE_WINDOW
)
143 if(!this.ui
.getWindow(this, qwebirc
.ui
.WINDOW_MESSAGES
))
144 return this.ui
.newWindow(this, qwebirc
.ui
.WINDOW_MESSAGES
, "Messages");
146 newQueryLine: function(window
, type
, data
, privmsg
, active
) {
147 if(this.getQueryWindow(window
))
148 return this.newLine(window
, type
, data
);
150 var w
= this.ui
.getWindow(this, qwebirc
.ui
.WINDOW_MESSAGES
);
154 e
= this.ui
.uiOptions
.DEDICATED_MSG_WINDOW
;
156 e
= this.ui
.uiOptions
.DEDICATED_NOTICE_WINDOW
;
159 return w
.addLine(type
, data
);
162 return this.newActiveLine(type
, data
);
164 return this.newLine(window
, type
, data
);
168 newQueryOrActiveLine: function(window
, type
, data
, privmsg
) {
169 this.newQueryLine(window
, type
, data
, privmsg
, true);
171 getActiveWindow: function() {
172 return this.ui
.getActiveIRCWindow(this);
174 getNickname: function() {
175 return this.nickname
;
177 addPrefix: function(nickchanentry
, prefix
) {
178 var ncp
= nickchanentry
.prefixes
+ prefix
;
182 for(var i
=0;i
<this.prefixes
.length
;i
++) {
183 var pc
= this.prefixes
.charAt(i
);
184 var index
= ncp
.indexOf(pc
);
189 nickchanentry
.prefixes
= prefixes
.join("");
191 stripPrefix: function(nick
) {
192 var l
= nick
.charAt(0);
196 if(this.prefixes
.indexOf(l
) != -1)
197 return nick
.substring(1);
201 removePrefix: function(nickchanentry
, prefix
) {
202 nickchanentry
.prefixes
= nickchanentry
.prefixes
.replaceAll(prefix
, "");
205 /* from here down are events */
206 rawNumeric: function(numeric
, prefix
, params
) {
207 this.newServerLine("RAW", {"n": "numeric", "m": params
.slice(1).join(" ")});
209 signedOn: function(nickname
) {
210 this.tracker
= new qwebirc
.irc
.IRCTracker(this);
211 this.nickname
= nickname
;
212 this.newServerLine("SIGNON");
214 /* we guarantee that +x is sent out before the joins */
215 if(this.ui
.uiOptions
.USE_HIDDENHOST
)
216 this.exec("/UMODE +x");
218 if(this.options
.autojoin
) {
219 if(qwebirc
.auth
.loggedin() && this.ui
.uiOptions
.USE_HIDDENHOST
) {
221 if($defined(this.activeTimers
.autojoin
))
222 this.ui
.getActiveWindow().infoMessage("Waiting for login before joining channels...");
224 this.activeTimers
.autojoin = function() {
225 var w
= this.ui
.getActiveWindow();
226 w
.errorMessage("No login response in 10 seconds.");
227 w
.errorMessage("You may want to try authing manually and then type: /autojoin (if you don't auth your host may be visible).");
228 }.delay(10000, this);
232 this.exec("/AUTOJOIN");
235 userJoined: function(user
, channel
) {
236 var nick
= user
.hostToNick();
237 var host
= user
.hostToHost();
239 if((nick
== this.nickname
) && !this.getWindow(channel
))
240 this.newWindow(channel
, qwebirc
.ui
.WINDOW_CHANNEL
, true);
241 this.tracker
.addNickToChannel(nick
, channel
);
243 if(nick
== this.nickname
) {
244 this.newChanLine(channel
, "OURJOIN", user
);
246 if(!this.ui
.uiOptions
.HIDE_JOINPARTS
) {
247 this.newChanLine(channel
, "JOIN", user
);
250 this.updateNickList(channel
);
252 userPart: function(user
, channel
, message
) {
253 var nick
= user
.hostToNick();
254 var host
= user
.hostToHost();
256 if(nick
== this.nickname
) {
257 this.tracker
.removeChannel(channel
);
259 this.tracker
.removeNickFromChannel(nick
, channel
);
260 if(!this.ui
.uiOptions
.HIDE_JOINPARTS
) {
261 this.newChanLine(channel
, "PART", user
, {"m": message
});
265 this.updateNickList(channel
);
267 if(nick
== this.nickname
) {
268 var w
= this.getWindow(channel
)
273 userKicked: function(kicker
, channel
, kickee
, message
) {
274 if(kickee
== this.nickname
) {
275 this.tracker
.removeChannel(channel
);
276 this.getWindow(channel
).close();
278 this.tracker
.removeNickFromChannel(kickee
, channel
);
279 this.updateNickList(channel
);
282 this.newChanLine(channel
, "KICK", kicker
, {"v": kickee
, "m": message
});
284 channelMode: function(user
, channel
, modes
, raw
) {
285 modes
.each(function(mo
) {
286 var direction
= mo
[0];
289 var prefixindex
= this.modeprefixes
.indexOf(mode
);
290 if(prefixindex
== -1)
294 var prefixchar
= this.prefixes
.charAt(prefixindex
);
296 var nc
= this.tracker
.getOrCreateNickOnChannel(nick
, channel
);
297 if(direction
== "-") {
298 this.removePrefix(nc
, prefixchar
);
300 this.addPrefix(nc
, prefixchar
);
304 this.newChanLine(channel
, "MODE", user
, {"m": raw
.join(" ")});
306 this.updateNickList(channel
);
308 userQuit: function(user
, message
) {
309 var nick
= user
.hostToNick();
311 var channels
= this.tracker
.getNick(nick
);
314 for(var c
in channels
) {
316 if(!this.ui
.uiOptions
.HIDE_JOINPARTS
) {
317 this.newChanLine(c
, "QUIT", user
, {"m": message
});
321 this.tracker
.removeNick(nick
);
323 clist
.each(function(cli
) {
324 this.updateNickList(cli
);
327 nickChanged: function(user
, newnick
) {
328 var oldnick
= user
.hostToNick();
330 if(oldnick
== this.nickname
)
331 this.nickname
= newnick
;
333 this.tracker
.renameNick(oldnick
, newnick
);
335 var channels
= this.tracker
.getNick(newnick
);
338 for(var c
in channels
) {
341 this.newChanLine(c
, "NICK", user
, {"w": newnick
});
342 /* TODO: rename queries */
343 this.updateNickList(c
);
346 /* this is quite horrible */
348 this.newServerLine("NICK", {"w": newnick
, n: user
.hostToNick(), h: user
.hostToHost(), "-": this.nickname
});
350 channelTopic: function(user
, channel
, topic
) {
351 this.newChanLine(channel
, "TOPIC", user
, {"m": topic
});
352 this.getWindow(channel
).updateTopic(topic
);
354 initialTopic: function(channel
, topic
) {
355 this.getWindow(channel
).updateTopic(topic
);
357 channelCTCP: function(user
, channel
, type
, args
) {
358 if(args
== undefined)
361 var nick
= user
.hostToNick();
362 if(type
== "ACTION") {
363 this.tracker
.updateLastSpoke(nick
, channel
, new Date().getTime());
364 this.newChanLine(channel
, "CHANACTION", user
, {"m": args
, "c": channel
, "@": this.getNickStatus(channel
, nick
)});
368 this.newChanLine(channel
, "CHANCTCP", user
, {"x": type
, "m": args
, "c": channel
, "@": this.getNickStatus(channel
, nick
)});
370 userCTCP: function(user
, type
, args
) {
371 var nick
= user
.hostToNick();
372 var host
= user
.hostToHost();
373 if(args
== undefined)
376 if(type
== "ACTION") {
377 this.newQueryWindow(nick
, true);
378 this.newQueryLine(nick
, "PRIVACTION", {"m": args
, "x": type
, "h": host
, "n": nick
}, true);
382 this.newTargetOrActiveLine(nick
, "PRIVCTCP", {"m": args
, "x": type
, "h": host
, "n": nick
, "-": this.nickname
});
384 userCTCPReply: function(user
, type
, args
) {
385 var nick
= user
.hostToNick();
386 var host
= user
.hostToHost();
387 if(args
== undefined)
390 this.newTargetOrActiveLine(nick
, "CTCPREPLY", {"m": args
, "x": type
, "h": host
, "n": nick
, "-": this.nickname
});
392 getNickStatus: function(channel
, nick
) {
393 var n
= this.tracker
.getNickOnChannel(nick
, channel
);
397 if(n
.prefixes
.length
== 0)
400 return n
.prefixes
.charAt(0);
402 channelPrivmsg: function(user
, channel
, message
) {
403 var nick
= user
.hostToNick();
405 this.tracker
.updateLastSpoke(nick
, channel
, new Date().getTime());
406 this.newChanLine(channel
, "CHANMSG", user
, {"m": message
, "@": this.getNickStatus(channel
, nick
)});
408 channelNotice: function(user
, channel
, message
) {
409 this.newChanLine(channel
, "CHANNOTICE", user
, {"m": message
, "@": this.getNickStatus(channel
, user
.hostToNick())});
411 userPrivmsg: function(user
, message
) {
412 var nick
= user
.hostToNick();
413 var host
= user
.hostToHost();
414 this.newQueryWindow(nick
, true);
415 this.pushLastNick(nick
);
416 this.newQueryLine(nick
, "PRIVMSG", {"m": message
, "h": host
, "n": nick
}, true);
418 this.checkLogin(user
, message
);
420 checkLogin: function(user
, message
) {
421 if(this.isNetworkService(user
) && $defined(this.activeTimers
.autojoin
)) {
422 if($defined(this.loginRegex
) && message
.match(this.loginRegex
)) {
423 $clear(this.activeTimers
.autojoin
);
424 delete this.activeTimers
["autojoin"];
425 this.ui
.getActiveWindow().infoMessage("Joining channels...");
426 this.exec("/AUTOJOIN");
430 serverNotice: function(user
, message
) {
432 this.newServerLine("SERVERNOTICE", {"m": message
});
434 this.newServerLine("PRIVNOTICE", {"m": message
, "n": user
});
437 userNotice: function(user
, message
) {
438 var nick
= user
.hostToNick();
439 var host
= user
.hostToHost();
441 if(this.ui
.uiOptions
.DEDICATED_NOTICE_WINDOW
) {
442 this.newQueryWindow(nick
, false);
443 this.newQueryOrActiveLine(nick
, "PRIVNOTICE", {"m": message
, "h": host
, "n": nick
}, false);
445 this.newTargetOrActiveLine(nick
, "PRIVNOTICE", {"m": message
, "h": host
, "n": nick
});
448 this.checkLogin(user
, message
);
450 isNetworkService: function(user
) {
451 return this.ui
.options
.networkServices
.indexOf(user
) > -1;
453 __joinInvited: function() {
454 this.exec("/JOIN " + this.inviteChanList
.join(","));
455 this.inviteChanList
= [];
456 delete this.activeTimers
["serviceInvite"];
458 userInvite: function(user
, channel
) {
459 var nick
= user
.hostToNick();
460 var host
= user
.hostToHost();
462 this.newServerLine("INVITE", {"c": channel
, "h": host
, "n": nick
});
463 if(this.ui
.uiOptions
.ACCEPT_SERVICE_INVITES
&& this.isNetworkService(user
)) {
464 if(this.activeTimers
.serviceInvite
)
465 $clear(this.activeTimers
.serviceInvite
);
467 /* we do this so we can batch the joins, i.e. instead of sending 5 JOIN comands we send 1 with 5 channels. */
468 this.activeTimers
.serviceInvite
= this.__joinInvited
.delay(100, this);
470 this.inviteChanList
.push(channel
);
473 userMode: function(modes
) {
474 this.newServerLine("UMODE", {"m": modes
, "n": this.nickname
});
476 channelNames: function(channel
, names
) {
477 if(names
.length
== 0) {
478 this.updateNickList(channel
);
482 names
.each(function(nick
) {
484 var splitnick
= nick
.split("");
486 splitnick
.every(function(c
, i
) {
487 if(this.prefixes
.indexOf(c
) == -1) {
488 nick
= nick
.substr(i
);
496 var nc
= this.tracker
.addNickToChannel(nick
, channel
);
497 prefixes
.each(function(p
) {
498 this.addPrefix(nc
, p
);
502 disconnected: function(message
) {
503 for(var x
in this.windows
) {
504 var w
= this.windows
[x
];
505 if(w
.type
== qwebirc
.ui
.WINDOW_CHANNEL
)
508 this.tracker
= undefined;
510 this.newServerLine("DISCONNECT", {"m": message
});
512 nickOnChanHasPrefix: function(nick
, channel
, prefix
) {
513 var entry
= this.tracker
.getNickOnChannel(nick
, channel
);
515 return false; /* shouldn't happen */
517 return entry
.prefixes
.indexOf(prefix
) != -1;
519 nickOnChanHasAtLeastPrefix: function(nick
, channel
, prefix
) {
520 var entry
= this.tracker
.getNickOnChannel(nick
, channel
);
522 return false; /* shouldn't happen */
524 /* this array is sorted */
525 var pos
= this.prefixes
.indexOf(prefix
);
527 return false; /* shouldn't happen */
530 this.prefixes
.slice(0, pos
+ 1).split("").each(function(x
) {
534 var prefixes
= entry
.prefixes
;
535 for(var i
=0;i
<prefixes
.length
;i
++)
536 if(modehash
[prefixes
.charAt(i
)])
541 supported: function(key
, value
) {
542 if(key
== "PREFIX") {
543 var l
= (value
.length
- 2) / 2;
545 this.modeprefixes
= value
.substr(1, l
);
546 this.prefixes
= value
.substr(l
+ 2, l
);
549 this.parent(key
, value
);
551 connected: function() {
552 this.newServerLine("CONNECT");
554 serverError: function(message
) {
555 this.newServerLine("ERROR", {"m": message
});
557 quit: function(message
) {
558 this.send("QUIT :" + message
, true);
561 disconnect: function() {
562 for(var k
in this.activeTimers
) {
563 this.activeTimers
[k
].cancel();
565 this.activeTimers
= {};
569 awayMessage: function(nick
, message
) {
570 this.newQueryLine(nick
, "AWAY", {"n": nick
, "m": message
}, true);
572 whois: function(nick
, type
, data
) {
573 var ndata
= {"n": nick
};
576 var xsend = function() {
577 this.newTargetOrActiveLine(nick
, "WHOIS" + mtype
, ndata
);
582 ndata
.h
= data
.ident
+ "@" + data
.hostname
;
585 ndata
.m
= data
.realname
;
586 } else if(type
== "server") {
588 ndata
.x
= data
.server
;
589 ndata
.m
= data
.serverdesc
;
590 } else if(type
== "oper") {
592 } else if(type
== "idle") {
594 ndata
.x
= qwebirc
.util
.longtoduration(data
.idle
);
595 ndata
.m
= qwebirc
.irc
.IRCDate(new Date(data
.connected
* 1000));
596 } else if(type
== "channels") {
598 ndata
.m
= data
.channels
;
599 } else if(type
== "account") {
601 ndata
.m
= data
.account
;
602 } else if(type
== "away") {
605 } else if(type
== "opername") {
607 ndata
.m
= data
.opername
;
608 } else if(type
== "actually") {
610 ndata
.m
= data
.hostname
;
612 } else if(type
== "generictext") {
613 mtype
= "GENERICTEXT";
615 } else if(type
== "end") {
624 genericError: function(target
, message
) {
625 this.newTargetOrActiveLine(target
, "GENERICERROR", {m: message
, t: target
});
627 genericQueryError: function(target
, message
) {
628 this.newQueryOrActiveLine(target
, "GENERICERROR", {m: message
, t: target
}, true);
630 awayStatus: function(state
, message
) {
631 this.newActiveLine("GENERICMESSAGE", {m: message
});
633 pushLastNick: function(nick
) {
634 var i
= this.lastNicks
.indexOf(nick
);
636 this.lastNicks
.splice(i
, 1);
638 if(this.lastNicks
.length
== this.options
.maxnicks
)
639 this.lastNicks
.pop();
641 this.lastNicks
.unshift(nick
);
643 wallops: function(user
, text
) {
644 var nick
= user
.hostToNick();
645 var host
= user
.hostToHost();
647 this.newServerLine("WALLOPS", {t: text
, n: nick
, h: host
});
649 channelModeIs: function(channel
, modes
) {
650 this.newTargetOrActiveLine(channel
, "CHANNELMODEIS", {c: channel
, m: modes
.join(" ")});
652 channelCreationTime: function(channel
, time
) {
653 this.newTargetOrActiveLine(channel
, "CHANNELCREATIONTIME", {c: channel
, m: qwebirc
.irc
.IRCDate(new Date(time
* 1000))});