From 53138fe1f05f8827ab0c8363f7be26686eb8c409 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Sun, 9 Feb 2014 23:53:46 -0600 Subject: [PATCH] Start working on emotes --- lib/channel.js | 154 +++++++++++++++++++++++++++++++++++++ www/assets/js/callbacks.js | 26 ++++++- www/assets/js/data.js | 3 +- www/assets/js/util.js | 23 ++++++ www/css/cytube.css | 5 ++ 5 files changed, 209 insertions(+), 2 deletions(-) diff --git a/lib/channel.js b/lib/channel.js index 21b65756..8cb0b06b 100644 --- a/lib/channel.js +++ b/lib/channel.js @@ -103,6 +103,7 @@ function Channel(name) { html: "" // Filtered MOTD text (XSS removed; \n replaced by
) }; self.filters = DEFAULT_FILTERS; + self.emotes = []; self.logger = new Logger.Logger(path.join(__dirname, "../chanlogs", self.uniqueName + ".log")); self.css = ""; // Up to 20KB of inline CSS @@ -264,6 +265,15 @@ Channel.prototype.loadState = function () { } } + // Emotes + if ("emotes" in data) { + data.emotes.forEach(function (e) { + self.emotes.push(e); + }); + + self.updateEmote({ name: ":test:", source: ":test:", image: "http://imgs.xkcd.com/comics/mobile_marketing.png" }); + } + // MOTD if ("motd" in data) { self.motd = { @@ -315,6 +325,7 @@ Channel.prototype.saveState = function () { opts: self.opts, permissions: self.permissions, filters: filters, + emotes: self.emotes, motd: self.motd, playlistLock: self.playlistLock, chatbuffer: self.chatbuffer, @@ -555,6 +566,7 @@ Channel.prototype.join = function (user) { self.sendMediaUpdate([user]); self.sendPlaylistLock([user]); self.sendUserlist([user]); + self.sendEmoteList([user]); self.sendRecentChat([user]); self.sendCSSJS([user]); self.sendPoll([user]); @@ -1013,6 +1025,16 @@ Channel.prototype.sendChatFilters = function (users) { }); }; +/** + * Sends the emote list + */ +Channel.prototype.sendEmoteList = function (users) { + var self = this; + users.forEach(function (u) { + u.socket.emit("emoteList", self.emotes); + }); +}; + /** * Sends the channel permissions */ @@ -2444,6 +2466,138 @@ Channel.prototype.handleMoveFilter = function (user, data) { this.moveFilter(data.from, data.to); }; +/** + * Imports a list of emotes, replacing the current list + */ +Channel.prototype.importEmotes = function (emotes) { + this.emotes = emotes; + this.sendEmoteList(this.users); +}; + +/** + * Handles a user message to import a list of emotes + */ +Channel.prototype.handleImportEmotes = function (user, data) { + if (!this.hasPermission(user, "emoteimport")) { + return; + } + + if (!(data instanceof Array)) { + return; + } + + this.emotes = data.map(this.validateEmote.bind(this)) + .filter(function (f) { return f !== false; }); + + this.sendEmoteList(this.users); +}; + +/** + * Validates data for an emote + */ +Channel.prototype.validateEmote = function (f) { + if (typeof f.source !== "string" || typeof f.image !== "string") { + return false; + } + + if (typeof f.name !== "string") { + f.name = f.source; + } + + f.image = f.image.substring(0, 1000); + f.image = XSS.sanitizeText(f.image); + + try { + new RegExp(f.regex, "gi"); + } catch (e) { + return false; + } + + return f; +}; + +/** + * Updates an emote, or adds a new one if the emote does not exist + */ +Channel.prototype.updateEmote = function (emote) { + var self = this; + + if (!emote.name) { + emote.name = emote.source; + } + + var found = false; + for (var i = 0; i < self.emotes.length; i++) { + if (self.emotes[i].name === emote.name) { + found = true; + self.emotes[i] = emote; + break; + } + } + + if (!found) { + self.emotes.push(emote); + } + + self.users.forEach(function (u) { + u.socket.emit("updateEmote", emote); + }); +}; + +/** + * Handles a user message to update an emote + */ +Channel.prototype.handleUpdateEmote = function (user, f) { + if (!this.hasPermission(user, "emoteedit")) { + user.kick("Attempted updateEmote with insufficient permission"); + return; + } + + var emote = this.validateEmote(f); + if (!emote) { + return; + } + + this.logger.log("[mod] " + user.name + " updated emote: " + f.name + " -> " + + f.image); + this.updateEmote(emote); +}; + +/** + * Removes an emote + */ +Channel.prototype.removeEmote = function (emote) { + var self = this; + + for (var i = 0; i < self.emotes.length; i++) { + if (self.emotes[i].name === emote.name) { + self.emotes.splice(i, 1); + self.users.forEach(function (u) { + u.socket.emit("deleteEmote", emote); + }); + break; + } + } +}; + +/** + * Handles a user message to delete an emote + */ +Channel.prototype.handleRemoveEmote = function (user, f) { + if (!this.hasPermission(user, "emoteedit")) { + user.kick("Attempted removeEmote with insufficient permission"); + return; + } + + if (typeof f.name !== "string") { + return; + } + + this.logger.log("[mod] " + user.name + " removed emote: " + f.name); + this.removeFilter(f); +}; + + /** * Handles a user message to change the channel permissions */ diff --git a/www/assets/js/callbacks.js b/www/assets/js/callbacks.js index 332d18df..62cf7f83 100644 --- a/www/assets/js/callbacks.js +++ b/www/assets/js/callbacks.js @@ -1032,7 +1032,31 @@ Callbacks = { } } } - } + }, + + emoteList: function (data) { + loadEmotes(data); + }, + + updateEmote: function (data) { + data.regex = new RegExp(data.source, "gi"); + var found = false; + for (var i = 0; i < CHANNEL.emotes.length; i++) { + if (CHANNEL.emotes[i].name === data.name) { + found = true; + CHANNEL.emotes[i] = data; + break; + } + } + + if (!found) { + CHANNEL.emotes.push(data); + } + }, + + deleteEmote: function (data) { + + }, } var SOCKET_DEBUG = true; diff --git a/www/assets/js/data.js b/www/assets/js/data.js index 2a7716e2..331e0d40 100644 --- a/www/assets/js/data.js +++ b/www/assets/js/data.js @@ -32,7 +32,8 @@ var CHANNEL = { motd: "", motd_text: "", name: false, - usercount: 0 + usercount: 0, + emotes: [] }; var PLAYER = false; diff --git a/www/assets/js/util.js b/www/assets/js/util.js index b08a50ce..dda5aaf4 100644 --- a/www/assets/js/util.js +++ b/www/assets/js/util.js @@ -1270,6 +1270,8 @@ function formatChatMessage(data) { if (data.meta.forceShowName) skip = false; + data.msg = execEmotes(data.msg); + LASTCHATNAME = data.username; LASTCHATTIME = data.time; var div = $("
"); @@ -2180,3 +2182,24 @@ function formatUserPlaylistList() { }); }); } + +function loadEmotes(data) { + CHANNEL.emotes = []; + data.forEach(function (e) { + e.regex = new RegExp(e.source, "gi"); + CHANNEL.emotes.push(e); + }); +} + +function execEmotes(msg) { + if (USEROPTS.no_emotes) { + return msg; + } + + CHANNEL.emotes.forEach(function (e) { + msg = msg.replace(e.regex, ''); + }); + + return msg; +} diff --git a/www/css/cytube.css b/www/css/cytube.css index a1a49f41..4aabf57d 100644 --- a/www/css/cytube.css +++ b/www/css/cytube.css @@ -483,3 +483,8 @@ li.ui-sortable-helper, li.ui-sortable-placeholder + li.queue_entry { border-top-left-radius: 0; border-top-right-radius: 0; } + +.channel-emote { + max-width: 200px; + max-height: 200px; +}