1 var ChannelModule
= require("./module");
2 var XSS
= require("../xss");
4 function EmoteList(defaults
) {
9 this.emotes
= defaults
.map(validateEmote
).filter(function (f
) {
14 EmoteList
.prototype = {
16 return Array
.prototype.slice
.call(this.emotes
);
19 importList: function (emotes
) {
20 this.emotes
= Array
.prototype.slice
.call(emotes
);
23 emoteExists: function (emote
){
24 for (let i
= 0; i
< this.emotes
.length
; i
++) {
25 if (this.emotes
[i
].name
=== emote
.name
) {
33 renameEmote: function (emote
) {
35 for (var i
= 0; i
< this.emotes
.length
; i
++) {
36 if (this.emotes
[i
].name
=== emote
.old
) {
38 this.emotes
[i
] = emote
;
39 delete this.emotes
[i
].old
;
50 updateEmote: function (emote
) {
52 for (var i
= 0; i
< this.emotes
.length
; i
++) {
53 if (this.emotes
[i
].name
=== emote
.name
) {
55 this.emotes
[i
] = emote
;
60 /* If no emote was updated, add a new one */
62 this.emotes
.push(emote
);
66 removeEmote: function (emote
) {
67 for (var i
= 0; i
< this.emotes
.length
; i
++) {
68 if (this.emotes
[i
].name
=== emote
.name
) {
69 this.emotes
.splice(i
, 1);
75 moveEmote: function (from, to
) {
76 if (from < 0 || to
< 0 ||
77 from >= this.emotes
.length
|| to
>= this.emotes
.length
) {
81 var f
= this.emotes
[from];
82 /* Offset from/to indexes to account for the fact that removing
83 an element changes the position of one of them.
85 I could have just done a swap, but it's already implemented this way
87 to
= to
> from ? to
+ 1 : to
;
88 from = to
> from ? from : from + 1;
90 this.emotes
.splice(to
, 0, f
);
91 this.emotes
.splice(from, 1);
96 function validateEmote(f
) {
97 if (typeof f
.name
!== "string" || typeof f
.image
!== "string") {
101 f
.image
= f
.image
.substring(0, 1000);
102 f
.image
= XSS
.sanitizeText(f
.image
);
104 var s
= XSS
.looseSanitizeText(f
.name
).replace(/([\\.?+*$^|()[\]{}])/g, "\\$1");
105 s
= "(^|\\s)" + s
+ "(?!\\S)";
108 if (!f
.image
|| !f
.name
) {
113 new RegExp(f
.source
, "gi");
121 function EmoteModule(_channel
) {
122 ChannelModule
.apply(this, arguments
);
123 this.emotes
= new EmoteList();
124 this.supportsDirtyCheck
= true;
127 EmoteModule
.prototype = Object
.create(ChannelModule
.prototype);
129 EmoteModule
.prototype.load = function (data
) {
130 if ("emotes" in data
) {
131 this.emotes
= new EmoteList(data
.emotes
);
137 EmoteModule
.prototype.save = function (data
) {
138 data
.emotes
= this.emotes
.pack();
141 EmoteModule
.prototype.packInfo = function (data
, isAdmin
) {
143 data
.emoteCount
= this.emotes
.emotes
.length
;
147 EmoteModule
.prototype.onUserPostJoin = function (user
) {
148 user
.socket
.on("renameEmote", this.handleRenameEmote
.bind(this, user
));
149 user
.socket
.on("updateEmote", this.handleUpdateEmote
.bind(this, user
));
150 user
.socket
.on("importEmotes", this.handleImportEmotes
.bind(this, user
));
151 user
.socket
.on("moveEmote", this.handleMoveEmote
.bind(this, user
));
152 user
.socket
.on("removeEmote", this.handleRemoveEmote
.bind(this, user
));
153 this.sendEmotes([user
]);
156 EmoteModule
.prototype.sendEmotes = function (users
) {
157 var f
= this.emotes
.pack();
158 users
.forEach(function (u
) {
159 u
.socket
.emit("emoteList", f
);
163 EmoteModule
.prototype.handleRenameEmote = function (user
, data
) {
164 if (typeof data
!== "object") {
169 ** This shouldn't be able to happen,
170 ** but we have idiots that like to send handcrafted frames to fuck with shit
172 if (typeof data
.old
!== "string"){
176 if (!this.channel
.modules
.permissions
.canEditEmotes(user
)) {
180 var e
= this.emotes
.emoteExists(data
);
181 var f
= validateEmote(data
);
183 var message
= "Unable to rename emote '" + JSON
.stringify(data
) + "'. " +
184 "Please contact an administrator for assistance.";
185 if (!data
.image
|| !data
.name
) {
186 message
= "Emote names and images must not be blank.";
189 message
= "Emote already exists.";
192 user
.socket
.emit("errorMsg", {
200 var success
= this.emotes
.renameEmote(Object
.assign({}, f
));
201 if(!success
){ return; }
205 var chan
= this.channel
;
206 chan
.broadcastAll("renameEmote", f
);
207 chan
.logger
.log(`[mod] ${user.getName()} renamed emote: ${f.old} -> ${f.name}`);
210 EmoteModule
.prototype.handleUpdateEmote = function (user
, data
) {
211 if (typeof data
!== "object") {
215 if (!this.channel
.modules
.permissions
.canEditEmotes(user
)) {
219 var f
= validateEmote(data
);
221 var message
= "Unable to update emote '" + JSON
.stringify(data
) + "'. " +
222 "Please contact an administrator for assistance.";
223 if (!data
.image
|| !data
.name
) {
224 message
= "Emote names and images must not be blank.";
227 user
.socket
.emit("errorMsg", {
234 this.emotes
.updateEmote(f
);
238 var chan
= this.channel
;
239 chan
.broadcastAll("updateEmote", f
);
241 chan
.logger
.log("[mod] " + user
.getName() + " updated emote: " + f
.name
+ " -> " +
245 EmoteModule
.prototype.handleImportEmotes = function (user
, data
) {
246 if (!(data
instanceof Array
)) {
250 /* Note: importing requires a different permission node than simply
252 if (!this.channel.modules.permissions.canImportEmotes(user)) {
256 this.emotes.importList(data.map(validateEmote).filter(function (f) {
262 this.sendEmotes(this.channel.users);
265 EmoteModule.prototype.handleRemoveEmote = function (user, data) {
266 if (typeof data !== "object") {
270 if (!this.channel.modules.permissions.canEditEmotes(user)) {
274 if (typeof data.name !== "string") {
278 this.emotes.removeEmote(data);
282 this.channel.logger.log("[mod] " + user.getName() + " removed emote: " + data.name);
283 this.channel.broadcastAll("removeEmote", data);
286 EmoteModule.prototype.handleMoveEmote = function (user, data) {
287 if (typeof data !== "object") {
291 if (!this.channel.modules.permissions.canEditEmotes(user)) {
295 if (typeof data.to !== "number" || typeof data.from !== "number") {
299 this.emotes.moveEmote(data.from, data.to);
304 module.exports = EmoteModule;