fore.st/src/channel/ranks.js
calzoneman 0ee7f05213 Make polls more efficient
Instead of emitting frames to each individual socket, group them into
socket.io rooms of people who can see hidden poll results and people who
can't, then just do 2 broadcasts.
2016-04-02 11:57:26 -07:00

194 lines
6.2 KiB
JavaScript

var ChannelModule = require("./module");
var Flags = require("../flags");
var Account = require("../account");
var db = require("../database");
const TYPE_SET_CHANNEL_RANK = {
name: "string",
rank: "number"
};
function RankModule(channel) {
ChannelModule.apply(this, arguments);
if (this.channel.modules.chat) {
this.channel.modules.chat.registerCommand("/rank", this.handleCmdRank.bind(this));
}
}
RankModule.prototype = Object.create(ChannelModule.prototype);
RankModule.prototype.onUserPostJoin = function (user) {
user.socket.typecheckedOn("setChannelRank", TYPE_SET_CHANNEL_RANK, this.handleRankChange.bind(this, user));
var self = this;
user.socket.on("requestChannelRanks", function () {
self.sendChannelRanks([user]);
});
};
RankModule.prototype.sendChannelRanks = function (users) {
if (!this.channel.is(Flags.C_REGISTERED)) {
return;
}
db.channels.allRanks(this.channel.name, function (err, ranks) {
if (err) {
return;
}
users.forEach(function (u) {
if (u.account.effectiveRank >= 3) {
u.socket.emit("channelRanks", ranks);
}
});
});
};
RankModule.prototype.handleCmdRank = function (user, msg, meta) {
var args = msg.split(" ");
args.shift(); /* shift off /rank */
var name = args.shift();
var rank = parseInt(args.shift());
if (!name || isNaN(rank)) {
user.socket.emit("noflood", {
action: "/rank",
msg: "Syntax: /rank <username> <rank>. <rank> must be a positive integer > 1"
});
return;
}
this.handleRankChange(user, { name: name, rank: rank });
};
RankModule.prototype.handleRankChange = function (user, data) {
if (user.account.effectiveRank < 3) {
return;
}
var rank = data.rank;
var userrank = user.account.effectiveRank;
var name = data.name.substring(0, 20).toLowerCase();
if (!name.match(/^[a-zA-Z0-9_-]{1,20}$/)) {
user.socket.emit("channelRankFail", {
msg: "Invalid target name " + data.name
});
return;
}
if (isNaN(rank) || rank < 1 || (rank >= userrank && !(userrank === 4 && rank === 4))) {
user.socket.emit("channelRankFail", {
msg: "Updating user rank failed: You can't promote someone to a rank equal " +
"or higher than yourself, or demote them to below rank 1."
});
return;
}
var receiver;
var lowerName = name.toLowerCase();
for (var i = 0; i < this.channel.users.length; i++) {
if (this.channel.users[i].getLowerName() === lowerName) {
receiver = this.channel.users[i];
break;
}
}
if (name === user.getLowerName()) {
user.socket.emit("channelRankFail", {
msg: "Updating user rank failed: You can't promote or demote yourself."
});
return;
}
if (!this.channel.is(Flags.C_REGISTERED)) {
user.socket.emit("channelRankFail", {
msg: "Updating user rank failed: in an unregistered channel, a user must " +
"be online in the channel in order to have their rank changed."
});
return;
}
if (receiver) {
var current = Math.max(receiver.account.globalRank, receiver.account.channelRank);
if (current >= userrank && !(userrank === 4 && current === 4)) {
user.socket.emit("channelRankFail", {
msg: "Updating user rank failed: You can't promote or demote "+
"someone who has equal or higher rank than yourself"
});
return;
}
receiver.account.channelRank = rank;
receiver.account.effectiveRank = Math.max(receiver.account.globalRank, rank);
receiver.emit("effectiveRankChange", receiver.account.effectiveRank);
receiver.socket.emit("rank", receiver.account.effectiveRank);
this.channel.logger.log("[mod] " + user.getName() + " set " + name + "'s rank " +
"to " + rank);
this.channel.broadcastAll("setUserRank", data);
if (!this.channel.is(Flags.C_REGISTERED)) {
user.socket.emit("channelRankFail", {
msg: "This channel is not registered. Any rank changes are temporary " +
"and not stored in the database."
});
return;
}
if (!receiver.is(Flags.U_REGISTERED)) {
user.socket.emit("channelRankFail", {
msg: "The user you promoted is not a registered account. " +
"Any rank changes are temporary and not stored in the database."
});
return;
}
data.userrank = userrank;
this.updateDatabase(data, function (err) {
if (err) {
user.socket.emit("channelRankFail", {
msg: "Database failure when updating rank"
});
}
});
} else {
data.userrank = userrank;
var self = this;
this.updateDatabase(data, function (err) {
if (err) {
user.socket.emit("channelRankFail", {
msg: "Updating user rank failed: " + err
});
}
self.channel.logger.log("[mod] " + user.getName() + " set " + data.name +
"'s rank to " + rank);
self.channel.broadcastAll("setUserRank", data);
if (self.channel.modules.chat) {
self.channel.modules.chat.sendModMessage(
user.getName() + " set " + data.name + "'s rank to " + rank,
3
);
}
});
}
};
RankModule.prototype.updateDatabase = function (data, cb) {
var chan = this.channel;
Account.rankForName(data.name, { channel: this.channel.name }, function (err, rank) {
if (err) {
return cb(err);
}
if (rank >= data.userrank && !(rank === 4 && data.userrank === 4)) {
cb("You can't promote or demote someone with equal or higher rank than you.");
return;
}
db.channels.setRank(chan.name, data.name, data.rank, cb);
});
};
module.exports = RankModule;