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 @@
- {{whois.nickname}} - ({{whois.username}}@{{whois.hostname}}): - {{whois.realname}} + {{whois.nick}} + ({{whois.user}}@{{whois.host}}): + {{whois.real_name}}
+{{#if whois.account}} +
+ {{whois.nick}} + is logged in as {{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}}
- {{whois.nickname}} - is connected to {{whois.server}} + {{whois.nick}} + is connected to {{whois.server}} ({{whois.server_info}}) +
+{{/if}} +{{#if whois.secure}} +
+ {{whois.nick}} + is using a secure connection
{{/if}} {{#if whois.away}}
- {{whois.nickname}} + {{whois.nick}} is away ({{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}} -
+
{{partial "chan"}}
{{/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();