Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base: d6b5114503
...
compare: 521631f5d3
  • 12 commits
  • 6 files changed
  • 0 commit comments
  • 2 contributors
View
2  README.md
@@ -18,6 +18,8 @@ Currently this simply joins #lullabuddies but I have plans to list out some offi
This will grab the neccessary modules for the package.
+2. Modify config.js to point to the IRC channel you want to join.
+
2. From your terminal go into your checkout and run:
$ node irc.js
View
11 config.js
@@ -0,0 +1,11 @@
+/**
+ * Configuration for irc.
+ */
+
+var cfg = {}
+
+// The channel to join. Be sure to include the leading #.
+cfg.channel = "#lullabuddies";
+
+module.exports = cfg;
+
View
72 irc.js
@@ -1,5 +1,5 @@
/***************************************\
- IRC#lullabuddies client
+ IRC client
\***************************************/
/*
@@ -19,7 +19,7 @@ var http = require('http')
, io = require('socket.io')
, express = require('express')
, ircjs = require('irc-js')
- , cfg = { channel:'#lullabuddies' }
+ , cfg = require('./config')
, app = express.createServer()
, io = require('socket.io').listen(app);
@@ -79,11 +79,14 @@ io.sockets.on('connection', function (client) {
var socket = client;
var irc = null;
var nickname = null;
+ var password = null;
+
client.on('message', function(data) {
var obj = JSON.parse(data);
if (obj.hasOwnProperty('nickname')) {
if (irc === null) {
nickname = obj.nickname;
+ password = obj.password;
irc = new ircjs({
server: 'irc.freenode.net',
port: 6667,
@@ -105,7 +108,7 @@ io.sockets.on('connection', function (client) {
* we issue a call to join.
*/
irc.connect(function () {
- irc.join(cfg.channel);
+ irc.join(cfg.channel, password);
});
/*
@@ -124,7 +127,7 @@ io.sockets.on('connection', function (client) {
}));
} else {
irc.privmsg(message.person.nick,
- "Automatic: I am using a web client. I can only talk on channel #lullabuddies.");
+ "Automatic: I am using a web client. I can only talk on channel " + cfg.channel + ".");
}
});
@@ -140,6 +143,42 @@ io.sockets.on('connection', function (client) {
});
/*
+ * Handler for the away component of a WHOIS response.
+ */
+ irc.addListener('301', function(message) {
+ client.send(JSON.stringify({
+ messagetype: "away",
+ nick: message.params[1],
+ message: message.params[2]
+ }));
+ });
+
+ /*
+ * Handler for the user info component of a WHOIS response.
+ */
+ irc.addListener('311', function(raw) {
+ console.log(raw);
+ console.log(raw.params);
+ client.send(JSON.stringify({
+ messagetype: "whois-info",
+ nick: raw.params[1],
+ username: raw.params[2],
+ address: raw.params[3],
+ info: raw.params[5]
+ }));
+ });
+
+ /*
+ * Handler for the end of a WHOIS response.
+ */
+ irc.addListener('318', function(message) {
+ client.send(JSON.stringify({
+ messagetype: "whois-end",
+ nick: message.params[1],
+ }));
+ });
+
+ /*
* Handler for the topic, 332
*/
irc.addListener('332', function (raw) {
@@ -330,7 +369,30 @@ io.sockets.on('connection', function (client) {
*/
switch (obj.messagetype) {
case "message":
- irc.privmsg(cfg.channel, (obj.message));
+ if (obj.message.indexOf("/nick ") === 0) {
+ nick = obj.message.substr(6);
+ irc.nick(nick);
+ client.send(JSON.stringify({
+ messagetype: "nick",
+ message: nick
+ }));
+ }
+ else if (obj.message.indexOf("/away") === 0) {
+ var away_message = obj.message.substr(6);
+ if (away_message == "") {
+ irc.raw("AWAY");
+ }
+ else {
+ irc.raw("AWAY :" + away_message);
+ }
+ }
+ else if (obj.message.indexOf("/whois ") === 0) {
+ nick = obj.message.substr(7);
+ irc.raw("WHOIS " + nick);
+ }
+ else {
+ irc.privmsg(cfg.channel, (obj.message));
+ }
break;
default:
console.log(data);
View
10 public/css/irc.css
@@ -123,7 +123,7 @@ a {
color: #ffd720;
}
.box {
- width:17em;
+ width:25em;
padding:50px 10px 30px 10px;
margin-bottom: 45px;
border-top: 1px solid #C9C9C9;
@@ -204,6 +204,14 @@ a {
padding-top: 5px;
font-size: 15px;
}
+
+#nick_list li.info {
+ border-bottom: none;
+ font-size: 10px;
+ word-wrap: break-word;
+ margin-left: 5px;
+}
+
#nick_list span.hints {
font-size: small;
display: block;
View
1  public/index.html
@@ -20,6 +20,7 @@
<p class="off" id="login-msg"></p>
<form id="join-form">
<input type="text" id="nick" class="btn" placeholder="username">
+ <input type="password" id="password" class="btn" placeholder="channel password">
<button href="#" class="btn btn-info" id="join"> Join</button>
</form>
</div>
View
162 public/js/app.js
@@ -17,14 +17,6 @@ $(document).ready(function(){
var doNotReconnect = false; //prohibit reconnect to freenode server after a socket disconnect, no retries
var motdPrevLineEmpty = false; //flag for determining if the prev motd line was only spaces and asterisks
- //used in tab completion
- var prevKeyWasTab = false;
- var pattern = ""; //text fragment respective pattern to look for
- var candidate = ""; //candidate
- var source = []; //array of values to be matched
- var sourcePos = 0; //the search sartting position
- //-
-
window.counter = 0;
$('#nick').focus();
@@ -264,12 +256,12 @@ $(document).ready(function(){
if (webNicks.length > 0) {
for (var i = 0; i < nicks.length; i++) {
idx = webNicks.indexOf(nicks[i]);
- (idx != -1) ? (content += '<li><p><span class="webnick">' + nicks[i] + '</span></p></li>') :
- (content += '<li>' + nicks[i] + '</li>');
+ (idx != -1) ? (content += '<li data-nick="' + nicks[i] + '"><p><span class="webnick">' + nicks[i] + '</span></p></li>') :
+ (content += '<li data-nick="' + nicks[i] + '">' + nicks[i] + '</li>');
}
} else {
for (var i = 0; i < nicks.length; i++) {
- content += '<li>' + nicks[i] + '</li>';
+ content += '<li data-nick="' + nicks[i] + '">' + nicks[i] + '</li>';
}
}
nick_ul.html(content);
@@ -282,6 +274,13 @@ $(document).ready(function(){
if (obj && obj.messagetype) {
var isSelf = (obj.from == nickname) ? true : false;
switch (obj.messagetype) {
+ case "nick":
+ nicks.splice(nicks.indexOf(nickname), 1);
+ nickname = window.nick = obj.message;
+ nicks.push(nickname);
+ nicks.sort(cisort);
+ nicksToList();
+ break;
case "433": //nick already in use
window.spinner.stop();
sock.disconnect();
@@ -329,6 +328,21 @@ $(document).ready(function(){
case "endnames":
nicks.sort(cisort);
nicksToList();
+ for (var i = 0; i < nicks.length; i++) {
+ nick = nicks[i];
+ if (nick.charAt(0) == '@') {
+ nick = nick.substring(1);
+ }
+
+ // Don't do away checking for ourselves.
+ if (nick == nickname) {
+ continue;
+ }
+
+ // Set the initial away check for all channel members.
+ setTimeout(perNickAwayPoller(nick, i), (i + 1) * 1000);
+ }
+
break;
/*
* motd is currently disabled
@@ -360,14 +374,17 @@ $(document).ready(function(){
nicks.push(obj.from);
nicks.sort(cisort);
nicksToList();
+ // Query WHOIS right away.
+ setTimeout(perNickAwayPoller(obj.from, 0), 1000);
}
+
requestStatistics();
break;
case "quit":
case "part":
appendEvent(obj.from, obj.messagetype, isSelf);
for (var i = 0; i < nicks.length; i++) {
- if (nicks[i] == obj.from) {
+ if (nicks[i] == obj.from || nicks[i] == "@" + obj.from) {
nicks.splice(i,1);
break;
}
@@ -389,6 +406,64 @@ $(document).ready(function(){
webNicks = obj.wu;
nicksToList();
break;
+ case "away":
+ $('ul#nick_ul li:not(".info")').each(function() {
+ // Ignore operator prefixes.
+ var nick = $(this).data('nick');
+ if (nick.charAt(0) == '@') {
+ nick = nick.substring(1);
+ }
+ if (nick == obj.nick) {
+ $(this).css('font-style', 'italic');
+ $(this).attr('title', obj.message);
+
+ // Store the time this nickname's away status was last
+ // updated. We need to do this due to the fact that
+ // clearing an away status is not a push operation, but
+ // a poll on WHOIS by the client with a missing 301
+ // resposne.
+ $(this).data('nick-time', Math.round(new Date().getTime() / 1000));
+ }
+ });
+ break;
+ case "whois-info":
+ $('ul#nick_ul li').each(function() {
+ // Ignore operator prefixes.
+ var nick = $(this).data('nick');
+ if (typeof nick != 'undefined' && nick.charAt(0) == '@') {
+ nick = nick.substring(1);
+ }
+
+ if (nick == obj.nick) {
+ var contents = obj.info + "<br />" + obj.address;
+ var $info = $(this).find('li.info');
+ if ($info.length == 0) {
+ $(this).append('<li class="info"></li>');
+ $info = $(this).find('li.info');
+ }
+ $info.html(contents);
+ }
+ });
+ break;
+ case "whois-end":
+ $('ul#nick_ul li:not(".info")').each(function() {
+ // Ignore operator prefixes.
+ var nick = $(this).data('nick');
+ var nick_time = $(this).data('nick-time');
+ if (nick.charAt(0) == '@') {
+ nick = nick.substring(1);
+ }
+ // Clear away statuses if it looks like we did a WHOIS
+ // that didn't respond with an away status. We give
+ // ourselves a nice long 10 seconds for the response
+ // to a WHOIS to finish.
+ if (nick == obj.nick && nick_time < (Math.round(new Date().getTime() / 1000) - 10)) {
+ $(this).css('font-style', '');
+ $(this).attr('title', '');
+ $(this).data('nick-time', '');
+ }
+ });
+ break;
default:
alert(data);
break;
@@ -406,10 +481,11 @@ $(document).ready(function(){
$('#login-msg').text("");
loginStatus.html("");
var nick = window.nick = getNickname($('#nick').val());
+ var password = window.password = $('#password').val();
$('#login-msg').text("Joining as " + nick + "...");
$('#join').prop("disabled", "disabled");
c.setIrcNoticesEnabled(false);
- sock.send(JSON.stringify({ nickname: nick }));
+ sock.send(JSON.stringify({ nickname: nick, password: password }));
//start spinner
window.target = document.getElementById('join-form');
window.spinner = new Spinner(c.getOpts()).spin(window.target);
@@ -441,6 +517,41 @@ $(document).ready(function(){
};
/*
+ * setTimeout() callback to poll each username's WHOIS response to look for
+ * away statuses.
+ *
+ * @param n
+ * The nickname to query.
+ * @param delay
+ * The delay in seconds to add to the next WHOIS check, on top of the
+ * default 60 second wait.
+ */
+ var perNickAwayPoller = function(n, delay) {
+ // We return an anonymous function so that we can
+ // keep the current nickname in scope, instead of
+ // referencing the last nickname in the array.
+ // @url http://stackoverflow.com/questions/6564814/passing-argument-to-settimeout-in-a-for-loop
+ return function() {
+
+ // Only check and continue to check if the user is
+ // still part of this channel.
+ if (nicks.indexOf(n) >= 0) {
+ sock.send(JSON.stringify({
+ messagetype: "message",
+ message: "/whois " + n
+ }));
+
+ // To avoid flooding the server we space our
+ // WHOIS queries by one second and check only
+ // once per minute per nickname. Since we support
+ // /whois from the client, the end user can always
+ // force a referesh on a given nickname.
+ setTimeout(perNickAwayPoller(n, delay), delay * 1000 + 60 * 1000);
+ }
+ }
+ };
+
+ /*
* requesting statistics is user action triggered
* in case it is proven to be to resource intensive
* another solution can be sought, e.g., on a timer
@@ -455,8 +566,31 @@ $(document).ready(function(){
chatForm.on('submit',function(e){
e.preventDefault();
- if (textInput.val() !== '') {
+ var message = textInput.val();
+ if (message !== '') {
sendMessage();
+
+ // Update our away status if needed.
+ if (message.indexOf("/away") === 0) {
+ var away_message = message.substr(6);
+ $('ul#nick_ul li:not(".info")').each(function() {
+ // Ignore operator prefixes.
+ var nick = $(this).data('nick');
+ if (nick.charAt(0) == '@') {
+ nick = nick.substring(1);
+ }
+ if (nick == nickname) {
+ if (away_message == "") {
+ $(this).css('font-style', '');
+ $(this).attr('title', '');
+ }
+ else {
+ $(this).css('font-style', 'italic');
+ $(this).attr('title', away_message);
+ }
+ }
+ });
+ }
} else {
alert('<p> You need to input a name</p>');
}

No commit comments for this range

Something went wrong with that request. Please try again.