diff --git a/client/css/style.css b/client/css/style.css
index 1e59d942..cb9f65eb 100644
--- a/client/css/style.css
+++ b/client/css/style.css
@@ -789,7 +789,8 @@ button,
#chat .nick .text,
#chat .part .text,
#chat .quit .text,
-#chat .topic .text {
+#chat .topic .text,
+#chat .topic_set_by .text {
color: #999;
}
@@ -826,6 +827,11 @@ button,
color: #2ecc40;
}
+#chat .ctcp .from:before {
+ font-family: FontAwesome;
+ content: "\f0f6";
+}
+
#chat .whois .from:before {
font-family: FontAwesome;
content: "\f007";
@@ -867,6 +873,10 @@ button,
color: #f00;
}
+#chat .msg.toggle .time {
+ visibility: hidden;
+}
+
#chat .toggle-button {
background: #f5f5f5;
border-radius: 2px;
diff --git a/client/js/libs/handlebars/date.js b/client/js/libs/handlebars/date.js
new file mode 100644
index 00000000..751e713d
--- /dev/null
+++ b/client/js/libs/handlebars/date.js
@@ -0,0 +1,7 @@
+Handlebars.registerHelper(
+ "localeDate", function(date) {
+ date = new Date(date);
+
+ return date.toLocaleString();
+ }
+);
diff --git a/client/js/libs/handlebars/tojson.js b/client/js/libs/handlebars/tojson.js
new file mode 100644
index 00000000..7df32f77
--- /dev/null
+++ b/client/js/libs/handlebars/tojson.js
@@ -0,0 +1,7 @@
+"use strict";
+
+Handlebars.registerHelper(
+ "toJSON", function(context) {
+ return JSON.stringify(context);
+ }
+);
diff --git a/client/js/lounge.js b/client/js/lounge.js
index 7dfa56d8..49bb54ae 100644
--- a/client/js/lounge.js
+++ b/client/js/lounge.js
@@ -221,8 +221,10 @@ $(function() {
"part",
"quit",
"topic",
+ "topic_set_by",
"action",
"whois",
+ "ctcp",
].indexOf(type) !== -1) {
data.msg.template = "actions/" + type;
msg = $(render("msg_action", data.msg));
@@ -309,6 +311,10 @@ $(function() {
sortable();
});
+ socket.on("network_changed", function(data) {
+ sidebar.find("#network-" + data.network).data("options", data.serverOptions);
+ });
+
socket.on("nick", function(data) {
var id = data.network;
var nick = data.nick;
@@ -769,7 +775,7 @@ $(function() {
if (msg.type === "invite") {
title = "New channel invite:";
- body = msg.from + " invited you to " + msg.text;
+ body = msg.from + " invited you to " + msg.channel;
} else {
title = msg.from;
if (!isQuery) {
diff --git a/client/views/actions/ctcp.tpl b/client/views/actions/ctcp.tpl
new file mode 100644
index 00000000..30334536
--- /dev/null
+++ b/client/views/actions/ctcp.tpl
@@ -0,0 +1,2 @@
+{{from}}
+{{ctcpType}} {{ctcpMessage}}
diff --git a/client/views/actions/invite.tpl b/client/views/actions/invite.tpl
index 6e689ac1..6b9cecac 100644
--- a/client/views/actions/invite.tpl
+++ b/client/views/actions/invite.tpl
@@ -3,7 +3,7 @@ invited
{{#if invitedYou}}
you
{{else}}
- {{target}}
+ {{invited}}
{{/if}}
to
-{{{parse text}}}
+{{{parse channel}}}
diff --git a/client/views/actions/nick.tpl b/client/views/actions/nick.tpl
index dc6ed133..2481e914 100644
--- a/client/views/actions/nick.tpl
+++ b/client/views/actions/nick.tpl
@@ -1,3 +1,3 @@
-{{mode}}{{from}}
+{{mode}}{{nick}}
is now known as
-{{mode}}{{text}}
+{{mode}}{{new_nick}}
diff --git a/client/views/actions/topic.tpl b/client/views/actions/topic.tpl
index 56962794..92fe29ae 100644
--- a/client/views/actions/topic.tpl
+++ b/client/views/actions/topic.tpl
@@ -1,8 +1,8 @@
-{{#if isSetByChan}}
- The topic is:
-{{else}}
+{{#if from}}
{{mode}}{{from}}
has changed the topic to:
+{{else}}
+ The topic is:
{{/if}}
{{{parse text}}}
diff --git a/client/views/actions/topic_set_by.tpl b/client/views/actions/topic_set_by.tpl
new file mode 100644
index 00000000..120c757d
--- /dev/null
+++ b/client/views/actions/topic_set_by.tpl
@@ -0,0 +1 @@
+Topic set by {{mode}}{{nick}} on {{localeDate when}}
diff --git a/client/views/actions/whois.tpl b/client/views/actions/whois.tpl
index 0e17a02c..ced3326b 100644
--- a/client/views/actions/whois.tpl
+++ b/client/views/actions/whois.tpl
@@ -1,26 +1,35 @@
+{{#if whois.account}}
+
+{{/if}}
{{#if whois.channels}}
-
{{whois.nickname}}
- is on the following channels:
- {{#each whois.channels}}
- {{{parse this}}}
- {{/each}}
+
{{whois.nick}}
+ is on the following channels: {{{parse whois.channels}}}
{{/if}}
{{#if whois.server}}
+{{/if}}
+{{#if whois.secure}}
+
{{/if}}
{{#if whois.away}}
{{/if}}
diff --git a/client/views/network.tpl b/client/views/network.tpl
index feec605d..c10162a0 100644
--- a/client/views/network.tpl
+++ b/client/views/network.tpl
@@ -1,5 +1,5 @@
{{#each networks}}
-
+
{{/each}}
diff --git a/package.json b/package.json
index 243688c2..86f0bd94 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,7 @@
"event-stream": "3.3.2",
"express": "4.13.4",
"lodash": "4.6.1",
+ "irc-framework": "1.0.10",
"mkdirp": "0.5.1",
"moment": "2.12.0",
"read": "1.0.7",
diff --git a/src/client.js b/src/client.js
index 9e0856e0..c0ddd323 100644
--- a/src/client.js
+++ b/src/client.js
@@ -1,19 +1,17 @@
var _ = require("lodash");
var Chan = require("./models/chan");
var crypto = require("crypto");
-var identd = require("./identd");
var log = require("./log");
-var net = require("net");
var Msg = require("./models/msg");
var Network = require("./models/network");
-var slate = require("slate-irc");
-var tls = require("tls");
+var ircFramework = require("irc-framework");
var Helper = require("./helper");
module.exports = Client;
var id = 0;
var events = [
+ "connection",
"ctcp",
"error",
"invite",
@@ -25,7 +23,6 @@ var events = [
"link",
"names",
"nick",
- "notice",
"part",
"quit",
"topic",
@@ -33,7 +30,7 @@ var events = [
"whois"
];
var inputs = [
- // These inputs are sorted in order that is most likely to be used
+ "ctcp",
"msg",
"part",
"action",
@@ -50,7 +47,7 @@ var inputs = [
var path = "./plugins/inputs/" + name;
var plugin = require(path);
plugin.commands.forEach(function(command) {
- plugins[command] = plugin.input;
+ plugins[command] = plugin;
});
return plugins;
}, {});
@@ -129,106 +126,68 @@ Client.prototype.connect = function(args) {
var config = Helper.getConfig();
var client = this;
- if (config.lockNetwork) {
- // This check is needed to prevent invalid user configurations
- if (args.host && args.host.length > 0 && args.host !== config.defaults.host) {
- var invalidHostnameMsg = new Msg({
- type: Msg.Type.ERROR,
- text: "Hostname you specified is not allowed."
- });
- client.emit("msg", {
- msg: invalidHostnameMsg
- });
- return;
- }
+ var nick = args.nick || "lounge-user";
- args.host = config.defaults.host;
- args.port = config.defaults.port;
- args.tls = config.defaults.tls;
- }
-
- var server = {
+ var network = new Network({
name: args.name || "",
host: args.host || "",
port: parseInt(args.port, 10) || (args.tls ? 6697 : 6667),
- rejectUnauthorized: false
- };
-
- if (server.host.length === 0) {
- var emptyHostnameMsg = new Msg({
- type: Msg.Type.ERROR,
- text: "You must specify a hostname to connect."
- });
- client.emit("msg", {
- msg: emptyHostnameMsg
- });
- return;
- }
-
- if (config.bind) {
- server.localAddress = config.bind;
- if (args.tls) {
- var socket = net.connect(server);
- server.socket = socket;
- }
- }
-
- var stream = args.tls ? tls.connect(server) : net.connect(server);
-
- stream.on("error", function(e) {
- console.log("Client#connect():\n" + e);
- stream.end();
- var msg = new Msg({
- type: Msg.Type.ERROR,
- text: "Connection error."
- });
- client.emit("msg", {
- msg: msg
- });
- });
-
- var nick = args.nick || "lounge-user";
- var username = args.username || nick.replace(/[^a-zA-Z0-9]/g, "");
- var realname = args.realname || "The Lounge User";
-
- var irc = slate(stream);
- identd.hook(stream, username);
-
- if (args.password) {
- irc.pass(args.password);
- }
-
- irc.me = nick;
- irc.nick(nick);
- irc.user(username, realname);
-
- var network = new Network({
- name: server.name,
- host: server.host,
- port: server.port,
tls: !!args.tls,
password: args.password,
- username: username,
- realname: realname,
+ username: args.username || nick.replace(/[^a-zA-Z0-9]/g, ""),
+ realname: args.realname || "The Lounge User",
commands: args.commands
});
- network.irc = irc;
-
client.networks.push(network);
client.emit("network", {
network: network
});
- events.forEach(function(plugin) {
- var path = "./plugins/irc-events/" + plugin;
- require(path).apply(client, [
- irc,
- network
- ]);
+ if (config.lockNetwork) {
+ // This check is needed to prevent invalid user configurations
+ if (args.host && args.host.length > 0 && args.host !== config.defaults.host) {
+ client.emit("msg", {
+ chan: network.channels[0].id,
+ msg: new Msg({
+ type: Msg.Type.ERROR,
+ text: "Hostname you specified is not allowed."
+ })
+ });
+ return;
+ }
+
+ network.host = config.defaults.host;
+ network.port = config.defaults.port;
+ network.tls = config.defaults.tls;
+ }
+
+ if (network.host.length === 0) {
+ client.emit("msg", {
+ chan: network.channels[0].id,
+ msg: new Msg({
+ type: Msg.Type.ERROR,
+ text: "You must specify a hostname to connect."
+ })
+ });
+ return;
+ }
+
+ network.irc = new ircFramework.Client();
+ network.irc.connect({
+ host: network.host,
+ port: network.port,
+ nick: nick,
+ username: network.username,
+ gecos: network.realname,
+ password: network.password,
+ tls: network.tls,
+ localAddress: config.bind,
+ rejectUnauthorized: false,
+ auto_reconnect: false, // TODO: Enable auto reconnection
});
- irc.once("welcome", function() {
+ network.irc.on("registered", function() {
var delay = 1000;
var commands = args.commands;
if (Array.isArray(commands)) {
@@ -242,18 +201,23 @@ Client.prototype.connect = function(args) {
delay += 1000;
});
}
- setTimeout(function() {
- irc.write("PING " + network.host);
- }, delay);
- });
- irc.once("pong", function() {
var join = (args.join || "");
if (join) {
- join = join.replace(/\,/g, " ").split(/\s+/g);
- irc.join(join);
+ setTimeout(function() {
+ join = join.split(/\s+/);
+ network.irc.join(join[0], join[1]);
+ }, delay);
}
});
+
+ events.forEach(function(plugin) {
+ var path = "./plugins/irc-events/" + plugin;
+ require(path).apply(client, [
+ network.irc,
+ network
+ ]);
+ });
};
Client.prototype.setPassword = function(hash) {
@@ -286,10 +250,27 @@ Client.prototype.input = function(data) {
var args = text.split(" ");
var cmd = args.shift().toLowerCase();
+ var irc = target.network.irc;
+ var connected = irc && irc.connection && irc.connection.connected;
+
if (cmd in inputs) {
- inputs[cmd].apply(client, [target.network, target.chan, cmd, args]);
- } else {
- target.network.irc.write(text);
+ var plugin = inputs[cmd];
+ if (connected || plugin.allowDisconnected) {
+ connected = true;
+ plugin.input.apply(client, [target.network, target.chan, cmd, args]);
+ }
+ } else if (connected) {
+ irc.raw(text);
+ }
+
+ if (!connected) {
+ this.emit("msg", {
+ chan: target.chan.id,
+ msg: new Msg({
+ type: Msg.Type.ERROR,
+ text: "You are not connected to the IRC network, unable to send your command."
+ })
+ });
}
};
@@ -375,11 +356,8 @@ Client.prototype.quit = function() {
}
}
this.networks.forEach(function(network) {
- var irc = network.irc;
- if (network.connected) {
- irc.quit();
- } else {
- irc.stream.end();
+ if (network.irc) {
+ network.irc.quit("Page closed");
}
});
};
diff --git a/src/models/chan.js b/src/models/chan.js
index a3501097..a6f232b8 100644
--- a/src/models/chan.js
+++ b/src/models/chan.js
@@ -23,18 +23,21 @@ function Chan(attr) {
}, attr));
}
-Chan.prototype.sortUsers = function() {
- this.users = _.sortBy(
- this.users,
- function(u) { return u.name.toLowerCase(); }
- );
+Chan.prototype.sortUsers = function(irc) {
+ var userModeSortPriority = {};
+ irc.network.options.PREFIX.forEach(function(prefix, index) {
+ userModeSortPriority[prefix.symbol] = index;
+ });
- ["+", "%", "@", "&", "~"].forEach(function(mode) {
- this.users = _.remove(
- this.users,
- function(u) { return u.mode === mode; }
- ).concat(this.users);
- }, this);
+ userModeSortPriority[""] = 99; // No mode is lowest
+
+ this.users = this.users.sort(function(a, b) {
+ if (a.mode === b.mode) {
+ return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
+ }
+
+ return userModeSortPriority[a.mode] - userModeSortPriority[b.mode];
+ });
};
Chan.prototype.getMode = function(name) {
diff --git a/src/models/msg.js b/src/models/msg.js
index d501c4d7..0f29d5ab 100644
--- a/src/models/msg.js
+++ b/src/models/msg.js
@@ -14,7 +14,9 @@ Msg.Type = {
PART: "part",
QUIT: "quit",
TOGGLE: "toggle",
+ CTCP: "ctcp",
TOPIC: "topic",
+ TOPIC_SET_BY: "topic_set_by",
WHOIS: "whois"
};
@@ -27,8 +29,13 @@ function Msg(attr) {
from: "",
id: id++,
text: "",
- time: new Date(),
type: Msg.Type.MESSAGE,
self: false
}, attr));
+
+ if (this.time > 0) {
+ this.time = new Date(this.time);
+ } else {
+ this.time = new Date();
+ }
}
diff --git a/src/models/network.js b/src/models/network.js
index 0b641177..1df03704 100644
--- a/src/models/network.js
+++ b/src/models/network.js
@@ -16,9 +16,11 @@ function Network(attr) {
username: "",
realname: "",
channels: [],
- connected: false,
id: id++,
irc: null,
+ serverOptions: {
+ PREFIX: [],
+ },
}, attr));
this.name = attr.name || prettify(attr.host);
this.channels.unshift(
@@ -30,7 +32,7 @@ function Network(attr) {
}
Network.prototype.toJSON = function() {
- var json = _.extend(this, {nick: (this.irc || {}).me || ""});
+ var json = _.extend(this, {nick: (this.irc && this.irc.user.nick) || ""});
return _.omit(json, "irc", "password");
};
@@ -45,7 +47,7 @@ Network.prototype.export = function() {
"realname",
"commands"
]);
- network.nick = (this.irc || {}).me;
+ network.nick = (this.irc && this.irc.user.nick) || "";
network.join = _.map(
_.filter(this.channels, {type: "channel"}),
"name"
@@ -53,6 +55,14 @@ Network.prototype.export = function() {
return network;
};
+Network.prototype.getChannel = function(name) {
+ name = name.toLowerCase();
+
+ return _.find(this.channels, function(that) {
+ return that.name.toLowerCase() === name;
+ });
+};
+
function prettify(host) {
var name = capitalize(host.split(".")[1]);
if (!name) {
diff --git a/src/models/user.js b/src/models/user.js
index 755e53dd..68d251d1 100644
--- a/src/models/user.js
+++ b/src/models/user.js
@@ -3,6 +3,10 @@ var _ = require("lodash");
module.exports = User;
function User(attr) {
+ // TODO: Remove this
+ attr.name = attr.name || attr.nick;
+ attr.mode = attr.mode || (attr.modes && attr.modes[0]) || "";
+
_.merge(this, _.extend({
mode: "",
name: ""
diff --git a/src/plugins/inputs/action.js b/src/plugins/inputs/action.js
index bdb85d25..4f8376ce 100644
--- a/src/plugins/inputs/action.js
+++ b/src/plugins/inputs/action.js
@@ -2,25 +2,24 @@ exports.commands = ["slap", "me"];
exports.input = function(network, chan, cmd, args) {
var irc = network.irc;
+ var text;
switch (cmd) {
case "slap":
- var slap = "slaps " + args[0] + " around a bit with a large trout";
+ text = "slaps " + args[0] + " around a bit with a large trout";
/* fall through */
case "me":
if (args.length === 0) {
break;
}
- var text = slap || args.join(" ");
- irc.action(
- chan.name,
- text
- );
- irc.emit("message", {
- from: irc.me,
- to: chan.name,
- message: "\u0001ACTION " + text
+ text = text || args.join(" ");
+
+ irc.say(chan.name, "\u0001ACTION " + text + "\u0001");
+ irc.emit("action", {
+ nick: irc.user.nick,
+ target: chan.name,
+ message: text
});
break;
}
diff --git a/src/plugins/inputs/connect.js b/src/plugins/inputs/connect.js
index 9aeba2a7..538cd4a2 100644
--- a/src/plugins/inputs/connect.js
+++ b/src/plugins/inputs/connect.js
@@ -1,4 +1,5 @@
exports.commands = ["connect", "server"];
+exports.allowDisconnected = true;
exports.input = function(network, chan, cmd, args) {
if (args.length === 0) {
diff --git a/src/plugins/inputs/ctcp.js b/src/plugins/inputs/ctcp.js
new file mode 100644
index 00000000..75906b79
--- /dev/null
+++ b/src/plugins/inputs/ctcp.js
@@ -0,0 +1,8 @@
+exports.commands = ["ctcp"];
+
+exports.input = function(network, chan, cmd, args) {
+ if (args.length > 1) {
+ var irc = network.irc;
+ irc.ctcpRequest(args[0], args.slice(1).join(" "));
+ }
+};
diff --git a/src/plugins/inputs/invite.js b/src/plugins/inputs/invite.js
index b81ae2c8..8eefcbac 100644
--- a/src/plugins/inputs/invite.js
+++ b/src/plugins/inputs/invite.js
@@ -4,9 +4,9 @@ exports.input = function(network, chan, cmd, args) {
var irc = network.irc;
if (args.length === 2) {
- irc.invite(args[0], args[1]); // Channel provided in the command
+ irc.raw("INVITE", args[0], args[1]); // Channel provided in the command
} else if (args.length === 1 && chan.type === "channel") {
- irc.invite(args[0], chan.name); // Current channel
+ irc.raw("INVITE", args[0], chan.name); // Current channel
}
return true;
diff --git a/src/plugins/inputs/kick.js b/src/plugins/inputs/kick.js
index a57d6cd4..d0b74802 100644
--- a/src/plugins/inputs/kick.js
+++ b/src/plugins/inputs/kick.js
@@ -3,7 +3,7 @@ exports.commands = ["kick"];
exports.input = function(network, chan, cmd, args) {
if (args.length !== 0) {
var irc = network.irc;
- irc.kick(chan.name, args[0]);
+ irc.raw("KICK", chan.name, args[0], args.slice(1).join(" "));
}
return true;
diff --git a/src/plugins/inputs/mode.js b/src/plugins/inputs/mode.js
index 5ca106be..b476256d 100644
--- a/src/plugins/inputs/mode.js
+++ b/src/plugins/inputs/mode.js
@@ -21,12 +21,9 @@ exports.input = function(network, chan, cmd, args) {
mode = args[0];
user = args[1];
}
+
var irc = network.irc;
- irc.mode(
- chan.name,
- mode,
- user
- );
+ irc.raw("MODE", chan.name, mode, user);
return true;
};
diff --git a/src/plugins/inputs/msg.js b/src/plugins/inputs/msg.js
index 27a27db3..a562b607 100644
--- a/src/plugins/inputs/msg.js
+++ b/src/plugins/inputs/msg.js
@@ -1,11 +1,10 @@
-var _ = require("lodash");
-
exports.commands = ["msg", "say"];
exports.input = function(network, chan, cmd, args) {
if (args.length === 0 || args[0] === "") {
return true;
}
+
var irc = network.irc;
var target = "";
if (cmd === "msg") {
@@ -16,13 +15,15 @@ exports.input = function(network, chan, cmd, args) {
} else {
target = chan.name;
}
+
var msg = args.join(" ");
- irc.send(target, msg);
- var channel = _.find(network.channels, {name: target});
+ irc.say(target, msg);
+
+ var channel = network.getChannel(target);
if (typeof channel !== "undefined") {
- irc.emit("message", {
- from: irc.me,
- to: channel.name,
+ irc.emit("privmsg", {
+ nick: irc.user.nick,
+ target: channel.name,
message: msg
});
}
diff --git a/src/plugins/inputs/notice.js b/src/plugins/inputs/notice.js
index 4c5981e5..33aeda00 100644
--- a/src/plugins/inputs/notice.js
+++ b/src/plugins/inputs/notice.js
@@ -1,6 +1,3 @@
-var _ = require("lodash");
-var Msg = require("../../models/msg");
-
exports.commands = ["notice"];
exports.input = function(network, chan, cmd, args) {
@@ -12,22 +9,16 @@ exports.input = function(network, chan, cmd, args) {
var irc = network.irc;
irc.notice(args[0], message);
- var targetChan = _.find(network.channels, {name: args[0]});
+ var targetChan = network.getChannel(args[0]);
if (typeof targetChan === "undefined") {
message = "{to " + args[0] + "} " + message;
targetChan = chan;
}
- var msg = new Msg({
- type: Msg.Type.NOTICE,
- mode: targetChan.getMode(irc.me),
- from: irc.me,
- text: message
- });
- targetChan.messages.push(msg);
- this.emit("msg", {
- chan: targetChan.id,
- msg: msg
+ irc.emit("notice", {
+ nick: irc.user.nick,
+ target: targetChan.name,
+ message: message
});
return true;
diff --git a/src/plugins/inputs/part.js b/src/plugins/inputs/part.js
index d8701544..7c2fcf8e 100644
--- a/src/plugins/inputs/part.js
+++ b/src/plugins/inputs/part.js
@@ -2,6 +2,7 @@ var _ = require("lodash");
var Msg = require("../../models/msg");
exports.commands = ["close", "leave", "part"];
+exports.allowDisconnected = true;
exports.input = function(network, chan, cmd, args) {
if (chan.type === "lobby") {
@@ -15,8 +16,8 @@ exports.input = function(network, chan, cmd, args) {
return;
}
- if (chan.type === "channel") {
- var irc = network.irc;
+ var irc = network.irc;
+ if (irc && chan.type === "channel") {
irc.part(chan.name, args.join(" "));
}
diff --git a/src/plugins/inputs/query.js b/src/plugins/inputs/query.js
index cb093015..616d1ae9 100644
--- a/src/plugins/inputs/query.js
+++ b/src/plugins/inputs/query.js
@@ -1,5 +1,6 @@
var _ = require("lodash");
var Chan = require("../../models/chan");
+var Msg = require("../../models/msg");
exports.commands = ["query"];
@@ -14,11 +15,31 @@ exports.input = function(network, chan, cmd, args) {
return;
}
- // If target doesn't start with an allowed character, ignore
- if (!/^[a-zA-Z_\\\[\]{}^`|]/.test(target)) {
+ var char = target[0];
+ if (network.irc.network.options.CHANTYPES && network.irc.network.options.CHANTYPES.indexOf(char) !== -1) {
+ this.emit("msg", {
+ chan: chan.id,
+ msg: new Msg({
+ type: Msg.Type.ERROR,
+ text: "You can not open query windows for channels, use /join instead."
+ })
+ });
return;
}
+ for (var i = 0; i < network.irc.network.options.PREFIX.length; i++) {
+ if (network.irc.network.options.PREFIX[i].symbol === char) {
+ this.emit("msg", {
+ chan: chan.id,
+ msg: new Msg({
+ type: Msg.Type.ERROR,
+ text: "You can not open query windows for names starting with a user prefix."
+ })
+ });
+ return;
+ }
+ }
+
var newChan = new Chan({
type: Chan.Type.QUERY,
name: target
diff --git a/src/plugins/inputs/quit.js b/src/plugins/inputs/quit.js
index fb208b2f..7f54fccf 100644
--- a/src/plugins/inputs/quit.js
+++ b/src/plugins/inputs/quit.js
@@ -1,6 +1,7 @@
var _ = require("lodash");
exports.commands = ["quit", "disconnect"];
+exports.allowDisconnected = true;
exports.input = function(network, chan, cmd, args) {
var client = this;
@@ -13,7 +14,9 @@ exports.input = function(network, chan, cmd, args) {
network: network.id
});
- irc.quit(quitMessage);
+ if (irc) {
+ irc.quit(quitMessage);
+ }
return true;
};
diff --git a/src/plugins/inputs/raw.js b/src/plugins/inputs/raw.js
index cab47bd8..b58a993c 100644
--- a/src/plugins/inputs/raw.js
+++ b/src/plugins/inputs/raw.js
@@ -3,7 +3,7 @@ exports.commands = ["raw", "send", "quote"];
exports.input = function(network, chan, cmd, args) {
if (args.length !== 0) {
var irc = network.irc;
- irc.write(args.join(" "));
+ irc.raw(args);
}
return true;
diff --git a/src/plugins/inputs/topic.js b/src/plugins/inputs/topic.js
index d342223f..d9e66852 100644
--- a/src/plugins/inputs/topic.js
+++ b/src/plugins/inputs/topic.js
@@ -1,12 +1,8 @@
exports.commands = ["topic"];
exports.input = function(network, chan, cmd, args) {
- var msg = "TOPIC";
- msg += " " + chan.name;
- msg += args[0] ? (" :" + args.join(" ")) : "";
-
var irc = network.irc;
- irc.write(msg);
+ irc.raw("TOPIC", chan.name, args.join(" "));
return true;
};
diff --git a/src/plugins/irc-events/connection.js b/src/plugins/irc-events/connection.js
new file mode 100644
index 00000000..5635c74a
--- /dev/null
+++ b/src/plugins/irc-events/connection.js
@@ -0,0 +1,75 @@
+var _ = require("lodash");
+var identd = require("../../identd");
+var Msg = require("../../models/msg");
+
+module.exports = function(irc, network) {
+ var client = this;
+
+ client.emit("msg", {
+ chan: network.channels[0].id,
+ msg: new Msg({
+ text: "Network created, connecting to " + network.host + ":" + network.port + "..."
+ })
+ });
+
+ irc.on("raw socket connected", function() {
+ identd.hook(irc.connection.socket, network.username);
+ });
+
+ irc.on("socket connected", function() {
+ client.emit("msg", {
+ chan: network.channels[0].id,
+ msg: new Msg({
+ text: "Connected to the network."
+ })
+ });
+ });
+
+ irc.on("socket close", function() {
+ client.emit("msg", {
+ chan: network.channels[0].id,
+ msg: new Msg({
+ text: "Disconnected from the network."
+ })
+ });
+ });
+
+ irc.on("socket error", function(err) {
+ console.log(err);
+ client.emit("msg", {
+ chan: network.channels[0].id,
+ msg: new Msg({
+ type: Msg.Type.ERROR,
+ text: "Socket error: " + err
+ })
+ });
+ });
+
+ irc.on("reconnecting", function() {
+ client.emit("msg", {
+ chan: network.channels[0].id,
+ msg: new Msg({
+ text: "Reconnecting..."
+ })
+ });
+ });
+
+ irc.on("server options", function(data) {
+ if (network.serverOptions.PREFIX === data.options.PREFIX) {
+ return;
+ }
+
+ network.prefixLookup = {};
+
+ _.each(data.options.PREFIX, function(mode) {
+ network.prefixLookup[mode.mode] = mode.symbol;
+ });
+
+ network.serverOptions.PREFIX = data.options.PREFIX;
+
+ client.emit("network_changed", {
+ network: network.id,
+ serverOptions: network.serverOptions
+ });
+ });
+};
diff --git a/src/plugins/irc-events/ctcp.js b/src/plugins/irc-events/ctcp.js
index 42b7fed8..d6cbd1a7 100644
--- a/src/plugins/irc-events/ctcp.js
+++ b/src/plugins/irc-events/ctcp.js
@@ -1,22 +1,38 @@
var pkg = require(process.cwd() + "/package.json");
+var Msg = require("../../models/msg");
-module.exports = function(irc/* , network */) {
- irc.on("message", function(data) {
- if (data.message.indexOf("\001") !== 0) {
- return;
+module.exports = function(irc, network) {
+ var client = this;
+
+ irc.on("ctcp response", function(data) {
+ var chan = network.getChannel(data.nick);
+ if (typeof chan === "undefined") {
+ chan = network.channels[0];
}
- var msg = data.message.replace(/\001/g, "");
- var split = msg.split(" ");
- switch (split[0]) {
+
+ var msg = new Msg({
+ type: Msg.Type.CTCP,
+ time: data.time,
+ from: data.nick,
+ ctcpType: data.type,
+ ctcpMessage: data.message
+ });
+ chan.messages.push(msg);
+ client.emit("msg", {
+ chan: chan.id,
+ msg: msg
+ });
+ });
+
+ irc.on("ctcp request", function(data) {
+ switch (data.type) {
case "VERSION":
- irc.ctcp(
- data.from,
- "VERSION " + pkg.name + " " + pkg.version
- );
+ irc.ctcpResponse(data.nick, "VERSION", pkg.name + " " + pkg.version);
break;
case "PING":
+ var split = data.message.split(" ");
if (split.length === 2) {
- irc.ctcp(data.from, "PING " + split[1]);
+ irc.ctcpResponse(data.nick, "PING", split[1]);
}
break;
}
diff --git a/src/plugins/irc-events/error.js b/src/plugins/irc-events/error.js
index c56a36cc..8726b0d3 100644
--- a/src/plugins/irc-events/error.js
+++ b/src/plugins/irc-events/error.js
@@ -2,21 +2,61 @@ var Msg = require("../../models/msg");
module.exports = function(irc, network) {
var client = this;
- irc.on("errors", function(data) {
+
+ // TODO: remove later
+ irc.on("irc_error", function(data) {
+ console.log("Got an irc_error");
+ irc.emit("error", data);
+ });
+
+ irc.on("error", function(data) {
+ console.log("error", data);
+ var text = data.error;
+ if (data.reason) {
+ text = data.reason + " (" + text + ")";
+ }
var lobby = network.channels[0];
var msg = new Msg({
type: Msg.Type.ERROR,
- text: data.message,
+ text: text,
});
client.emit("msg", {
chan: lobby.id,
msg: msg
});
- if (!network.connected) {
- if (data.cmd === "ERR_NICKNAMEINUSE") {
- var random = irc.me + Math.floor(10 + (Math.random() * 89));
- irc.nick(random);
- }
+ });
+
+ irc.on("nick in use", function(data) {
+ var lobby = network.channels[0];
+ var msg = new Msg({
+ type: Msg.Type.ERROR,
+ text: data.nick + ": " + (data.reason || "Nickname is already in use."),
+ });
+ client.emit("msg", {
+ chan: lobby.id,
+ msg: msg
+ });
+
+ if (irc.connection.registered === false) {
+ var random = (data.nick || irc.user.nick) + Math.floor(10 + (Math.random() * 89));
+ irc.changeNick(random);
+ }
+ });
+
+ irc.on("nick invalid", function(data) {
+ var lobby = network.channels[0];
+ var msg = new Msg({
+ type: Msg.Type.ERROR,
+ text: data.nick + ": " + (data.reason || "Nickname is invalid."),
+ });
+ client.emit("msg", {
+ chan: lobby.id,
+ msg: msg
+ });
+
+ if (irc.connection.registered === false) {
+ var random = "i" + Math.random().toString(36).substr(2, 10); // 'i' so it never begins with a number
+ irc.changeNick(random);
}
});
};
diff --git a/src/plugins/irc-events/invite.js b/src/plugins/irc-events/invite.js
index 6bf1e3b7..7accbb7c 100644
--- a/src/plugins/irc-events/invite.js
+++ b/src/plugins/irc-events/invite.js
@@ -1,22 +1,20 @@
-var _ = require("lodash");
var Msg = require("../../models/msg");
module.exports = function(irc, network) {
var client = this;
irc.on("invite", function(data) {
- var target = data.to;
-
- var chan = _.find(network.channels, {name: data.channel});
+ var chan = network.getChannel(data.channel);
if (typeof chan === "undefined") {
chan = network.channels[0];
}
var msg = new Msg({
type: Msg.Type.INVITE,
- from: data.from,
- target: target,
- text: data.channel,
- invitedYou: target.toLowerCase() === irc.me.toLowerCase()
+ time: data.time,
+ from: data.nick,
+ invited: data.invited,
+ channel: data.channel,
+ invitedYou: data.invited === irc.user.nick
});
chan.messages.push(msg);
client.emit("msg", {
diff --git a/src/plugins/irc-events/join.js b/src/plugins/irc-events/join.js
index 07e0bd0f..d2f6496e 100644
--- a/src/plugins/irc-events/join.js
+++ b/src/plugins/irc-events/join.js
@@ -1,4 +1,3 @@
-var _ = require("lodash");
var Chan = require("../../models/chan");
var Msg = require("../../models/msg");
var User = require("../../models/user");
@@ -6,7 +5,7 @@ var User = require("../../models/user");
module.exports = function(irc, network) {
var client = this;
irc.on("join", function(data) {
- var chan = _.find(network.channels, {name: data.channel});
+ var chan = network.getChannel(data.channel);
if (typeof chan === "undefined") {
chan = new Chan({
name: data.channel
@@ -18,20 +17,17 @@ module.exports = function(irc, network) {
chan: chan
});
}
- chan.users.push(new User({name: data.nick}));
- chan.sortUsers();
+ chan.users.push(new User({nick: data.nick, modes: ""}));
+ chan.sortUsers(irc);
client.emit("users", {
chan: chan.id
});
- var self = false;
- if (data.nick.toLowerCase() === irc.me.toLowerCase()) {
- self = true;
- }
var msg = new Msg({
+ time: data.time,
from: data.nick,
- hostmask: data.hostmask.username + "@" + data.hostmask.hostname,
+ hostmask: data.ident + "@" + data.hostname,
type: Msg.Type.JOIN,
- self: self
+ self: data.nick === irc.user.nick
});
chan.messages.push(msg);
client.emit("msg", {
diff --git a/src/plugins/irc-events/kick.js b/src/plugins/irc-events/kick.js
index c5bac10d..8e2522d9 100644
--- a/src/plugins/irc-events/kick.js
+++ b/src/plugins/irc-events/kick.js
@@ -4,35 +4,30 @@ var Msg = require("../../models/msg");
module.exports = function(irc, network) {
var client = this;
irc.on("kick", function(data) {
- var from = data.nick;
- var chan = _.find(network.channels, {name: data.channel});
- var mode = chan.getMode(from);
-
+ var chan = network.getChannel(data.channel);
if (typeof chan === "undefined") {
return;
}
- if (data.client === irc.me) {
+ if (data.kicked === irc.user.nick) {
chan.users = [];
} else {
- chan.users = _.without(chan.users, _.find(chan.users, {name: data.client}));
+ chan.users = _.without(chan.users, _.find(chan.users, {name: data.kicked}));
}
client.emit("users", {
chan: chan.id
});
- var self = false;
- if (data.nick.toLowerCase() === irc.me.toLowerCase()) {
- self = true;
- }
var msg = new Msg({
type: Msg.Type.KICK,
- mode: mode,
- from: from,
- target: data.client,
+ time: data.time,
+ mode: chan.getMode(data.nick),
+ from: data.nick,
+ target: data.kicked,
text: data.message || "",
- self: self
+ highlight: data.kicked === irc.user.nick,
+ self: data.nick === irc.user.nick
});
chan.messages.push(msg);
client.emit("msg", {
diff --git a/src/plugins/irc-events/link.js b/src/plugins/irc-events/link.js
index d5317016..af21a1d4 100644
--- a/src/plugins/irc-events/link.js
+++ b/src/plugins/irc-events/link.js
@@ -9,7 +9,7 @@ process.setMaxListeners(0);
module.exports = function(irc, network) {
var client = this;
- irc.on("message", function(data) {
+ irc.on("privmsg", function(data) {
var config = Helper.getConfig();
if (!config.prefetch) {
return;
@@ -27,15 +27,13 @@ module.exports = function(irc, network) {
return;
}
- var self = data.to.toLowerCase() === irc.me.toLowerCase();
- var chan = _.find(network.channels, {name: self ? data.from : data.to});
+ var chan = network.getChannel(data.target);
if (typeof chan === "undefined") {
return;
}
var msg = new Msg({
type: Msg.Type.TOGGLE,
- time: ""
});
chan.messages.push(msg);
client.emit("msg", {
diff --git a/src/plugins/irc-events/message.js b/src/plugins/irc-events/message.js
index 0894843b..53cda20b 100644
--- a/src/plugins/irc-events/message.js
+++ b/src/plugins/irc-events/message.js
@@ -1,4 +1,3 @@
-var _ = require("lodash");
var Chan = require("../../models/chan");
var Msg = require("../../models/msg");
var Helper = require("../../helper");
@@ -7,44 +6,64 @@ module.exports = function(irc, network) {
var client = this;
var config = Helper.getConfig();
- irc.on("message", function(data) {
- if (data.message.indexOf("\u0001") === 0 && data.message.substring(0, 7) !== "\u0001ACTION") {
- // Hide ctcp messages.
- return;
+ irc.on("notice", function(data) {
+ // Some servers send notices without any nickname
+ if (!data.nick) {
+ data.from_server = true;
+ data.nick = network.host;
}
- var target = data.to;
- if (target.toLowerCase() === irc.me.toLowerCase()) {
- target = data.from;
+ data.type = Msg.Type.NOTICE;
+ handleMessage(data);
+ });
+
+ irc.on("action", function(data) {
+ data.type = Msg.Type.ACTION;
+ handleMessage(data);
+ });
+
+ irc.on("privmsg", function(data) {
+ data.type = Msg.Type.MESSAGE;
+ handleMessage(data);
+ });
+
+ function handleMessage(data) {
+ // Server messages go to server window, no questions asked
+ if (data.from_server) {
+ chan = network.channels[0];
+ } else {
+ var target = data.target;
+
+ // If the message is targeted at us, use sender as target instead
+ if (target.toLowerCase() === irc.user.nick.toLowerCase()) {
+ target = data.nick;
+ }
+
+ var chan = network.getChannel(target);
+ if (typeof chan === "undefined") {
+ // Send notices that are not targeted at us into the server window
+ if (data.type === Msg.Type.NOTICE) {
+ chan = network.channels[0];
+ } else {
+ chan = new Chan({
+ type: Chan.Type.QUERY,
+ name: target
+ });
+ network.channels.push(chan);
+ client.emit("join", {
+ network: network.id,
+ chan: chan
+ });
+ }
+ }
}
- var chan = _.find(network.channels, {name: target});
- if (typeof chan === "undefined") {
- chan = new Chan({
- type: Chan.Type.QUERY,
- name: data.from
- });
- network.channels.push(chan);
- client.emit("join", {
- network: network.id,
- chan: chan
- });
- }
-
- var type = Msg.Type.MESSAGE;
- var text = data.message;
- var textSplit = text.split(" ");
- if (textSplit[0] === "\u0001ACTION") {
- type = Msg.Type.ACTION;
- text = text.replace(/^\u0001ACTION|\u0001$/g, "");
- }
-
- var self = (data.from.toLowerCase() === irc.me.toLowerCase());
+ var self = data.nick === irc.user.nick;
// Self messages are never highlighted
// Non-self messages are highlighted as soon as the nick is detected
- var highlight = !self && textSplit.some(function(w) {
- return (w.replace(/^@/, "").toLowerCase().indexOf(irc.me.toLowerCase()) === 0);
+ var highlight = !self && data.message.split(" ").some(function(w) {
+ return (w.replace(/^@/, "").toLowerCase().indexOf(irc.user.nick.toLowerCase()) === 0);
});
if (chan.id !== client.activeChannel) {
@@ -55,12 +74,12 @@ module.exports = function(irc, network) {
}
}
- var name = data.from;
var msg = new Msg({
- type: type,
- mode: chan.getMode(name),
- from: name,
- text: text,
+ type: data.type,
+ time: data.time,
+ mode: chan.getMode(data.nick),
+ from: data.nick,
+ text: data.message,
self: self,
highlight: highlight
});
@@ -74,5 +93,5 @@ module.exports = function(irc, network) {
chan: chan.id,
msg: msg
});
- });
+ }
};
diff --git a/src/plugins/irc-events/mode.js b/src/plugins/irc-events/mode.js
index be4868eb..1444720f 100644
--- a/src/plugins/irc-events/mode.js
+++ b/src/plugins/irc-events/mode.js
@@ -1,34 +1,53 @@
var _ = require("lodash");
+var Chan = require("../../models/chan");
var Msg = require("../../models/msg");
module.exports = function(irc, network) {
var client = this;
irc.on("mode", function(data) {
- var chan = _.find(network.channels, {name: data.target});
- if (typeof chan !== "undefined") {
- setTimeout(function() {
- irc.write("NAMES " + data.target);
- }, 200);
- var from = data.nick;
- if (from.indexOf(".") !== -1) {
- from = data.target;
+ var targetChan;
+
+ if (data.target === irc.user.nick) {
+ targetChan = network.channels[0];
+ } else {
+ targetChan = network.getChannel(data.target);
+ if (typeof targetChan === "undefined") {
+ return;
}
- var self = false;
- if (from.toLowerCase() === irc.me.toLowerCase()) {
- self = true;
+ }
+
+ var usersUpdated;
+
+ for (var i = 0; i < data.modes.length; i++) {
+ var mode = data.modes[i];
+ var text = mode.mode;
+ if (mode.param) {
+ text += " " + mode.param;
+
+ var user = _.find(targetChan.users, {name: mode.param});
+ if (typeof user !== "undefined") {
+ usersUpdated = true;
+ }
}
+
var msg = new Msg({
+ time: data.time,
type: Msg.Type.MODE,
- mode: chan.getMode(from),
- from: from,
- text: data.mode + " " + (data.client || ""),
- self: self
+ mode: (targetChan.type !== Chan.Type.LOBBY && targetChan.getMode(data.nick)) || "",
+ from: data.nick,
+ text: text,
+ self: data.nick === irc.user.nick
});
- chan.messages.push(msg);
+ targetChan.messages.push(msg);
client.emit("msg", {
- chan: chan.id,
+ chan: targetChan.id,
msg: msg,
});
}
+
+ if (usersUpdated) {
+ // TODO: This is horrible
+ irc.raw("NAMES", data.target);
+ }
});
};
diff --git a/src/plugins/irc-events/motd.js b/src/plugins/irc-events/motd.js
index 3f7d078a..bb5cd141 100644
--- a/src/plugins/irc-events/motd.js
+++ b/src/plugins/irc-events/motd.js
@@ -4,16 +4,31 @@ module.exports = function(irc, network) {
var client = this;
irc.on("motd", function(data) {
var lobby = network.channels[0];
- data.motd.forEach(function(text) {
+
+ if (data.motd) {
+ data.motd.split("\n").forEach(function(text) {
+ var msg = new Msg({
+ type: Msg.Type.MOTD,
+ text: text
+ });
+ lobby.messages.push(msg);
+ client.emit("msg", {
+ chan: lobby.id,
+ msg: msg
+ });
+ });
+ }
+
+ if (data.error) {
var msg = new Msg({
type: Msg.Type.MOTD,
- text: text
+ text: data.error
});
lobby.messages.push(msg);
client.emit("msg", {
chan: lobby.id,
msg: msg
});
- });
+ }
});
};
diff --git a/src/plugins/irc-events/names.js b/src/plugins/irc-events/names.js
index 5f4f7aa9..4307ff22 100644
--- a/src/plugins/irc-events/names.js
+++ b/src/plugins/irc-events/names.js
@@ -3,16 +3,23 @@ var User = require("../../models/user");
module.exports = function(irc, network) {
var client = this;
- irc.on("names", function(data) {
- var chan = _.find(network.channels, {name: data.channel});
+ irc.on("userlist", function(data) {
+ var chan = network.getChannel(data.channel);
if (typeof chan === "undefined") {
return;
}
chan.users = [];
- _.each(data.names, function(u) {
- chan.users.push(new User(u));
+ _.each(data.users, function(u) {
+ var user = new User(u);
+
+ // irc-framework sets characater mode, but lounge works with symbols
+ if (user.mode) {
+ user.mode = network.prefixLookup[user.mode];
+ }
+
+ chan.users.push(user);
});
- chan.sortUsers();
+ chan.sortUsers(irc);
client.emit("users", {
chan: chan.id
});
diff --git a/src/plugins/irc-events/nick.js b/src/plugins/irc-events/nick.js
index a77d12a3..25e2b609 100644
--- a/src/plugins/irc-events/nick.js
+++ b/src/plugins/irc-events/nick.js
@@ -5,11 +5,10 @@ module.exports = function(irc, network) {
var client = this;
irc.on("nick", function(data) {
var self = false;
- var nick = data["new"];
- if (nick === irc.me) {
+ if (data.nick === irc.user.nick) {
var lobby = network.channels[0];
var msg = new Msg({
- text: "You're now known as " + nick,
+ text: "You're now known as " + data.new_nick,
});
lobby.messages.push(msg);
client.emit("msg", {
@@ -20,23 +19,26 @@ module.exports = function(irc, network) {
client.save();
client.emit("nick", {
network: network.id,
- nick: nick
+ nick: data.new_nick
});
}
+
network.channels.forEach(function(chan) {
var user = _.find(chan.users, {name: data.nick});
if (typeof user === "undefined") {
return;
}
- user.name = nick;
- chan.sortUsers();
+ user.name = data.new_nick;
+ chan.sortUsers(irc);
client.emit("users", {
chan: chan.id
});
var msg = new Msg({
+ time: data.time,
type: Msg.Type.NICK,
- from: data.nick,
- text: nick,
+ mode: chan.getMode(data.new_nick),
+ nick: data.nick,
+ new_nick: data.new_nick,
self: self
});
chan.messages.push(msg);
diff --git a/src/plugins/irc-events/notice.js b/src/plugins/irc-events/notice.js
deleted file mode 100644
index 391ebda8..00000000
--- a/src/plugins/irc-events/notice.js
+++ /dev/null
@@ -1,33 +0,0 @@
-var _ = require("lodash");
-var Msg = require("../../models/msg");
-
-module.exports = function(irc, network) {
- var client = this;
- irc.on("notice", function(data) {
- var target = data.to;
- if (target.toLowerCase() === irc.me.toLowerCase()) {
- target = data.from;
- }
-
- var chan = _.find(network.channels, {name: target});
- if (typeof chan === "undefined") {
- chan = network.channels[0];
- }
-
- var from = data.from || "";
- if (data.to === "*" || data.from.indexOf(".") !== -1) {
- from = "";
- }
- var msg = new Msg({
- type: Msg.Type.NOTICE,
- mode: chan.getMode(from),
- from: from,
- text: data.message
- });
- chan.messages.push(msg);
- client.emit("msg", {
- chan: chan.id,
- msg: msg
- });
- });
-};
diff --git a/src/plugins/irc-events/part.js b/src/plugins/irc-events/part.js
index 89d2364e..5aee2c49 100644
--- a/src/plugins/irc-events/part.js
+++ b/src/plugins/irc-events/part.js
@@ -4,12 +4,12 @@ var Msg = require("../../models/msg");
module.exports = function(irc, network) {
var client = this;
irc.on("part", function(data) {
- var chan = _.find(network.channels, {name: data.channels[0]});
+ var chan = network.getChannel(data.channel);
if (typeof chan === "undefined") {
return;
}
var from = data.nick;
- if (from === irc.me) {
+ if (from === irc.user.nick) {
network.channels = _.without(network.channels, chan);
client.save();
client.emit("part", {
@@ -23,9 +23,10 @@ module.exports = function(irc, network) {
});
var msg = new Msg({
type: Msg.Type.PART,
- mode: chan.getMode(from),
+ time: data.time,
+ mode: (user && user.mode) || "",
text: data.message || "",
- hostmask:data.hostmask.username + "@" + data.hostmask.hostname,
+ hostmask: data.ident + "@" + data.hostname,
from: from
});
chan.messages.push(msg);
diff --git a/src/plugins/irc-events/quit.js b/src/plugins/irc-events/quit.js
index 59ff5f3b..288135af 100644
--- a/src/plugins/irc-events/quit.js
+++ b/src/plugins/irc-events/quit.js
@@ -15,10 +15,11 @@ module.exports = function(irc, network) {
chan: chan.id
});
var msg = new Msg({
+ time: data.time,
type: Msg.Type.QUIT,
- mode: chan.getMode(from),
+ mode: user.mode || "",
text: data.message || "",
- hostmask: data.hostmask.username + "@" + data.hostmask.hostname,
+ hostmask: data.ident + "@" + data.hostname,
from: from
});
chan.messages.push(msg);
diff --git a/src/plugins/irc-events/topic.js b/src/plugins/irc-events/topic.js
index 600780fe..f341fcd7 100644
--- a/src/plugins/irc-events/topic.js
+++ b/src/plugins/irc-events/topic.js
@@ -1,33 +1,51 @@
-var _ = require("lodash");
var Msg = require("../../models/msg");
module.exports = function(irc, network) {
var client = this;
irc.on("topic", function(data) {
- var chan = _.find(network.channels, {name: data.channel});
+ var chan = network.getChannel(data.channel);
if (typeof chan === "undefined") {
return;
}
- var from = data.nick || chan.name;
- var topic = data.topic;
var msg = new Msg({
+ time: data.time,
type: Msg.Type.TOPIC,
- mode: chan.getMode(from),
- from: from,
- text: topic,
- isSetByChan: from === chan.name,
- self: (from.toLowerCase() === irc.me.toLowerCase())
+ mode: (data.nick && chan.getMode(data.nick)) || "",
+ from: data.nick,
+ text: data.topic,
+ self: data.nick === irc.user.nick
});
chan.messages.push(msg);
client.emit("msg", {
chan: chan.id,
msg: msg
});
- chan.topic = topic;
+
+ chan.topic = data.topic;
client.emit("topic", {
chan: chan.id,
topic: chan.topic
});
});
+
+ irc.on("topicsetby", function(data) {
+ var chan = network.getChannel(data.channel);
+ if (typeof chan === "undefined") {
+ return;
+ }
+
+ var msg = new Msg({
+ type: Msg.Type.TOPIC_SET_BY,
+ mode: chan.getMode(data.nick),
+ nick: data.nick,
+ when: new Date(data.when * 1000),
+ self: data.nick === irc.user.nick
+ });
+ chan.messages.push(msg);
+ client.emit("msg", {
+ chan: chan.id,
+ msg: msg
+ });
+ });
};
diff --git a/src/plugins/irc-events/welcome.js b/src/plugins/irc-events/welcome.js
index 9bac7c6c..ef8de616 100644
--- a/src/plugins/irc-events/welcome.js
+++ b/src/plugins/irc-events/welcome.js
@@ -2,13 +2,11 @@ var Msg = require("../../models/msg");
module.exports = function(irc, network) {
var client = this;
- irc.on("welcome", function(data) {
- network.connected = true;
- irc.write("PING " + network.host);
+ irc.on("registered", function(data) {
+ network.nick = data.nick;
var lobby = network.channels[0];
- var nick = data;
var msg = new Msg({
- text: "You're now known as " + nick
+ text: "You're now known as " + data.nick
});
lobby.messages.push(msg);
client.emit("msg", {
@@ -18,7 +16,7 @@ module.exports = function(irc, network) {
client.save();
client.emit("nick", {
network: network.id,
- nick: nick
+ nick: data.nick
});
});
};
diff --git a/src/plugins/irc-events/whois.js b/src/plugins/irc-events/whois.js
index d4d8374b..ea5430d1 100644
--- a/src/plugins/irc-events/whois.js
+++ b/src/plugins/irc-events/whois.js
@@ -1,18 +1,14 @@
-var _ = require("lodash");
var Chan = require("../../models/chan");
var Msg = require("../../models/msg");
module.exports = function(irc, network) {
var client = this;
- irc.on("whois", function(err, data) {
- if (data === null) {
- return;
- }
- var chan = _.find(network.channels, {name: data.nickname});
+ irc.on("whois", function(data) {
+ var chan = network.getChannel(data.nick);
if (typeof chan === "undefined") {
chan = new Chan({
type: Chan.Type.QUERY,
- name: data.nickname
+ name: data.nick
});
network.channels.push(chan);
client.emit("join", {
@@ -20,10 +16,20 @@ module.exports = function(irc, network) {
chan: chan
});
}
- var msg = new Msg({
- type: Msg.Type.WHOIS,
- whois: data
- });
+
+ var msg;
+ if (data.error) {
+ msg = new Msg({
+ type: Msg.Type.ERROR,
+ text: "No such nick: " + data.nick
+ });
+ } else {
+ msg = new Msg({
+ type: Msg.Type.WHOIS,
+ whois: data
+ });
+ }
+
chan.messages.push(msg);
client.emit("msg", {
chan: chan.id,
diff --git a/test/models/chan.js b/test/models/chan.js
index d6e42714..90f8c609 100644
--- a/test/models/chan.js
+++ b/test/models/chan.js
@@ -5,8 +5,9 @@ var expect = require("chai").expect;
var Chan = require("../../src/models/chan");
var User = require("../../src/models/user");
-function makeUser(name, mode) {
- return new User({name: name, mode: mode});
+function makeUser(name) {
+ // TODO Update/Fix this when User constructor gets reworked (see its TODO)
+ return new User({nick: name, mode: ""});
}
function getUserNames(chan) {
@@ -14,13 +15,20 @@ function getUserNames(chan) {
}
describe("Chan", function() {
- describe("#sortUsers()", function() {
+ describe("#sortUsers(irc)", function() {
+ var fullNetworkPrefix = [
+ {symbol: "~", mode: "q"},
+ {symbol: "&", mode: "a"},
+ {symbol: "@", mode: "o"},
+ {symbol: "%", mode: "h"},
+ {symbol: "+", mode: "v"}
+ ];
it("should sort a simple user list", function() {
var chan = new Chan({users: [
"JocelynD", "YaManicKill", "astorije", "xPaw", "Max-P"
].map(makeUser)});
- chan.sortUsers();
+ chan.sortUsers({network: {options: {PREFIX: fullNetworkPrefix}}});
expect(getUserNames(chan)).to.deep.equal([
"astorije", "JocelynD", "Max-P", "xPaw", "YaManicKill"
@@ -35,7 +43,7 @@ describe("Chan", function() {
new User({name: "xPaw", mode: "~"}),
new User({name: "Max-P", mode: "@"}),
]});
- chan.sortUsers();
+ chan.sortUsers({network: {options: {PREFIX: fullNetworkPrefix}}});
expect(getUserNames(chan)).to.deep.equal([
"xPaw", "JocelynD", "Max-P", "astorije", "YaManicKill"
@@ -50,7 +58,7 @@ describe("Chan", function() {
new User({name: "xPaw"}),
new User({name: "Max-P", mode: "@"}),
]});
- chan.sortUsers();
+ chan.sortUsers({network: {options: {PREFIX: fullNetworkPrefix}}});
expect(getUserNames(chan)).to.deep.equal(
["Max-P", "YaManicKill", "astorije", "JocelynD", "xPaw"]
@@ -59,7 +67,7 @@ describe("Chan", function() {
it("should be case-insensitive", function() {
var chan = new Chan({users: ["aB", "Ad", "AA", "ac"].map(makeUser)});
- chan.sortUsers();
+ chan.sortUsers({network: {options: {PREFIX: fullNetworkPrefix}}});
expect(getUserNames(chan)).to.deep.equal(["AA", "aB", "ac", "Ad"]);
});
@@ -69,7 +77,7 @@ describe("Chan", function() {
"[foo", "]foo", "(foo)", "{foo}", "", "_foo", "@foo", "^foo",
"&foo", "!foo", "+foo", "Foo"
].map(makeUser)});
- chan.sortUsers();
+ chan.sortUsers({network: {options: {PREFIX: fullNetworkPrefix}}});
expect(getUserNames(chan)).to.deep.equal([
"!foo", "&foo", "(foo)", "+foo", "", "@foo", "[foo", "]foo",
diff --git a/test/models/network.js b/test/models/network.js
index 300e6545..dcd9d92b 100644
--- a/test/models/network.js
+++ b/test/models/network.js
@@ -22,7 +22,7 @@ describe("Network", function() {
username: "",
realname: "",
commands: [],
- nick: undefined,
+ nick: "",
join: "#thelounge,&foobar",
});
});
diff --git a/test/util.js b/test/util.js
index cda4a419..a0e00172 100644
--- a/test/util.js
+++ b/test/util.js
@@ -2,9 +2,10 @@ var EventEmitter = require("events").EventEmitter;
var util = require("util");
var _ = require("lodash");
var express = require("express");
+var Network = require("../src/models/network");
function MockClient(opts) {
- this.me = "test-user";
+ this.user = {nick: "test-user"};
for (var k in opts) {
this[k] = opts[k];
@@ -13,14 +14,13 @@ function MockClient(opts) {
util.inherits(MockClient, EventEmitter);
MockClient.prototype.createMessage = function(opts) {
-
var message = _.extend({
message: "dummy message",
- from: "test-user",
- to: "test-channel"
+ nick: "test-user",
+ target: "#test-channel"
}, opts);
- this.emit("message", message);
+ this.emit("privmsg", message);
};
module.exports = {
@@ -28,12 +28,13 @@ module.exports = {
return new MockClient();
},
createNetwork: function() {
- return {
+ return new Network({
+ host: "example.com",
channels: [{
- name: "test-channel",
+ name: "#test-channel",
messages: []
}]
- };
+ });
},
createWebserver: function() {
return express();