Merge pull request #632 from thelounge/xpaw/multi-prefix-support

Make use of multi-prefix cap and remove NAMES spam on mode changes
This commit is contained in:
Jérémie Astori 2016-10-08 14:03:37 -04:00 committed by GitHub
commit 1e864e266e
5 changed files with 110 additions and 54 deletions

View File

@ -2,13 +2,16 @@ var _ = require("lodash");
module.exports = User; module.exports = User;
function User(attr) { function User(attr, prefixLookup) {
// TODO: Remove this
attr.name = attr.name || attr.nick;
attr.mode = attr.mode || (attr.modes && attr.modes[0]) || "";
_.merge(this, _.extend({ _.merge(this, _.extend({
mode: "", modes: [],
name: "" nick: ""
}, attr)); }, attr));
// irc-framework sets character mode, but lounge works with symbols
this.modes = this.modes.map(mode => prefixLookup[mode]);
// TODO: Remove this
this.name = this.nick;
this.mode = (this.modes && this.modes[0]) || "";
} }

View File

@ -17,7 +17,7 @@ module.exports = function(irc, network) {
chan: chan chan: chan
}); });
} }
chan.users.push(new User({nick: data.nick, modes: ""})); chan.users.push(new User({nick: data.nick}));
chan.sortUsers(irc); chan.sortUsers(irc);
client.emit("users", { client.emit("users", {
chan: chan.id chan: chan.id

View File

@ -17,17 +17,19 @@ module.exports = function(irc, network) {
} }
var usersUpdated; var usersUpdated;
var supportsMultiPrefix = network.irc.network.cap.isEnabled("multi-prefix");
var userModeSortPriority = {};
irc.network.options.PREFIX.forEach(function(prefix, index) {
userModeSortPriority[prefix.symbol] = index;
});
for (var i = 0; i < data.modes.length; i++) { for (var i = 0; i < data.modes.length; i++) {
var mode = data.modes[i]; var mode = data.modes[i];
var text = mode.mode; var text = mode.mode;
if (mode.param) { if (mode.param) {
text += " " + mode.param; text += " " + mode.param;
var user = _.find(targetChan.users, {name: mode.param});
if (typeof user !== "undefined") {
usersUpdated = true;
}
} }
var msg = new Msg({ var msg = new Msg({
@ -39,11 +41,51 @@ module.exports = function(irc, network) {
self: data.nick === irc.user.nick self: data.nick === irc.user.nick
}); });
targetChan.pushMessage(client, msg); targetChan.pushMessage(client, msg);
if (!mode.param) {
continue;
} }
if (usersUpdated) { var user = _.find(targetChan.users, {name: mode.param});
if (!user) {
continue;
}
usersUpdated = true;
if (!supportsMultiPrefix) {
continue;
}
var add = mode.mode[0] === "+";
var changedMode = network.prefixLookup[mode.mode[1]];
if (!add) {
_.pull(user.modes, changedMode);
} else if (user.modes.indexOf(changedMode) === -1) {
user.modes.push(changedMode);
user.modes.sort(function(a, b) {
return userModeSortPriority[a] - userModeSortPriority[b];
});
}
// TODO: remove in future
user.mode = (user.modes && user.modes[0]) || "";
}
if (!usersUpdated) {
return;
}
if (!supportsMultiPrefix) {
// TODO: This is horrible // TODO: This is horrible
irc.raw("NAMES", data.target); irc.raw("NAMES", data.target);
} else {
targetChan.sortUsers(irc);
client.emit("users", {
chan: targetChan.id
});
} }
}); });
}; };

View File

@ -8,18 +8,17 @@ module.exports = function(irc, network) {
if (typeof chan === "undefined") { if (typeof chan === "undefined") {
return; return;
} }
chan.users = [];
_.each(data.users, function(u) {
var user = new User(u);
// irc-framework sets characater mode, but lounge works with symbols chan.users = [];
if (user.mode) {
user.mode = network.prefixLookup[user.mode]; _.each(data.users, function(u) {
} var user = new User(u, network.prefixLookup);
chan.users.push(user); chan.users.push(user);
}); });
chan.sortUsers(irc); chan.sortUsers(irc);
client.emit("users", { client.emit("users", {
chan: chan.id chan: chan.id
}); });

View File

@ -1,36 +1,48 @@
"use strict"; "use strict";
var _ = require("lodash");
var expect = require("chai").expect; var expect = require("chai").expect;
var Chan = require("../../src/models/chan"); var Chan = require("../../src/models/chan");
var User = require("../../src/models/user"); var User = require("../../src/models/user");
function makeUser(name) {
// TODO Update/Fix this when User constructor gets reworked (see its TODO)
return new User({nick: name, mode: ""});
}
function getUserNames(chan) {
return chan.users.map(function(u) {
return u.name;
});
}
describe("Chan", function() { describe("Chan", function() {
describe("#sortUsers(irc)", function() { describe("#sortUsers(irc)", function() {
var fullNetworkPrefix = [ var network = {
network: {
options: {
PREFIX: [
{symbol: "~", mode: "q"}, {symbol: "~", mode: "q"},
{symbol: "&", mode: "a"}, {symbol: "&", mode: "a"},
{symbol: "@", mode: "o"}, {symbol: "@", mode: "o"},
{symbol: "%", mode: "h"}, {symbol: "%", mode: "h"},
{symbol: "+", mode: "v"} {symbol: "+", mode: "v"}
]; ]
}
}
};
var prefixLookup = {};
_.each(network.network.options.PREFIX, function(mode) {
prefixLookup[mode.mode] = mode.symbol;
});
var makeUser = function(nick) {
return new User({nick: nick}, prefixLookup);
};
var getUserNames = function(chan) {
return chan.users.map(function(u) {
return u.name;
});
};
it("should sort a simple user list", function() { it("should sort a simple user list", function() {
var chan = new Chan({users: [ var chan = new Chan({users: [
"JocelynD", "YaManicKill", "astorije", "xPaw", "Max-P" "JocelynD", "YaManicKill", "astorije", "xPaw", "Max-P"
].map(makeUser)}); ].map(makeUser)});
chan.sortUsers({network: {options: {PREFIX: fullNetworkPrefix}}}); chan.sortUsers(network);
expect(getUserNames(chan)).to.deep.equal([ expect(getUserNames(chan)).to.deep.equal([
"astorije", "JocelynD", "Max-P", "xPaw", "YaManicKill" "astorije", "JocelynD", "Max-P", "xPaw", "YaManicKill"
@ -39,13 +51,13 @@ describe("Chan", function() {
it("should group users by modes", function() { it("should group users by modes", function() {
var chan = new Chan({users: [ var chan = new Chan({users: [
new User({name: "JocelynD", mode: "&"}), new User({nick: "JocelynD", modes: ["a", "o"]}, prefixLookup),
new User({name: "YaManicKill", mode: "+"}), new User({nick: "YaManicKill", modes: ["v"]}, prefixLookup),
new User({name: "astorije", mode: "%"}), new User({nick: "astorije", modes: ["h"]}, prefixLookup),
new User({name: "xPaw", mode: "~"}), new User({nick: "xPaw", modes: ["q"]}, prefixLookup),
new User({name: "Max-P", mode: "@"}), new User({nick: "Max-P", modes: ["o"]}, prefixLookup),
]}); ]});
chan.sortUsers({network: {options: {PREFIX: fullNetworkPrefix}}}); chan.sortUsers(network);
expect(getUserNames(chan)).to.deep.equal([ expect(getUserNames(chan)).to.deep.equal([
"xPaw", "JocelynD", "Max-P", "astorije", "YaManicKill" "xPaw", "JocelynD", "Max-P", "astorije", "YaManicKill"
@ -54,13 +66,13 @@ describe("Chan", function() {
it("should sort a mix of users and modes", function() { it("should sort a mix of users and modes", function() {
var chan = new Chan({users: [ var chan = new Chan({users: [
new User({name: "JocelynD"}), new User({nick: "JocelynD"}, prefixLookup),
new User({name: "YaManicKill", mode: "@"}), new User({nick: "YaManicKill", modes: ["o"]}, prefixLookup),
new User({name: "astorije"}), new User({nick: "astorije"}, prefixLookup),
new User({name: "xPaw"}), new User({nick: "xPaw"}, prefixLookup),
new User({name: "Max-P", mode: "@"}), new User({nick: "Max-P", modes: ["o"]}, prefixLookup),
]}); ]});
chan.sortUsers({network: {options: {PREFIX: fullNetworkPrefix}}}); chan.sortUsers(network);
expect(getUserNames(chan)).to.deep.equal( expect(getUserNames(chan)).to.deep.equal(
["Max-P", "YaManicKill", "astorije", "JocelynD", "xPaw"] ["Max-P", "YaManicKill", "astorije", "JocelynD", "xPaw"]
@ -69,7 +81,7 @@ describe("Chan", function() {
it("should be case-insensitive", function() { it("should be case-insensitive", function() {
var chan = new Chan({users: ["aB", "Ad", "AA", "ac"].map(makeUser)}); var chan = new Chan({users: ["aB", "Ad", "AA", "ac"].map(makeUser)});
chan.sortUsers({network: {options: {PREFIX: fullNetworkPrefix}}}); chan.sortUsers(network);
expect(getUserNames(chan)).to.deep.equal(["AA", "aB", "ac", "Ad"]); expect(getUserNames(chan)).to.deep.equal(["AA", "aB", "ac", "Ad"]);
}); });
@ -79,7 +91,7 @@ describe("Chan", function() {
"[foo", "]foo", "(foo)", "{foo}", "<foo>", "_foo", "@foo", "^foo", "[foo", "]foo", "(foo)", "{foo}", "<foo>", "_foo", "@foo", "^foo",
"&foo", "!foo", "+foo", "Foo" "&foo", "!foo", "+foo", "Foo"
].map(makeUser)}); ].map(makeUser)});
chan.sortUsers({network: {options: {PREFIX: fullNetworkPrefix}}}); chan.sortUsers(network);
expect(getUserNames(chan)).to.deep.equal([ expect(getUserNames(chan)).to.deep.equal([
"!foo", "&foo", "(foo)", "+foo", "<foo>", "@foo", "[foo", "]foo", "!foo", "&foo", "(foo)", "+foo", "<foo>", "@foo", "[foo", "]foo",