diff --git a/client/css/style.css b/client/css/style.css index 17771103..ffec738c 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -419,6 +419,7 @@ kbd { margin-right: 9px; } +.context-menu-edit::before, #set-nick::before { content: "\f303"; /* https://fontawesome.com/icons/pencil-alt?style=solid */ } diff --git a/client/js/contextMenuFactory.js b/client/js/contextMenuFactory.js index a822deea..4a94a94f 100644 --- a/client/js/contextMenuFactory.js +++ b/client/js/contextMenuFactory.js @@ -153,6 +153,21 @@ function addFocusItem() { }); } +function addEditNetworkItem() { + function edit(itemData) { + socket.emit("network:get", itemData); + $('button[data-target="#connect"]').trigger("click"); + } + + addContextMenuItem({ + check: (target) => target.hasClass("lobby"), + className: "edit", + displayName: "Edit this network…", + data: (target) => target.closest(".network").data("uuid"), + callback: edit, + }); +} + function addChannelListItem() { function list(itemData) { socket.emit("input", { @@ -207,6 +222,7 @@ function addDefaultItems() { addQueryItem(); addKickItem(); addFocusItem(); + addEditNetworkItem(); addChannelListItem(); addBanListItem(); addJoinItem(); diff --git a/client/js/lounge.js b/client/js/lounge.js index 9d98aef6..809bbfd1 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -18,7 +18,6 @@ const utils = require("./utils"); require("./webpush"); require("./keybinds"); require("./clipboard"); -const Changelog = require("./socket-events/changelog"); const contextMenuFactory = require("./contextMenuFactory"); const contextMenuContainer = $("#context-menu-container"); @@ -322,15 +321,6 @@ $(function() { socket.emit("names", {target: self.data("id")}); } - if (target === "#settings") { - $("#session-list").html("

Loading…

"); - socket.emit("sessions:get"); - } - - if (target === "#help" || target === "#changelog") { - Changelog.requestIfNeeded(); - } - // Pushes states to history web API when clicking elements with a data-target attribute. // States are very trivial and only contain a single `clickTarget` property which // contains a CSS selector that targets elements which takes the user to a different view diff --git a/client/js/socket-events/changelog.js b/client/js/socket-events/changelog.js index 983b15f3..ae6c4b0a 100644 --- a/client/js/socket-events/changelog.js +++ b/client/js/socket-events/changelog.js @@ -4,10 +4,6 @@ const $ = require("jquery"); const socket = require("../socket"); const templates = require("../../views"); -module.exports = { - requestIfNeeded, -}; - // Requests version information if it hasn't been retrieved before (or if it has // been removed from the page, i.e. when clicking on "Check now". Displays a // loading state until received. @@ -54,6 +50,8 @@ socket.on("changelog", function(data) { } }); +$("#help, #changelog").on("show", requestIfNeeded); + // When clicking the "Check now" button, remove current checker information and // request a new one. Loading will be displayed in the meantime. $("#help").on("click", "#check-now", () => { diff --git a/client/js/socket-events/configuration.js b/client/js/socket-events/configuration.js index aa76b203..54232f50 100644 --- a/client/js/socket-events/configuration.js +++ b/client/js/socket-events/configuration.js @@ -5,6 +5,7 @@ const socket = require("../socket"); const templates = require("../../views"); const options = require("../options"); const webpush = require("../webpush"); +const connect = $("#connect"); socket.on("configuration", function(data) { if (options.initialized) { @@ -14,10 +15,14 @@ socket.on("configuration", function(data) { } $("#settings").html(templates.windows.settings(data)); - $("#connect").html(templates.windows.connect(data)); $("#help").html(templates.windows.help(data)); $("#changelog").html(templates.windows.changelog()); + $("#settings").on("show", () => { + $("#session-list").html("

Loading…

"); + socket.emit("sessions:get"); + }); + $("#play").on("click", () => { const pop = new Audio(); pop.src = "audio/pop.ogg"; @@ -33,9 +38,7 @@ socket.on("configuration", function(data) { options.processSetting("theme", data.defaultTheme, true); } - const forms = $("#connect form, #change-password form"); - - forms.on("submit", function() { + function handleFormSubmit() { const form = $(this); const event = form.data("event"); @@ -51,27 +54,34 @@ socket.on("configuration", function(data) { socket.emit(event, values); return false; + } + + $("#change-password form").on("submit", handleFormSubmit); + connect.on("submit", "form", handleFormSubmit); + + connect.on("show", function() { + connect + .html(templates.windows.connect(data)) + .find("#connect\\:nick") + .on("focusin", function() { + // Need to set the first "lastvalue", so it can be used in the below function + const nick = $(this); + nick.data("lastvalue", nick.val()); + }) + .on("input", function() { + const nick = $(this).val(); + const usernameInput = connect.find(".username"); + + // Because this gets called /after/ it has already changed, we need use the previous value + const lastValue = $(this).data("lastvalue"); + + // They were the same before the change, so update the username field + if (usernameInput.val() === lastValue) { + usernameInput.val(nick); + } + + // Store the "previous" value, for next time + $(this).data("lastvalue", nick); + }); }); - - $(".nick") - .on("focusin", function() { - // Need to set the first "lastvalue", so it can be used in the below function - const nick = $(this); - nick.data("lastvalue", nick.val()); - }) - .on("input", function() { - const nick = $(this).val(); - const usernameInput = forms.find(".username"); - - // Because this gets called /after/ it has already changed, we need use the previous value - const lastValue = $(this).data("lastvalue"); - - // They were the same before the change, so update the username field - if (usernameInput.val() === lastValue) { - usernameInput.val(nick); - } - - // Store the "previous" value, for next time - $(this).data("lastvalue", nick); - }); }); diff --git a/client/js/socket-events/network.js b/client/js/socket-events/network.js index af294d4c..e012d211 100644 --- a/client/js/socket-events/network.js +++ b/client/js/socket-events/network.js @@ -3,6 +3,7 @@ const $ = require("jquery"); const socket = require("../socket"); const render = require("../render"); +const templates = require("../../views"); const sidebar = $("#sidebar"); socket.on("network", function(data) { @@ -27,3 +28,17 @@ socket.on("network:status", function(data) { .toggleClass("not-connected", !data.connected) .toggleClass("not-secure", !data.secure); }); + +socket.on("network:info", function(data) { + $("#connect") + .html(templates.windows.connect(data)) + .find("form").on("submit", function() { + const uuid = $(this).find("input[name=uuid]").val(); + const newName = $(this).find("#connect\\:name").val(); + + sidebar.find(`.network[data-uuid="${uuid}"] .chan.lobby .name`) + .attr("title", newName) + .text(newName) + .click(); + }); +}); diff --git a/client/views/windows/connect.tpl b/client/views/windows/connect.tpl index 56ab219d..bb7bef78 100644 --- a/client/views/windows/connect.tpl +++ b/client/views/windows/connect.tpl @@ -1,20 +1,26 @@
-
+

- {{#if public}}The Lounge - {{/if}} - Connect - {{#unless displayNetwork}} - {{#if lockNetwork}} - to {{defaults.name}} - {{/if}} - {{/unless}} + {{#if defaults.uuid}} + + + Edit {{defaults.name}} + {{else}} + {{#if public}}The Lounge - {{/if}} + Connect + {{#unless displayNetwork}} + {{#if lockNetwork}} + to {{defaults.name}} + {{/if}} + {{/unless}} + {{/if}}

- {{#if displayNetwork}} + {{#if displayNetwork}}

Network settings

@@ -29,7 +35,7 @@
- +
@@ -51,7 +57,7 @@
- {{/if}} + {{/if}}

User preferences

@@ -59,16 +65,16 @@
- +
- {{#unless useHexIp}} + {{#unless useHexIp}}
- {{/unless}} + {{/unless}}
@@ -81,6 +87,18 @@
+ {{#if defaults.uuid}} +
+ +
+
+ +
+
+ +
+ {{else}}
@@ -90,5 +108,6 @@
+ {{/if}}
diff --git a/src/client.js b/src/client.js index 76562aa8..bc90a7f8 100644 --- a/src/client.js +++ b/src/client.js @@ -2,12 +2,10 @@ const _ = require("lodash"); const colors = require("chalk"); -const pkg = require("../package.json"); const Chan = require("./models/chan"); const crypto = require("crypto"); const Msg = require("./models/msg"); const Network = require("./models/network"); -const ircFramework = require("irc-framework"); const Helper = require("./helper"); const UAParser = require("ua-parser-js"); const MessageStorage = require("./plugins/sqlite"); @@ -140,8 +138,6 @@ Client.prototype.find = function(channelId) { Client.prototype.connect = function(args) { const client = this; - const nick = args.nick || "thelounge"; - let webirc = null; let channels = []; if (args.channels) { @@ -176,105 +172,33 @@ Client.prototype.connect = function(args) { }); } - args.ip = args.ip || (client.config && client.config.ip) || client.ip; - args.hostname = args.hostname || (client.config && client.config.hostname) || client.hostname; - const network = new Network({ uuid: args.uuid, - name: args.name || (Helper.config.displayNetwork ? "" : Helper.config.defaults.name) || "", - host: args.host || "", - port: parseInt(args.port, 10) || (args.tls ? 6697 : 6667), + name: String(args.name || (Helper.config.displayNetwork ? "" : Helper.config.defaults.name) || ""), + host: String(args.host || ""), + port: parseInt(args.port, 10), tls: !!args.tls, rejectUnauthorized: !!args.rejectUnauthorized, - password: args.password, - username: args.username || nick.replace(/[^a-zA-Z0-9]/g, ""), - realname: args.realname || "The Lounge User", - commands: args.commands, - ip: args.ip, - hostname: args.hostname, + password: String(args.password || ""), + nick: String(args.nick || ""), + username: String(args.username || ""), + realname: String(args.realname || ""), + commands: args.commands || [], + ip: args.ip || (client.config && client.config.ip) || client.ip, + hostname: args.hostname || (client.config && client.config.hostname) || client.hostname, channels: channels, }); - network.setNick(nick); client.networks.push(network); client.emit("network", { networks: [network.getFilteredClone(this.lastActiveChannel, -1)], }); - if (Helper.config.lockNetwork) { - // This check is needed to prevent invalid user configurations - if (!Helper.config.public && args.host && args.host.length > 0 && args.host !== Helper.config.defaults.host) { - network.channels[0].pushMessage(client, new Msg({ - type: Msg.Type.ERROR, - text: "Hostname you specified is not allowed.", - }), true); - return; - } - - network.host = Helper.config.defaults.host; - network.port = Helper.config.defaults.port; - network.tls = Helper.config.defaults.tls; - network.rejectUnauthorized = Helper.config.defaults.rejectUnauthorized; - } - - if (network.host.length === 0) { - network.channels[0].pushMessage(client, new Msg({ - type: Msg.Type.ERROR, - text: "You must specify a hostname to connect.", - }), true); + if (!network.validate(client)) { return; } - if (Helper.config.webirc && network.host in Helper.config.webirc) { - if (!args.hostname) { - args.hostname = args.ip; - } - - if (args.ip) { - if (Helper.config.webirc[network.host] instanceof Function) { - webirc = Helper.config.webirc[network.host](client, args); - } else { - webirc = { - password: Helper.config.webirc[network.host], - username: pkg.name, - address: args.ip, - hostname: args.hostname, - }; - } - } else { - log.warn("Cannot find a valid WEBIRC configuration for " + nick - + "!" + network.username + "@" + network.host); - } - } - - network.irc = new ircFramework.Client({ - version: false, // We handle it ourselves - host: network.host, - port: network.port, - nick: nick, - username: Helper.config.useHexIp ? Helper.ip2hex(args.ip) : network.username, - gecos: network.realname, - password: network.password, - tls: network.tls, - outgoing_addr: Helper.config.bind, - rejectUnauthorized: network.rejectUnauthorized, - enable_chghost: true, - enable_echomessage: true, - auto_reconnect: true, - auto_reconnect_wait: 10000 + Math.floor(Math.random() * 1000), // If multiple users are connected to the same network, randomize their reconnections a little - auto_reconnect_max_retries: 360, // At least one hour (plus timeouts) worth of reconnections - webirc: webirc, - }); - - network.irc.requestCap([ - "znc.in/self-message", // Legacy echo-message for ZNC - ]); - - // Request only new messages from ZNC if we have sqlite logging enabled - // See http://wiki.znc.in/Playback - if (client.config.log && Helper.config.messageStorage.includes("sqlite")) { - network.irc.requestCap("znc.in/playback"); - } + network.createIrcFramework(client); events.forEach((plugin) => { require(`./plugins/irc-events/${plugin}`).apply(client, [ diff --git a/src/helper.js b/src/helper.js index 8625411d..d4f00fd3 100644 --- a/src/helper.js +++ b/src/helper.js @@ -31,6 +31,7 @@ const Helper = { getGitCommit, ip2hex, mergeConfig, + getDefaultNick, password: { hash: passwordHash, @@ -191,6 +192,14 @@ function passwordCompare(password, expected) { return bcrypt.compare(password, expected); } +function getDefaultNick() { + if (!this.config.defaults.nick) { + return "thelounge"; + } + + return this.config.defaults.nick.replace(/%/g, () => Math.floor(Math.random() * 10)); +} + function mergeConfig(oldConfig, newConfig) { return _.mergeWith(oldConfig, newConfig, (objValue, srcValue, key) => { // Do not override config variables if the type is incorrect (e.g. object changed into a string) diff --git a/src/models/network.js b/src/models/network.js index f3c2a28b..ff7eee67 100644 --- a/src/models/network.js +++ b/src/models/network.js @@ -2,7 +2,10 @@ const _ = require("lodash"); const uuidv4 = require("uuid/v4"); +const IrcFramework = require("irc-framework"); const Chan = require("./chan"); +const Msg = require("./msg"); +const Helper = require("../helper"); module.exports = Network; @@ -59,6 +62,165 @@ function Network(attr) { ); } +Network.prototype.validate = function(client) { + this.setNick(String(this.nick || Helper.getDefaultNick()).replace(" ", "_")); + + if (!this.username) { + this.username = this.nick.replace(/[^a-zA-Z0-9]/g, ""); + } + + if (!this.realname) { + this.realname = "The Lounge User"; + } + + if (!this.port) { + this.port = this.tls ? 6697 : 6667; + } + + if (Helper.config.lockNetwork) { + // This check is needed to prevent invalid user configurations + if (!Helper.config.public && this.host && this.host.length > 0 && this.host !== Helper.config.defaults.host) { + this.channels[0].pushMessage(client, new Msg({ + type: Msg.Type.ERROR, + text: "Hostname you specified is not allowed.", + }), true); + + return false; + } + + this.host = Helper.config.defaults.host; + this.port = Helper.config.defaults.port; + this.tls = Helper.config.defaults.tls; + this.rejectUnauthorized = Helper.config.defaults.rejectUnauthorized; + } + + if (this.host.length === 0) { + this.channels[0].pushMessage(client, new Msg({ + type: Msg.Type.ERROR, + text: "You must specify a hostname to connect.", + }), true); + + return false; + } + + return true; +}; + +Network.prototype.createIrcFramework = function(client) { + this.irc = new IrcFramework.Client({ + version: false, // We handle it ourselves + host: this.host, + port: this.port, + nick: this.nick, + username: Helper.config.useHexIp ? Helper.ip2hex(this.ip) : this.username, + gecos: this.realname, + password: this.password, + tls: this.tls, + outgoing_addr: Helper.config.bind, + rejectUnauthorized: this.rejectUnauthorized, + enable_chghost: true, + enable_echomessage: true, + auto_reconnect: true, + auto_reconnect_wait: 10000 + Math.floor(Math.random() * 1000), // If multiple users are connected to the same network, randomize their reconnections a little + auto_reconnect_max_retries: 360, // At least one hour (plus timeouts) worth of reconnections + webirc: this.createWebIrc(client), + }); + + this.irc.requestCap([ + "znc.in/self-message", // Legacy echo-message for ZNC + ]); + + // Request only new messages from ZNC if we have sqlite logging enabled + // See http://wiki.znc.in/Playback + if (client.config.log && Helper.config.messageStorage.includes("sqlite")) { + this.irc.requestCap("znc.in/playback"); + } +}; + +Network.prototype.createWebIrc = function(client) { + if (!Helper.config.webirc || !(this.host in Helper.config.webirc)) { + return null; + } + + if (!this.ip) { + log.warn(`Cannot find a valid WEBIRC configuration for ${this.nick}!${this.username}@${this.host}`); + + return null; + } + + if (!this.hostname) { + this.hostname = this.ip; + } + + if (Helper.config.webirc[this.host] instanceof Function) { + return Helper.config.webirc[this.host](client, this); + } + + return { + password: Helper.config.webirc[this.host], + username: "thelounge", + address: this.ip, + hostname: this.hostname, + }; +}; + +Network.prototype.edit = function(client, args) { + const oldNick = this.nick; + + this.nick = args.nick; + this.host = String(args.host || ""); + this.name = String(args.name || "") || this.host; + this.port = parseInt(args.port, 10); + this.tls = !!args.tls; + this.rejectUnauthorized = !!args.rejectUnauthorized; + this.password = String(args.password || ""); + this.username = String(args.username || ""); + this.realname = String(args.realname || ""); + + // Split commands into an array + this.commands = String(args.commands || "") + .replace(/\r\n|\r|\n/g, "\n") + .split("\n") + .filter((command) => command.length > 0); + + // Sync lobby channel name + this.channels[0].name = this.name; + + if (!this.validate(client)) { + return; + } + + if (this.irc) { + if (this.nick !== oldNick) { + if (this.irc.connection && this.irc.connection.connected) { + // Send new nick straight away + this.irc.raw("NICK", this.nick); + } else { + this.irc.options.nick = this.irc.user.nick = this.nick; + + // Update UI nick straight away if IRC is not connected + client.emit("nick", { + network: this.id, + nick: this.nick, + }); + } + } + + this.irc.options.host = this.host; + this.irc.options.port = this.port; + this.irc.options.password = this.password; + this.irc.options.gecos = this.irc.user.gecos = this.realname; + this.irc.options.tls = this.tls; + this.irc.options.rejectUnauthorized = this.rejectUnauthorized; + + if (!Helper.config.useHexIp) { + this.irc.options.username = this.irc.user.username = this.username; + } + } + + client.save(); +}; + Network.prototype.destroy = function() { this.channels.forEach((channel) => channel.destroy()); }; diff --git a/src/plugins/irc-events/error.js b/src/plugins/irc-events/error.js index 9908a1fa..df1d99f5 100644 --- a/src/plugins/irc-events/error.js +++ b/src/plugins/irc-events/error.js @@ -1,6 +1,7 @@ "use strict"; const Msg = require("../../models/msg"); +const Helper = require("../../helper"); module.exports = function(irc, network) { const client = this; @@ -59,7 +60,7 @@ module.exports = function(irc, network) { lobby.pushMessage(client, msg, true); if (irc.connection.registered === false) { - irc.changeNick("thelounge" + Math.floor(Math.random() * 100)); + irc.changeNick(Helper.getDefaultNick()); } client.emit("nick", { diff --git a/src/server.js b/src/server.js index 0971fb9c..201c597a 100644 --- a/src/server.js +++ b/src/server.js @@ -301,17 +301,46 @@ function initializeClient(socket, client, token, lastMessage) { } }); - socket.on("conn", (data) => { + socket.on("network:new", (data) => { if (typeof data === "object") { // prevent people from overriding webirc settings data.ip = null; data.hostname = null; data.uuid = null; + data.commands = null; client.connect(data); } }); + socket.on("network:get", (data) => { + if (typeof data !== "string") { + return; + } + + const network = _.find(client.networks, {uuid: data}); + + if (!network) { + return; + } + + socket.emit("network:info", getClientConfiguration(network.export())); + }); + + socket.on("network:edit", (data) => { + if (typeof data !== "object") { + return; + } + + const network = _.find(client.networks, {uuid: data.uuid}); + + if (!network) { + return; + } + + network.edit(client, data); + }); + if (!Helper.config.public && !Helper.config.ldap.enable) { socket.on("change-password", (data) => { if (typeof data === "object") { @@ -541,27 +570,22 @@ function initializeClient(socket, client, token, lastMessage) { } } -function getClientConfiguration() { +function getClientConfiguration(network) { const config = _.pick(Helper.config, [ "public", "lockNetwork", "displayNetwork", "useHexIp", - "themes", "prefetch", ]); config.ldapEnabled = Helper.config.ldap.enable; - config.version = pkg.version; - config.gitCommit = Helper.getGitCommit(); - config.themes = themes.getAll(); - config.defaultTheme = Helper.config.theme; if (config.displayNetwork) { - config.defaults = _.clone(Helper.config.defaults); + config.defaults = _.clone(network || Helper.config.defaults); } else { // Only send defaults that are visible on the client - config.defaults = _.pick(Helper.config.defaults, [ + config.defaults = _.pick(network || Helper.config.defaults, [ "nick", "username", "password", @@ -570,7 +594,13 @@ function getClientConfiguration() { ]); } - config.defaults.nick = config.defaults.nick.replace(/%/g, () => Math.floor(Math.random() * 10)); + if (!network) { + config.version = pkg.version; + config.gitCommit = Helper.getGitCommit(); + config.themes = themes.getAll(); + config.defaultTheme = Helper.config.theme; + config.defaults.nick = Helper.getDefaultNick(); + } return config; } diff --git a/test/models/network.js b/test/models/network.js index 900e4f8e..9ac04e2a 100644 --- a/test/models/network.js +++ b/test/models/network.js @@ -5,6 +5,7 @@ const Chan = require("../../src/models/chan"); const Msg = require("../../src/models/msg"); const User = require("../../src/models/user"); const Network = require("../../src/models/network"); +const Helper = require("../../src/helper"); describe("Network", function() { describe("#export()", function() { @@ -49,6 +50,104 @@ describe("Network", function() { }); }); + it("validate should set correct defaults", function() { + Helper.config.defaults.nick = ""; + + const network = new Network({ + host: "localhost", + }); + + expect(network.validate()).to.be.true; + expect(network.nick).to.equal("thelounge"); + expect(network.username).to.equal("thelounge"); + expect(network.realname).to.equal("The Lounge User"); + expect(network.port).to.equal(6667); + + const network2 = new Network({ + host: "localhost", + nick: "@Invalid Nick?", + }); + expect(network2.validate()).to.be.true; + expect(network2.username).to.equal("InvalidNick"); + }); + + it("lockNetwork should be enforced when validating", function() { + Helper.config.lockNetwork = true; + Helper.config.public = false; + + const network = new Network({ + host: "", + port: 1337, + tls: false, + rejectUnauthorized: false, + }); + expect(network.validate()).to.be.true; + expect(network.host).to.equal("chat.freenode.net"); + expect(network.port).to.equal(6697); + expect(network.tls).to.be.true; + expect(network.rejectUnauthorized).to.be.true; + + Helper.config.public = true; + + const network2 = new Network({ + host: "some.fake.tld", + }); + expect(network2.validate()).to.be.true; + expect(network2.host).to.equal("chat.freenode.net"); + + Helper.config.lockNetwork = false; + Helper.config.public = false; + }); + + it("editing a network should enforce correct types", function() { + let saveCalled = false; + + const network = new Network(); + network.edit({ + save() { + saveCalled = true; + }, + }, { + nick: "newNick", + host: "new.tld", + name: "Lounge Test Network", + port: "1337", + tls: undefined, + rejectUnauthorized: undefined, + username: 1234, + password: 4567, + realname: 8901, + commands: "/command 1 2 3\r\n/ping HELLO\r\r\r\r/whois test\r\n\r\n", + ip: "newIp", + hostname: "newHostname", + id: 1000000, + guid: "newGuid", + }); + + expect(saveCalled).to.be.true; + expect(network.id).to.not.equal(1000000); + expect(network.guid).to.not.equal("newGuid"); + expect(network.ip).to.be.null; + expect(network.hostname).to.be.null; + + expect(network.name).to.equal("Lounge Test Network"); + expect(network.channels[0].name).to.equal("Lounge Test Network"); + + expect(network.nick).to.equal("newNick"); + expect(network.host).to.equal("new.tld"); + expect(network.port).to.equal(1337); + expect(network.tls).to.be.false; + expect(network.rejectUnauthorized).to.be.false; + expect(network.username).to.equal("1234"); + expect(network.password).to.equal("4567"); + expect(network.realname).to.equal("8901"); + expect(network.commands).to.deep.equal([ + "/command 1 2 3", + "/ping HELLO", + "/whois test", + ]); + }); + it("should generate uuid (v4) for each network", function() { const network1 = new Network(); const network2 = new Network(); diff --git a/test/server.js b/test/server.js index 35fa66b0..cf4f3895 100644 --- a/test/server.js +++ b/test/server.js @@ -88,7 +88,7 @@ describe("Server", function() { it("should create network", (done) => { client.on("init", () => { - client.emit("conn", { + client.emit("network:new", { username: "test-user", realname: "The Lounge Test", nick: "test-user",