Add MOTD and chat filter interface

This commit is contained in:
calzoneman 2013-04-01 16:02:09 -05:00
parent a0c26ee0e5
commit fab4039fc1
9 changed files with 192 additions and 30 deletions

View File

@ -48,6 +48,10 @@ var Channel = function(name) {
[/(^| )_([^_]+)_/g , "$1<em>$2</em>" , true], [/(^| )_([^_]+)_/g , "$1<em>$2</em>" , true],
[/\\\\([-a-zA-Z0-9]+)/g, "[](/$1)" , true] [/\\\\([-a-zA-Z0-9]+)/g, "[](/$1)" , true]
]; ];
this.motd = {
motd: "",
html: ""
};
this.ipbans = {}; this.ipbans = {};
this.logger = new Logger.Logger("chanlogs/" + this.name + ".log"); this.logger = new Logger.Logger("chanlogs/" + this.name + ".log");
@ -76,6 +80,9 @@ var Channel = function(name) {
data.filters[i][2]]; data.filters[i][2]];
} }
} }
if(data.motd) {
this.motd = data.motd;
}
} }
catch(e) { catch(e) {
Logger.errlog.log("Channel dump load failed: "); Logger.errlog.log("Channel dump load failed: ");
@ -435,6 +442,7 @@ Channel.prototype.userJoin = function(user) {
user.socket.emit("newPoll", this.poll.packUpdate()); user.socket.emit("newPoll", this.poll.packUpdate());
} }
user.socket.emit("channelOpts", this.opts); user.socket.emit("channelOpts", this.opts);
user.socket.emit("updateMotd", this.motd);
if(user.playerReady) if(user.playerReady)
this.sendMediaUpdate(user); this.sendMediaUpdate(user);
this.logger.log("+++ /" + user.ip + " joined"); this.logger.log("+++ /" + user.ip + " joined");
@ -797,10 +805,7 @@ Channel.prototype.chatMessage = function(user, msg) {
this.sendMessage(user.name, msg, ""); this.sendMessage(user.name, msg, "");
} }
Channel.prototype.sendMessage = function(username, msg, msgclass) { Channel.prototype.filterMessage = function(msg) {
// I don"t want HTML from strangers
msg = msg.replace(/</g, "&lt;").replace(/>/g, "&gt;");
// Match URLs
msg = msg.replace(/(((https?)|(ftp))(:\/\/[0-9a-zA-Z\.]+(:[0-9]+)?[^\s$]+))/g, "<a href=\"$1\" target=\"_blank\">$1</a>"); msg = msg.replace(/(((https?)|(ftp))(:\/\/[0-9a-zA-Z\.]+(:[0-9]+)?[^\s$]+))/g, "<a href=\"$1\" target=\"_blank\">$1</a>");
// Apply other filters // Apply other filters
for(var i = 0; i < this.filters.length; i++) { for(var i = 0; i < this.filters.length; i++) {
@ -810,12 +815,13 @@ Channel.prototype.sendMessage = function(username, msg, msgclass) {
var replace = this.filters[i][1]; var replace = this.filters[i][1];
msg = msg.replace(regex, replace); msg = msg.replace(regex, replace);
} }
// Chat modifier - monospace return msg;
//msg = msg.replace(/`([^`]+)`/g, "<span class=\"mono\">$1</span>"); }
// Bold
//msg = msg.replace(/\*\*([^\*]+)\*\*/g, "<strong>$1</strong>"); Channel.prototype.sendMessage = function(username, msg, msgclass) {
// Italic // I don"t want HTML from strangers
//msg = msg.replace(/\*([^\*]+)\*/g, "<em>$1</em>"); msg = msg.replace(/</g, "&lt;").replace(/>/g, "&gt;");
msg = this.filterMessage(msg);
this.sendAll("chatMsg", { this.sendAll("chatMsg", {
username: username, username: username,
msg: msg, msg: msg,
@ -961,20 +967,13 @@ Channel.prototype.broadcastNewUser = function(user) {
rank: user.rank, rank: user.rank,
leader: this.leader == user leader: this.leader == user
}); });
this.handleRankChange(user);
} }
// Someone"s rank changed, or their leadership status changed Channel.prototype.handleRankChange = function(user) {
Channel.prototype.broadcastRankUpdate = function(user) {
user.socket.emit("rank", { user.socket.emit("rank", {
rank: user.rank rank: user.rank
}); });
this.sendAll("updateUser", {
name: user.name,
rank: user.rank,
leader: this.leader == user
});
// Rank specific stuff
if(Rank.hasPermission(user, "ipban")) { if(Rank.hasPermission(user, "ipban")) {
var ents = []; var ents = [];
for(var ip in this.ipbans) { for(var ip in this.ipbans) {
@ -997,6 +996,16 @@ Channel.prototype.broadcastRankUpdate = function(user) {
} }
} }
// Someone"s rank changed, or their leadership status changed
Channel.prototype.broadcastRankUpdate = function(user) {
this.sendAll("updateUser", {
name: user.name,
rank: user.rank,
leader: this.leader == user
});
this.handleRankChange(user);
}
Channel.prototype.broadcastPoll = function() { Channel.prototype.broadcastPoll = function() {
this.sendAll("newPoll", this.poll.packUpdate()); this.sendAll("newPoll", this.poll.packUpdate());
} }
@ -1043,6 +1052,10 @@ Channel.prototype.broadcastChatFilters = function() {
} }
} }
Channel.prototype.broadcastMotd = function() {
this.sendAll("updateMotd", this.motd);
}
// Send to ALL the clients! // Send to ALL the clients!
Channel.prototype.sendAll = function(message, data) { Channel.prototype.sendAll = function(message, data) {
io.sockets.in(this.name).emit(message, data); io.sockets.in(this.name).emit(message, data);

View File

@ -30,6 +30,7 @@ var permissions = {
channelOpts : exports.Moderator, channelOpts : exports.Moderator,
jump : exports.Moderator, jump : exports.Moderator,
chatFilter : exports.Moderator, chatFilter : exports.Moderator,
updateMotd : exports.Moderator,
search : exports.Guest, search : exports.Guest,
chat : exports.Guest, chat : exports.Guest,
}; };

View File

@ -66,7 +66,8 @@ function shutdown() {
currentPosition: chan.currentPosition, currentPosition: chan.currentPosition,
queue: chan.queue, queue: chan.queue,
opts: chan.opts, opts: chan.opts,
filters: filts filters: filts,
motd: chan.motd
}; };
var text = JSON.stringify(dump); var text = JSON.stringify(dump);
fs.writeFileSync("chandump/" + name, text); fs.writeFileSync("chandump/" + name, text);

26
user.js
View File

@ -250,12 +250,28 @@ User.prototype.initCallbacks = function() {
}.bind(this)); }.bind(this));
this.socket.on("chatFilter", function(data) { this.socket.on("chatFilter", function(data) {
if(data.cmd && data.cmd == "update" && this.channel != null) { if(Rank.hasPermission(this, "chatFilter")) {
data.filter[0] = new RegExp(data.filter[0], "g"); if(data.cmd && data.cmd == "update" && this.channel != null) {
this.channel.updateFilter(data.filter); data.filter[0] = new RegExp(data.filter[0], "g");
this.channel.updateFilter(data.filter);
}
else if(data.cmd && data.cmd == "remove" && this.channel != null) {
this.channel.removeFilter(data.filter[0]);
}
} }
else if(data.cmd && data.cmd == "remove" && this.channel != null) { }.bind(this));
this.channel.removeFilter(data.filter[0]);
this.socket.on("updateMotd", function(data) {
if(Rank.hasPermission(this, "updateMotd")) {
if(data.motd != undefined && this.channel != null) {
var html = data.motd.replace(/\n/g, "<br>");
html = this.channel.filterMessage(html);
this.channel.motd = {
motd: data.motd,
html: html
};
this.channel.broadcastMotd();
}
} }
}.bind(this)); }.bind(this));
} }

View File

@ -124,3 +124,7 @@
.nick-highlight { .nick-highlight {
background-color: #ddffdd; background-color: #ddffdd;
} }
#motdtext {
width: 100%;
}

View File

@ -26,6 +26,19 @@ function initCallbacks() {
showAnnouncement(data.title, data.text); showAnnouncement(data.title, data.text);
}); });
socket.on("updateMotd", function(data) {
$("#motdtext").val(data.motd);
if(data.motd != "")
$("#motd").parent().css("display", "");
else
$("#motd").parent().css("display", "none");
$("#motd")[0].innerHTML = data.html;
});
socket.on("chatFilters", function(data) {
updateChatFilters(data.filters);
});
socket.on("registerChannel", function(data) { socket.on("registerChannel", function(data) {
if(data.success) { if(data.success) {
$("#chregnotice").remove(); $("#chregnotice").remove();

View File

@ -278,18 +278,47 @@ $("#opt_submit").click(function() {
socket.emit("channelOpts", opts); socket.emit("channelOpts", opts);
}); });
$("#updatemotd").click(function() {
var motd = $("#motdtext").val();
socket.emit("updateMotd", {
motd: motd
});
});
$("#show_chancontrols").click(function() { $("#show_chancontrols").click(function() {
$("#show_chancontrols").parent().addClass("active");
$("#show_banlist").parent().removeClass("active"); $("#show_banlist").parent().removeClass("active");
$("#banlist").hide(); $("#show_motdeditor").parent().removeClass("active");
$("#show_filtereditor").parent().removeClass("active");
$("#show_chancontrols").parent().addClass("active");
$(".modonly").hide();
$("#chancontrols").show(); $("#chancontrols").show();
}); });
$("#show_banlist").click(function() { $("#show_banlist").click(function() {
$("#show_chancontrols").parent().removeClass("active"); $("#show_chancontrols").parent().removeClass("active");
$("#show_motdeditor").parent().removeClass("active");
$("#show_filtereditor").parent().removeClass("active");
$("#show_banlist").parent().addClass("active"); $("#show_banlist").parent().addClass("active");
$(".modonly").hide();
$("#banlist").show(); $("#banlist").show();
$("#chancontrols").hide(); });
$("#show_motdeditor").click(function() {
$("#show_chancontrols").parent().removeClass("active");
$("#show_banlist").parent().removeClass("active");
$("#show_filtereditor").parent().removeClass("active");
$("#show_motdeditor").parent().addClass("active");
$(".modonly").hide();
$("#motdeditor").show();
});
$("#show_filtereditor").click(function() {
$("#show_chancontrols").parent().removeClass("active");
$("#show_banlist").parent().removeClass("active");
$("#show_motdeditor").parent().removeClass("active");
$("#show_filtereditor").parent().addClass("active");
$(".modonly").hide();
$("#filtereditor").show();
}); });
function searchLibrary() { function searchLibrary() {

View File

@ -675,6 +675,62 @@ function updateBanlist(entries) {
} }
} }
function updateChatFilters(entries) {
var tbl = $("#filtereditor table");
if(tbl.children().length > 1) {
$(tbl.children()[1]).remove();
}
for(var i = 0; i < entries.length; i++) {
var tr = $("<tr/>").appendTo(tbl);
var remove = $("<button/>").addClass("btn btn-mini btn-danger")
.appendTo($("<td/>").appendTo(tr));
$("<i/>").addClass("icon-remove-circle").appendTo(remove);
var regex = $("<code/>").text(entries[i][0])
.appendTo($("<td/>").appendTo(tr));
var replace = $("<code/>").text(entries[i][1])
.appendTo($("<td/>").appendTo(tr));
var activetd = $("<td/>").appendTo(tr);
var active = $("<input/>").attr("type", "checkbox")
.prop("checked", entries[i][2]).appendTo(activetd);
var remcallback = (function(filter) { return function() {
socket.emit("chatFilter", {
cmd: "remove",
filter: filter
});
} })(entries[i]);
remove.click(remcallback);
var actcallback = (function(filter) { return function() {
filter[2] = active.prop("checked");
socket.emit("chatFilter", {
cmd: "update",
filter: filter
});
} })(entries[i]);
active.click(actcallback);
}
var newfilt = $("<tr/>").appendTo(tbl);
$("<td/>").appendTo(newfilt);
var regex = $("<input/>").attr("type", "text")
.appendTo($("<td/>").appendTo(newfilt));
var replace = $("<input/>").attr("type", "text")
.appendTo($("<td/>").appendTo(newfilt));
var add = $("<button/>").addClass("btn btn-primary")
.text("Add Filter")
.appendTo($("<td/>").appendTo(newfilt));
var cback = (function(regex, replace) { return function() {
if(regex.val() && replace.val()) {
socket.emit("chatFilter", {
cmd: "update",
filter: [regex.val(), replace.val(), true]
});
}
} })(regex, replace);
add.click(cback);
}
function handleRankChange() { function handleRankChange() {
rebuildPlaylist(); rebuildPlaylist();
if(RANK >= Rank.Moderator || LEADER) { if(RANK >= Rank.Moderator || LEADER) {

View File

@ -48,6 +48,11 @@
<h3 id="welcome"></h3> <h3 id="welcome"></h3>
</div> </div>
</div> </div>
<div class="row">
<div class="span10 offset1 well">
<p id="motd"></p>
</div>
</div>
<div class="row" style="margin-top: 20px;"> <div class="row" style="margin-top: 20px;">
<div class="span5" id="chatdiv"> <div class="span5" id="chatdiv">
<p id="usercount"></p> <p id="usercount"></p>
@ -97,10 +102,16 @@
<li> <li>
<a href="javascript:void(0)" id="show_banlist">Ban List</a> <a href="javascript:void(0)" id="show_banlist">Ban List</a>
</li> </li>
<li>
<a href="javascript:void(0)" id="show_motdeditor">MOTD</a>
</li>
<li>
<a href="javascript:void(0)" id="show_filtereditor">Chat Filters</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>
<div class="row" style="display: none" id="chancontrols"> <div class="row modonly" style="display: none" id="chancontrols">
<div class="span10 offset1"> <div class="span10 offset1">
<form action="javascript:void(0)"> <form action="javascript:void(0)">
<fieldset> <fieldset>
@ -141,7 +152,7 @@
</form> </form>
</div> </div>
</div> </div>
<div class="row" id="banlist" style="display: none;"> <div class="row modonly" id="banlist" style="display: none;">
<div class="span10 offset1"> <div class="span10 offset1">
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
@ -153,6 +164,24 @@
</table> </table>
</div> </div>
</div> </div>
<div class="row modonly" id="motdeditor" style="display: none;">
<div class="span10 offset1">
<textarea rows="10" id="motdtext"></textarea>
<button class="btn btn-primary" id="updatemotd">Update</button>
</div>
</div>
<div class="row modonly" id="filtereditor" style="display: none;">
<div class="span10 offset1">
<table class="table table-striped">
<thead>
<th></th>
<th>Regex</th>
<th>Replacement</th>
<th>Active</th>
</thead>
</table>
</div>
</div>
<div class="row"> <div class="row">
<div class="span4 offset3"> <div class="span4 offset3">
<div class="btn-group" style="width: 100%"> <div class="btn-group" style="width: 100%">