1 var FilterList
= require("cytubefilters");
2 var ChannelModule
= require("./module");
4 const LOGGER
= require('@calzoneman/jsli')('filters');
7 * Converts JavaScript-style replacements ($1, $2, etc.) with
8 * PCRE-style (\1, \2, etc.)
10 function fixReplace(replace
) {
11 return replace
.replace(/\$(\d)/g, "\\$1");
14 function validateFilter(f
) {
15 if (typeof f
.source
!== "string" || typeof f
.flags
!== "string" ||
16 typeof f
.replace
!== "string") {
20 if (typeof f
.name
!== "string") {
24 f
.replace
= fixReplace(f
.replace
.substring(0, 1000));
25 f
.flags
= f
.flags
.substring(0, 4);
28 FilterList
.checkValidRegex(f
.source
);
36 replace
: fixReplace(f
.replace
),
39 filterlinks
: !!f
.filterlinks
45 function makeDefaultFilter(name
, source
, flags
, replace
) {
56 const DEFAULT_FILTERS
= [
57 makeDefaultFilter("monospace", "`(.+?)`", "g", "<code>\\1</code>"),
58 makeDefaultFilter("bold", "\\*(.+?)\\*", "g", "<strong>\\1</strong>"),
59 makeDefaultFilter("italic", "_(.+?)_", "g", "<em>\\1</em>"),
60 makeDefaultFilter("strike", "~~(.+?)~~", "g", "<s>\\1</s>"),
61 makeDefaultFilter("inline spoiler", "\\[sp\\](.*?)\\[\\/sp\\]", "ig",
62 "<span class=\"spoiler\">\\1</span>")
65 function ChatFilterModule(_channel
) {
66 ChannelModule
.apply(this, arguments
);
67 this.filters
= new FilterList();
68 this.supportsDirtyCheck
= true;
71 ChatFilterModule
.prototype = Object
.create(ChannelModule
.prototype);
73 ChatFilterModule
.prototype.load = function (data
) {
74 if ("filters" in data
) {
75 var filters
= data
.filters
.map(validateFilter
).filter(function (f
) {
79 this.filters
= new FilterList(filters
);
81 LOGGER
.error("Filter load failed: " + e
+ " (channel:" +
83 this.channel
.logger
.log("Failed to load filters: " + e
);
86 this.filters
= new FilterList(DEFAULT_FILTERS
);
92 ChatFilterModule
.prototype.save = function (data
) {
93 data
.filters
= this.filters
.pack();
96 ChatFilterModule
.prototype.packInfo = function (data
, isAdmin
) {
98 data
.chatFilterCount
= this.filters
.length
;
102 ChatFilterModule
.prototype.onUserPostJoin = function (user
) {
103 user
.socket
.on("addFilter", this.handleAddFilter
.bind(this, user
));
104 user
.socket
.on("updateFilter", this.handleUpdateFilter
.bind(this, user
));
105 user
.socket
.on("importFilters", this.handleImportFilters
.bind(this, user
));
106 user
.socket
.on("moveFilter", this.handleMoveFilter
.bind(this, user
));
107 user
.socket
.on("removeFilter", this.handleRemoveFilter
.bind(this, user
));
108 user
.socket
.on("requestChatFilters", this.handleRequestChatFilters
.bind(this, user
));
111 ChatFilterModule
.prototype.sendChatFilters = function (users
) {
112 var f
= this.filters
.pack();
113 var chan
= this.channel
;
114 users
.forEach(function (u
) {
115 if (chan
.modules
.permissions
.canEditFilters(u
)) {
116 u
.socket
.emit("chatFilters", f
);
121 ChatFilterModule
.prototype.handleAddFilter = function (user
, data
) {
122 if (typeof data
!== "object") {
126 if (!this.channel
.modules
.permissions
.canEditFilters(user
)) {
131 FilterList
.checkValidRegex(data
.source
);
133 user
.socket
.emit("errorMsg", {
134 msg
: "Invalid regex: " + e
.message
,
140 data
= validateFilter(data
);
146 this.filters
.addFilter(data
);
148 user
.socket
.emit("errorMsg", {
149 msg
: "Filter add failed: " + e
.message
,
157 user
.socket
.emit("addFilterSuccess");
159 var chan
= this.channel
;
160 chan
.users
.forEach(function (u
) {
161 if (chan
.modules
.permissions
.canEditFilters(u
)) {
162 u
.socket
.emit("updateChatFilter", data
);
166 chan
.logger
.log("[mod] " + user
.getName() + " added filter: " + data
.name
+ " -> " +
167 "s/" + data
.source
+ "/" + data
.replace
+ "/" + data
.flags
+
168 " active: " + data
.active
+ ", filterlinks: " + data
.filterlinks
);
171 ChatFilterModule
.prototype.handleUpdateFilter = function (user
, data
) {
172 if (typeof data
!== "object") {
176 if (!this.channel
.modules
.permissions
.canEditFilters(user
)) {
181 FilterList
.checkValidRegex(data
.source
);
183 user
.socket
.emit("errorMsg", {
184 msg
: "Invalid regex: " + e
.message
,
190 data
= validateFilter(data
);
196 this.filters
.updateFilter(data
);
198 user
.socket
.emit("errorMsg", {
199 msg
: "Filter update failed: " + e
.message
,
207 var chan
= this.channel
;
208 chan
.users
.forEach(function (u
) {
209 if (chan
.modules
.permissions
.canEditFilters(u
)) {
210 u
.socket
.emit("updateChatFilter", data
);
214 chan
.logger
.log("[mod] " + user
.getName() + " updated filter: " + data
.name
+ " -> " +
215 "s/" + data
.source
+ "/" + data
.replace
+ "/" + data
.flags
+
216 " active: " + data
.active
+ ", filterlinks: " + data
.filterlinks
);
219 ChatFilterModule
.prototype.handleImportFilters = function (user
, data
) {
220 if (!(data
instanceof Array
)) {
224 /* Note: importing requires a different permission node than simply
226 if (!this.channel.modules.permissions.canImportFilters(user)) {
231 this.filters = new FilterList(data.map(validateFilter).filter(function (f) {
235 user.socket.emit("errorMsg", {
236 msg: "Filter import failed: " + e.message,
244 this.channel.logger.log("[mod] " + user.getName() + " imported the filter list");
245 this.sendChatFilters(this.channel.users);
248 ChatFilterModule.prototype.handleRemoveFilter = function (user, data) {
249 if (typeof data !== "object") {
253 if (!this.channel.modules.permissions.canEditFilters(user)) {
257 if (typeof data.name !== "string") {
262 this.filters.removeFilter(data);
264 user.socket.emit("errorMsg", {
265 msg: "Filter removal failed: " + e.message,
273 var chan = this.channel;
274 chan.users.forEach(function (u) {
275 if (chan.modules.permissions.canEditFilters(u)) {
276 u.socket.emit("deleteChatFilter", data);
280 this.channel.logger.log("[mod] " + user.getName() + " removed filter: " + data.name);
283 ChatFilterModule.prototype.handleMoveFilter = function (user, data) {
284 if (typeof data !== "object") {
288 if (!this.channel.modules.permissions.canEditFilters(user)) {
292 if (typeof data.to !== "number" || typeof data.from !== "number") {
297 this.filters.moveFilter(data.from, data.to);
299 user.socket.emit("errorMsg", {
300 msg: "Filter move failed: " + e.message,
309 ChatFilterModule.prototype.handleRequestChatFilters = function (user) {
310 this.sendChatFilters([user]);
313 module.exports = ChatFilterModule;