]>
jfr.im git - irc/quakenet/qwebirc.git/blob - js/irc/ircclient.js
b756fa63c07a75a0a3d5bb9be65a0633f1e77316
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
);
28 newLine: function(window
, type
, data
) {
32 var w
= this.getWindow(window
);
34 w
.addLine(type
, data
);
36 this.statusWindow
.addLine(type
, data
);
39 newChanLine: function(channel
, type
, user
, extra
) {
43 extra
["n"] = user
.hostToNick();
44 extra
["h"] = user
.hostToHost();
46 extra
["-"] = this.nickname
;
48 this.newLine(channel
, type
, extra
);
50 newServerLine: function(type
, data
) {
51 this.statusWindow
.addLine(type
, data
);
53 newActiveLine: function(type
, data
) {
54 this.getActiveWindow().addLine(type
, data
);
56 newTargetOrActiveLine: function(target
, type
, data
) {
57 if(this.getWindow(target
)) {
58 this.newLine(target
, type
, data
);
60 this.newActiveLine(type
, data
);
63 updateNickList: function(channel
) {
64 var n1
= this.tracker
.getChannel(channel
);
65 var names
= new Array();
66 var tff
= String
.fromCharCode(255);
74 if(nc
.prefixes
.length
> 0) {
75 var c
= nc
.prefixes
.charAt(0);
76 nx
= String
.fromCharCode(this.prefixes
.indexOf(c
)) + this.toIRCLower(n
);
79 nx
= tff
+ this.toIRCLower(n
);
87 var sortednames
= new Array();
88 names
.each(function(name
) {
89 sortednames
.push(nh
[name
]);
92 var w
= this.getWindow(channel
);
94 w
.updateNickList(sortednames
);
96 getWindow: function(name
) {
97 return this.windows
[this.toIRCLower(name
)];
99 newWindow: function(name
, type
, select
) {
100 var w
= this.getWindow(name
);
102 w
= this.windows
[this.toIRCLower(name
)] = this.ui
.newWindow(this, type
, name
);
104 w
.addEvent("close", function(w
) {
105 delete this.windows
[this.toIRCLower(name
)];
110 this.ui
.selectWindow(w
);
114 getQueryWindow: function(name
) {
115 return this.ui
.getWindow(this, qwebirc
.ui
.WINDOW_QUERY
, name
);
117 newQueryWindow: function(name
, privmsg
) {
120 if(this.getQueryWindow(name
))
124 return this.newPrivmsgQueryWindow(name
);
125 return this.newNoticeQueryWindow(name
);
127 newPrivmsgQueryWindow: function(name
) {
128 if(this.ui
.uiOptions
.DEDICATED_MSG_WINDOW
) {
129 if(!this.ui
.getWindow(this, qwebirc
.ui
.WINDOW_MESSAGES
))
130 return this.ui
.newWindow(this, qwebirc
.ui
.WINDOW_MESSAGES
, "Messages");
132 return this.newWindow(name
, qwebirc
.ui
.WINDOW_QUERY
, false);
135 newNoticeQueryWindow: function(name
) {
136 if(this.ui
.uiOptions
.DEDICATED_NOTICE_WINDOW
)
137 if(!this.ui
.getWindow(this, qwebirc
.ui
.WINDOW_MESSAGES
))
138 return this.ui
.newWindow(this, qwebirc
.ui
.WINDOW_MESSAGES
, "Messages");
140 newQueryLine: function(window
, type
, data
, privmsg
, active
) {
141 if(this.getQueryWindow(window
))
142 return this.newLine(window
, type
, data
);
144 var w
= this.ui
.getWindow(this, qwebirc
.ui
.WINDOW_MESSAGES
);
148 e
= this.ui
.uiOptions
.DEDICATED_MSG_WINDOW
;
150 e
= this.ui
.uiOptions
.DEDICATED_NOTICE_WINDOW
;
153 return w
.addLine(type
, data
);
156 return this.newActiveLine(type
, data
);
158 return this.newLine(window
, type
, data
);
162 newQueryOrActiveLine: function(window
, type
, data
, privmsg
) {
163 this.newQueryLine(window
, type
, data
, privmsg
, true);
165 getActiveWindow: function() {
166 return this.ui
.getActiveIRCWindow(this);
168 getNickname: function() {
169 return this.nickname
;
171 addPrefix: function(nickchanentry
, prefix
) {
172 var ncp
= nickchanentry
.prefixes
+ prefix
;
176 for(var i
=0;i
<this.prefixes
.length
;i
++) {
177 var pc
= this.prefixes
.charAt(i
);
178 var index
= ncp
.indexOf(pc
);
183 nickchanentry
.prefixes
= prefixes
.join("");
185 stripPrefix: function(nick
) {
186 var l
= nick
.charAt(0);
190 if(this.prefixes
.indexOf(l
) != -1)
191 return nick
.substring(1);
195 removePrefix: function(nickchanentry
, prefix
) {
196 nickchanentry
.prefixes
= nickchanentry
.prefixes
.replaceAll(prefix
, "");
199 /* from here down are events */
200 rawNumeric: function(numeric
, prefix
, params
) {
201 this.newServerLine("RAW", {"n": "numeric", "m": params
.slice(1).join(" ")});
203 signedOn: function(nickname
) {
204 this.tracker
= new qwebirc
.irc
.IRCTracker(this);
205 this.nickname
= nickname
;
206 this.newServerLine("SIGNON");
208 /* we guarantee that +x is sent out before the joins */
209 if(this.ui
.uiOptions
.USE_HIDDENHOST
)
210 this.exec("/UMODE +x");
212 if(this.options
.autojoin
) {
213 if(qwebirc
.auth
.loggedin() && this.ui
.uiOptions
.USE_HIDDENHOST
) {
215 if($defined(this.activeTimers
.autojoin
))
216 this.ui
.getActiveWindow().infoMessage("Waiting for login before joining channels...");
218 this.activeTimers
.autojoin = function() {
219 var w
= this.ui
.getActiveWindow();
220 w
.errorMessage("No login response in 10 seconds.");
221 w
.errorMessage("You may want to try authing manually and then type: /autojoin (if you don't auth your host may be visible).");
222 }.delay(10000, this);
226 this.exec("/AUTOJOIN");
229 userJoined: function(user
, channel
) {
230 var nick
= user
.hostToNick();
231 var host
= user
.hostToHost();
233 if((nick
== this.nickname
) && !this.getWindow(channel
))
234 this.newWindow(channel
, qwebirc
.ui
.WINDOW_CHANNEL
, true);
235 this.tracker
.addNickToChannel(nick
, channel
);
237 if(nick
== this.nickname
) {
238 this.newChanLine(channel
, "OURJOIN", user
);
240 this.newChanLine(channel
, "JOIN", user
);
242 this.updateNickList(channel
);
244 userPart: function(user
, channel
, message
) {
245 var nick
= user
.hostToNick();
246 var host
= user
.hostToHost();
248 if(nick
== this.nickname
) {
249 this.tracker
.removeChannel(channel
);
251 this.tracker
.removeNickFromChannel(nick
, channel
);
252 this.newChanLine(channel
, "PART", user
, {"m": message
});
255 this.updateNickList(channel
);
257 if(nick
== this.nickname
) {
258 var w
= this.getWindow(channel
)
263 userKicked: function(kicker
, channel
, kickee
, message
) {
264 if(kickee
== this.nickname
) {
265 this.tracker
.removeChannel(channel
);
266 this.getWindow(channel
).close();
268 this.tracker
.removeNickFromChannel(kickee
, channel
);
269 this.updateNickList(channel
);
272 this.newChanLine(channel
, "KICK", kicker
, {"v": kickee
, "m": message
});
274 channelMode: function(user
, channel
, modes
, raw
) {
275 modes
.each(function(mo
) {
276 var direction
= mo
[0];
279 var prefixindex
= this.modeprefixes
.indexOf(mode
);
280 if(prefixindex
== -1)
284 var prefixchar
= this.prefixes
.charAt(prefixindex
);
286 var nc
= this.tracker
.getOrCreateNickOnChannel(nick
, channel
);
287 if(direction
== "-") {
288 this.removePrefix(nc
, prefixchar
);
290 this.addPrefix(nc
, prefixchar
);
294 this.newChanLine(channel
, "MODE", user
, {"m": raw
.join(" ")});
296 this.updateNickList(channel
);
298 userQuit: function(user
, message
) {
299 var nick
= user
.hostToNick();
301 var channels
= this.tracker
.getNick(nick
);
304 for(var c
in channels
) {
306 this.newChanLine(c
, "QUIT", user
, {"m": message
});
309 this.tracker
.removeNick(nick
);
311 clist
.each(function(cli
) {
312 this.updateNickList(cli
);
315 nickChanged: function(user
, newnick
) {
316 var oldnick
= user
.hostToNick();
318 if(oldnick
== this.nickname
)
319 this.nickname
= newnick
;
321 this.tracker
.renameNick(oldnick
, newnick
);
323 var channels
= this.tracker
.getNick(newnick
);
326 for(var c
in channels
) {
329 this.newChanLine(c
, "NICK", user
, {"w": newnick
});
330 /* TODO: rename queries */
331 this.updateNickList(c
);
334 /* this is quite horrible */
336 this.newServerLine("NICK", {"w": newnick
, n: user
.hostToNick(), h: user
.hostToHost(), "-": this.nickname
});
338 channelTopic: function(user
, channel
, topic
) {
339 this.newChanLine(channel
, "TOPIC", user
, {"m": topic
});
340 this.getWindow(channel
).updateTopic(topic
);
342 initialTopic: function(channel
, topic
) {
343 this.getWindow(channel
).updateTopic(topic
);
345 channelCTCP: function(user
, channel
, type
, args
) {
346 if(args
== undefined)
349 var nick
= user
.hostToNick();
350 if(type
== "ACTION") {
351 this.tracker
.updateLastSpoke(nick
, channel
, new Date().getTime());
352 this.newChanLine(channel
, "CHANACTION", user
, {"m": args
, "c": channel
, "@": this.getNickStatus(channel
, nick
)});
356 this.newChanLine(channel
, "CHANCTCP", user
, {"x": type
, "m": args
, "c": channel
, "@": this.getNickStatus(channel
, nick
)});
358 userCTCP: function(user
, type
, args
) {
359 var nick
= user
.hostToNick();
360 var host
= user
.hostToHost();
361 if(args
== undefined)
364 if(type
== "ACTION") {
365 this.newQueryWindow(nick
, true);
366 this.newQueryLine(nick
, "PRIVACTION", {"m": args
, "x": type
, "h": host
, "n": nick
}, true);
370 this.newTargetOrActiveLine(nick
, "PRIVCTCP", {"m": args
, "x": type
, "h": host
, "n": nick
, "-": this.nickname
});
372 userCTCPReply: function(user
, type
, args
) {
373 var nick
= user
.hostToNick();
374 var host
= user
.hostToHost();
375 if(args
== undefined)
378 this.newTargetOrActiveLine(nick
, "CTCPREPLY", {"m": args
, "x": type
, "h": host
, "n": nick
, "-": this.nickname
});
380 getNickStatus: function(channel
, nick
) {
381 var n
= this.tracker
.getNickOnChannel(nick
, channel
);
385 if(n
.prefixes
.length
== 0)
388 return n
.prefixes
.charAt(0);
390 channelPrivmsg: function(user
, channel
, message
) {
391 var nick
= user
.hostToNick();
393 this.tracker
.updateLastSpoke(nick
, channel
, new Date().getTime());
394 this.newChanLine(channel
, "CHANMSG", user
, {"m": message
, "@": this.getNickStatus(channel
, nick
)});
396 channelNotice: function(user
, channel
, message
) {
397 this.newChanLine(channel
, "CHANNOTICE", user
, {"m": message
, "@": this.getNickStatus(channel
, user
.hostToNick())});
399 userPrivmsg: function(user
, message
) {
400 var nick
= user
.hostToNick();
401 var host
= user
.hostToHost();
402 this.newQueryWindow(nick
, true);
403 this.pushLastNick(nick
);
404 this.newQueryLine(nick
, "PRIVMSG", {"m": message
, "h": host
, "n": nick
}, true);
406 this.checkLogin(user
, message
);
408 checkLogin: function(user
, message
) {
409 if(this.isNetworkService(user
) && $defined(this.activeTimers
.autojoin
)) {
410 if($defined(this.loginRegex
) && message
.match(this.loginRegex
)) {
411 $clear(this.activeTimers
.autojoin
);
412 delete this.activeTimers
["autojoin"];
413 this.ui
.getActiveWindow().infoMessage("Joining channels...");
414 this.exec("/AUTOJOIN");
418 serverNotice: function(user
, message
) {
420 this.newServerLine("SERVERNOTICE", {"m": message
});
422 this.newServerLine("PRIVNOTICE", {"m": message
, "n": user
});
425 userNotice: function(user
, message
) {
426 var nick
= user
.hostToNick();
427 var host
= user
.hostToHost();
429 if(this.ui
.uiOptions
.DEDICATED_NOTICE_WINDOW
) {
430 this.newQueryWindow(nick
, false);
431 this.newQueryOrActiveLine(nick
, "PRIVNOTICE", {"m": message
, "h": host
, "n": nick
}, false);
433 this.newTargetOrActiveLine(nick
, "PRIVNOTICE", {"m": message
, "h": host
, "n": nick
});
436 this.checkLogin(user
, message
);
438 isNetworkService: function(user
) {
439 return this.ui
.options
.networkServices
.indexOf(user
) > -1;
441 __joinInvited: function() {
442 this.exec("/JOIN " + this.inviteChanList
.join(","));
443 this.inviteChanList
= [];
444 delete this.activeTimers
["serviceInvite"];
446 userInvite: function(user
, channel
) {
447 var nick
= user
.hostToNick();
448 var host
= user
.hostToHost();
450 this.newServerLine("INVITE", {"c": channel
, "h": host
, "n": nick
});
451 if(this.ui
.uiOptions
.ACCEPT_SERVICE_INVITES
&& this.isNetworkService(user
)) {
452 if(this.activeTimers
.serviceInvite
)
453 $clear(this.activeTimers
.serviceInvite
);
455 /* we do this so we can batch the joins, i.e. instead of sending 5 JOIN comands we send 1 with 5 channels. */
456 this.activeTimers
.serviceInvite
= this.__joinInvited
.delay(100, this);
458 this.inviteChanList
.push(channel
);
461 userMode: function(modes
) {
462 this.newServerLine("UMODE", {"m": modes
, "n": this.nickname
});
464 channelNames: function(channel
, names
) {
465 if(names
.length
== 0) {
466 this.updateNickList(channel
);
470 names
.each(function(nick
) {
472 var splitnick
= nick
.split("");
474 splitnick
.every(function(c
, i
) {
475 if(this.prefixes
.indexOf(c
) == -1) {
476 nick
= nick
.substr(i
);
484 var nc
= this.tracker
.addNickToChannel(nick
, channel
);
485 prefixes
.each(function(p
) {
486 this.addPrefix(nc
, p
);
490 disconnected: function(message
) {
491 for(var x
in this.windows
) {
492 var w
= this.windows
[x
];
493 if(w
.type
== qwebirc
.ui
.WINDOW_CHANNEL
)
496 this.tracker
= undefined;
498 this.newServerLine("DISCONNECT", {"m": message
});
500 nickOnChanHasPrefix: function(nick
, channel
, prefix
) {
501 var entry
= this.tracker
.getNickOnChannel(nick
, channel
);
503 return false; /* shouldn't happen */
505 return entry
.prefixes
.indexOf(prefix
) != -1;
507 supported: function(key
, value
) {
508 if(key
== "PREFIX") {
509 var l
= (value
.length
- 2) / 2;
511 this.modeprefixes
= value
.substr(1, l
);
512 this.prefixes
= value
.substr(l
+ 2, l
);
515 this.parent(key
, value
);
517 connected: function() {
518 this.newServerLine("CONNECT");
520 serverError: function(message
) {
521 this.newServerLine("ERROR", {"m": message
});
523 quit: function(message
) {
524 this.send("QUIT :" + message
);
527 disconnect: function() {
528 for(var k
in this.activeTimers
) {
529 this.activeTimers
[k
].cancel();
531 this.activeTimers
= {};
535 awayMessage: function(nick
, message
) {
536 this.newQueryLine(nick
, "AWAY", {"n": nick
, "m": message
}, true);
538 whois: function(nick
, type
, data
) {
539 var ndata
= {"n": nick
};
542 var xsend = function() {
543 this.newTargetOrActiveLine(nick
, "WHOIS" + mtype
, ndata
);
548 ndata
.h
= data
.ident
+ "@" + data
.hostname
;
551 ndata
.m
= data
.realname
;
552 } else if(type
== "server") {
554 ndata
.x
= data
.server
;
555 ndata
.m
= data
.serverdesc
;
556 } else if(type
== "oper") {
558 } else if(type
== "idle") {
560 ndata
.x
= qwebirc
.util
.longtoduration(data
.idle
);
561 ndata
.m
= qwebirc
.irc
.IRCDate(new Date(data
.connected
* 1000));
562 } else if(type
== "channels") {
564 ndata
.m
= data
.channels
;
565 } else if(type
== "account") {
567 ndata
.m
= data
.account
;
568 } else if(type
== "away") {
571 } else if(type
== "opername") {
573 ndata
.m
= data
.opername
;
574 } else if(type
== "actually") {
576 ndata
.m
= data
.hostname
;
578 } else if(type
== "end") {
587 genericError: function(target
, message
) {
588 this.newTargetOrActiveLine(target
, "GENERICERROR", {m: message
, t: target
});
590 genericQueryError: function(target
, message
) {
591 this.newQueryOrActiveLine(target
, "GENERICERROR", {m: message
, t: target
}, true);
593 awayStatus: function(state
, message
) {
594 this.newActiveLine("GENERICMESSAGE", {m: message
});
596 pushLastNick: function(nick
) {
597 var i
= this.lastNicks
.indexOf(nick
);
599 this.lastNicks
.splice(i
, 1);
601 if(this.lastNicks
.length
== this.options
.maxnicks
)
602 this.lastNicks
.pop();
604 this.lastNicks
.unshift(nick
);
606 wallops: function(user
, text
) {
607 var nick
= user
.hostToNick();
608 var host
= user
.hostToHost();
610 this.newServerLine("WALLOPS", {t: text
, n: nick
, h: host
});