mirror of https://github.com/calzoneman/sync.git
Cytubefilters, part 1
This commit is contained in:
parent
709724efd4
commit
aa5e50f1d2
|
@ -315,7 +315,7 @@ ChatModule.prototype.filterMessage = function (msg) {
|
|||
/* substring is a URL */
|
||||
if (convertLinks && parts[j].match(link)) {
|
||||
var original = parts[j];
|
||||
parts[j] = filters.exec(parts[j], { filterlinks: true });
|
||||
parts[j] = filters.filter(parts[j], true);
|
||||
|
||||
/* no filters changed the URL, apply link filter */
|
||||
if (parts[j] === original) {
|
||||
|
@ -325,7 +325,7 @@ ChatModule.prototype.filterMessage = function (msg) {
|
|||
|
||||
} else {
|
||||
/* substring is not a URL */
|
||||
parts[j] = filters.exec(parts[j], { filterlinks: false });
|
||||
parts[j] = filters.filter(parts[j], false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,120 +1,7 @@
|
|||
var FilterList = require('cytubefilters');
|
||||
var ChannelModule = require("./module");
|
||||
var XSS = require("../xss");
|
||||
|
||||
function ChatFilter(name, regex, flags, replace, active, filterlinks) {
|
||||
this.name = name;
|
||||
this.source = regex;
|
||||
this.flags = flags;
|
||||
this.regex = new RegExp(this.source, flags);
|
||||
this.replace = replace;
|
||||
this.active = active === false ? false : true;
|
||||
this.filterlinks = filterlinks || false;
|
||||
}
|
||||
|
||||
ChatFilter.prototype = {
|
||||
pack: function () {
|
||||
return {
|
||||
name: this.name,
|
||||
source: this.source,
|
||||
flags: this.flags,
|
||||
replace: this.replace,
|
||||
active: this.active,
|
||||
filterlinks: this.filterlinks
|
||||
};
|
||||
},
|
||||
|
||||
exec: function (str) {
|
||||
return str.replace(this.regex, this.replace);
|
||||
}
|
||||
};
|
||||
|
||||
function FilterList(defaults) {
|
||||
if (!defaults) {
|
||||
defaults = [];
|
||||
}
|
||||
|
||||
this.filters = defaults.map(function (f) {
|
||||
return new ChatFilter(f.name, f.source, f.flags, f.replace, f.active, f.filterlinks);
|
||||
});
|
||||
}
|
||||
|
||||
FilterList.prototype = {
|
||||
pack: function () {
|
||||
return this.filters.map(function (f) { return f.pack(); });
|
||||
},
|
||||
|
||||
importList: function (filters) {
|
||||
this.filters = Array.prototype.slice.call(filters);
|
||||
},
|
||||
|
||||
updateFilter: function (filter) {
|
||||
if (!filter.name) {
|
||||
filter.name = filter.source;
|
||||
}
|
||||
|
||||
var found = false;
|
||||
for (var i = 0; i < this.filters.length; i++) {
|
||||
if (this.filters[i].name === filter.name) {
|
||||
found = true;
|
||||
this.filters[i] = filter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If no filter was updated, add a new one */
|
||||
if (!found) {
|
||||
this.filters.push(filter);
|
||||
}
|
||||
},
|
||||
|
||||
removeFilter: function (filter) {
|
||||
var found = false;
|
||||
for (var i = 0; i < this.filters.length; i++) {
|
||||
if (this.filters[i].name === filter.name) {
|
||||
this.filters.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
moveFilter: function (from, to) {
|
||||
if (from < 0 || to < 0 ||
|
||||
from >= this.filters.length || to >= this.filters.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var f = this.filters[from];
|
||||
/* Offset from/to indexes to account for the fact that removing
|
||||
an element changes the position of one of them.
|
||||
|
||||
I could have just done a swap, but it's already implemented this way
|
||||
and it works. */
|
||||
to = to > from ? to + 1 : to;
|
||||
from = to > from ? from : from + 1;
|
||||
|
||||
this.filters.splice(to, 0, f);
|
||||
this.filters.splice(from, 1);
|
||||
return true;
|
||||
},
|
||||
|
||||
exec: function (str, opts) {
|
||||
if (!opts) {
|
||||
opts = {};
|
||||
}
|
||||
|
||||
this.filters.forEach(function (f) {
|
||||
if (opts.filterlinks && !f.filterlinks) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (f.active) {
|
||||
str = f.exec(str);
|
||||
}
|
||||
});
|
||||
|
||||
return str;
|
||||
}
|
||||
};
|
||||
var Logger = require("../logger");
|
||||
|
||||
function validateFilter(f) {
|
||||
if (typeof f.source !== "string" || typeof f.flags !== "string" ||
|
||||
|
@ -136,17 +23,39 @@ function validateFilter(f) {
|
|||
return false;
|
||||
}
|
||||
|
||||
var filter = new ChatFilter(f.name, f.source, f.flags, f.replace,
|
||||
Boolean(f.active), Boolean(f.filterlinks));
|
||||
var filter = {
|
||||
name: f.name,
|
||||
source: f.source,
|
||||
replace: fixReplace(f.replace),
|
||||
flags: f.flags,
|
||||
active: !!f.active,
|
||||
filterlinks: !!f.filterlinks
|
||||
};
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
function fixReplace(replace) {
|
||||
return replace.replace(/\$(\d)/g, '\\$1');
|
||||
}
|
||||
|
||||
function makeDefaultFilter(name, source, flags, replace) {
|
||||
return {
|
||||
name: name,
|
||||
source: source,
|
||||
flags: flags,
|
||||
replace: fixReplace(replace),
|
||||
active: true,
|
||||
filterlinks: false
|
||||
};
|
||||
}
|
||||
|
||||
const DEFAULT_FILTERS = [
|
||||
new ChatFilter("monospace", "`(.+?)`", "g", "<code>$1</code>"),
|
||||
new ChatFilter("bold", "\\*(.+?)\\*", "g", "<strong>$1</strong>"),
|
||||
new ChatFilter("italic", "_(.+?)_", "g", "<em>$1</em>"),
|
||||
new ChatFilter("strike", "~~(.+?)~~", "g", "<s>$1</s>"),
|
||||
new ChatFilter("inline spoiler", "\\[sp\\](.*?)\\[\\/sp\\]", "ig", "<span class=\"spoiler\">$1</span>")
|
||||
makeDefaultFilter("monospace", "`(.+?)`", "g", "<code>$1</code>"),
|
||||
makeDefaultFilter("bold", "\\*(.+?)\\*", "g", "<strong>$1</strong>"),
|
||||
makeDefaultFilter("italic", "_(.+?)_", "g", "<em>$1</em>"),
|
||||
makeDefaultFilter("strike", "~~(.+?)~~", "g", "<s>$1</s>"),
|
||||
makeDefaultFilter("inline spoiler", "\\[sp\\](.*?)\\[\\/sp\\]", "ig", "<span class=\"spoiler\">$1</span>")
|
||||
];
|
||||
|
||||
function ChatFilterModule(channel) {
|
||||
|
@ -161,7 +70,21 @@ ChatFilterModule.prototype.load = function (data) {
|
|||
for (var i = 0; i < data.filters.length; i++) {
|
||||
var f = validateFilter(data.filters[i]);
|
||||
if (f) {
|
||||
this.filters.updateFilter(f);
|
||||
try {
|
||||
this.filters.updateFilter(f);
|
||||
} catch (e) {
|
||||
if (e.message.match(/does not exist/i)) {
|
||||
try {
|
||||
this.filters.addFilter(f);
|
||||
} catch (e) {
|
||||
Logger.errlog.log("Filter load failed: " +
|
||||
JSON.stringify(f) + " c:" + this.channel.name);
|
||||
}
|
||||
} else {
|
||||
Logger.errlog.log("Filter load failed: " +
|
||||
JSON.stringify(f) + " c:" + this.channel.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,11 +96,12 @@ ChatFilterModule.prototype.save = function (data) {
|
|||
|
||||
ChatFilterModule.prototype.packInfo = function (data, isAdmin) {
|
||||
if (isAdmin) {
|
||||
data.chatFilterCount = this.filters.filters.length;
|
||||
data.chatFilterCount = this.filters.length;
|
||||
}
|
||||
};
|
||||
|
||||
ChatFilterModule.prototype.onUserPostJoin = function (user) {
|
||||
user.socket.on("addFilter", this.handleAddFilter.bind(this, user));
|
||||
user.socket.on("updateFilter", this.handleUpdateFilter.bind(this, user));
|
||||
user.socket.on("importFilters", this.handleImportFilters.bind(this, user));
|
||||
user.socket.on("moveFilter", this.handleMoveFilter.bind(this, user));
|
||||
|
@ -195,6 +119,41 @@ ChatFilterModule.prototype.sendChatFilters = function (users) {
|
|||
});
|
||||
};
|
||||
|
||||
ChatFilterModule.prototype.handleAddFilter = function (user, data) {
|
||||
if (typeof data !== "object") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.channel.modules.permissions.canEditFilters(user)) {
|
||||
return;
|
||||
}
|
||||
|
||||
data = validateFilter(data);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.filters.addFilter(data);
|
||||
} catch (e) {
|
||||
user.socket.emit("errorMsg", {
|
||||
msg: "Filter add failed: " + e.message,
|
||||
alert: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
var chan = this.channel;
|
||||
chan.users.forEach(function (u) {
|
||||
if (chan.modules.permissions.canEditFilters(u)) {
|
||||
u.socket.emit("updateChatFilter", data);
|
||||
}
|
||||
});
|
||||
|
||||
chan.logger.log("[mod] " + user.getName() + " added filter: " + data.name + " -> " +
|
||||
"s/" + data.source + "/" + data.replace + "/" + data.flags + " active: " +
|
||||
data.active + ", filterlinks: " + data.filterlinks);
|
||||
};
|
||||
|
||||
ChatFilterModule.prototype.handleUpdateFilter = function (user, data) {
|
||||
if (typeof data !== "object") {
|
||||
return;
|
||||
|
@ -204,13 +163,25 @@ ChatFilterModule.prototype.handleUpdateFilter = function (user, data) {
|
|||
return;
|
||||
}
|
||||
|
||||
var f = validateFilter(data);
|
||||
if (!f) {
|
||||
data = validateFilter(data);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.filters.updateFilter(data);
|
||||
} catch (e) {
|
||||
if (e.message.match(/filter to be updated does not exist/i)) {
|
||||
this.handleAddFilter(user, data);
|
||||
} else {
|
||||
user.socket.emit("errorMsg", {
|
||||
msg: "Filter update failed: " + e.message,
|
||||
alert: true
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
data = f.pack();
|
||||
|
||||
this.filters.updateFilter(f);
|
||||
var chan = this.channel;
|
||||
chan.users.forEach(function (u) {
|
||||
if (chan.modules.permissions.canEditFilters(u)) {
|
||||
|
@ -218,9 +189,9 @@ ChatFilterModule.prototype.handleUpdateFilter = function (user, data) {
|
|||
}
|
||||
});
|
||||
|
||||
chan.logger.log("[mod] " + user.getName() + " updated filter: " + f.name + " -> " +
|
||||
"s/" + f.source + "/" + f.replace + "/" + f.flags + " active: " +
|
||||
f.active + ", filterlinks: " + f.filterlinks);
|
||||
chan.logger.log("[mod] " + user.getName() + " updated filter: " + data.name + " -> " +
|
||||
"s/" + data.source + "/" + data.replace + "/" + data.flags + " active: " +
|
||||
data.active + ", filterlinks: " + data.filterlinks);
|
||||
};
|
||||
|
||||
ChatFilterModule.prototype.handleImportFilters = function (user, data) {
|
||||
|
@ -234,9 +205,17 @@ ChatFilterModule.prototype.handleImportFilters = function (user, data) {
|
|||
return;
|
||||
}
|
||||
|
||||
this.filters.importList(data.map(validateFilter).filter(function (f) {
|
||||
return f !== false;
|
||||
}));
|
||||
try {
|
||||
this.filters = new FilterList(data.map(validateFilter).filter(function (f) {
|
||||
return f !== false;
|
||||
}));
|
||||
} catch (e) {
|
||||
user.socket.emit("errorMsg", {
|
||||
msg: "Filter import failed: " + e.message,
|
||||
alert: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.channel.logger.log("[mod] " + user.getName() + " imported the filter list");
|
||||
this.sendChatFilters(this.channel.users);
|
||||
|
@ -255,7 +234,15 @@ ChatFilterModule.prototype.handleRemoveFilter = function (user, data) {
|
|||
return;
|
||||
}
|
||||
|
||||
this.filters.removeFilter(data);
|
||||
try {
|
||||
this.filters.removeFilter(data);
|
||||
} catch (e) {
|
||||
user.socket.emit("errorMsg", {
|
||||
msg: "Filter removal failed: " + e.message,
|
||||
alert: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
var chan = this.channel;
|
||||
chan.users.forEach(function (u) {
|
||||
if (chan.modules.permissions.canEditFilters(u)) {
|
||||
|
@ -278,7 +265,15 @@ ChatFilterModule.prototype.handleMoveFilter = function (user, data) {
|
|||
return;
|
||||
}
|
||||
|
||||
this.filters.moveFilter(data.from, data.to);
|
||||
try {
|
||||
this.filters.moveFilter(data.from, data.to);
|
||||
} catch (e) {
|
||||
user.socket.emit("errorMsg", {
|
||||
msg: "Filter move failed: " + e.message,
|
||||
alert: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
ChatFilterModule.prototype.handleRequestChatFilters = function (user) {
|
||||
|
|
Loading…
Reference in New Issue