Start rewriting channel.js

This commit is contained in:
calzoneman 2013-12-27 11:08:03 -05:00
parent e27667b6d2
commit ba0664641e
6 changed files with 235 additions and 14 deletions

210
lib/channel-new.js Normal file
View File

@ -0,0 +1,210 @@
var DEFAULT_FILTERS = [
new Filter("monospace", "`(.+?)`", "g", "<code>$1</code>"),
new Filter("bold", "\\*(.+?)\\*", "g", "<strong>$1</strong>"),
new Filter("italic", "_(.+?)_", "g", "<em>$1</em>"),
new Filter("strike", "~~(.+?)~~", "g", "<s>$1</s>"),
new Filter("inline spoiler", "\\[sp\\](.*?)\\[\\/sp\\]", "ig", "<span class=\"spoiler\">$1</span>")
];
function Channel(name) {
var self = this; // Alias `this` to prevent scoping issues
Logger.syslog.log("Loading channel " + name);
// Defaults
self.ready = false;
self.name = name;
self.uniqueName = name.toLowerCase(); // To prevent casing issues
self.registered = false; // set to true if the channel exists in the database
self.users = [];
self.mutedUsers = new $util.Set();
self.playlist = new Playlist(self);
self.plqueue = new AsyncQueue(); // For synchronizing playlist actions
self.drinks = 0;
self.leader = null;
self.chatbuffer = [];
self.playlistLock = true;
self.poll = null;
self.voteskip = null;
self.permissions = {
playlistadd: 1.5, // Add video to the playlist
playlistnext: 1.5, // TODO I don't think this is used
playlistmove: 1.5, // Move a video on the playlist
playlistdelete: 2, // Delete a video from the playlist
playlistjump: 1.5, // Start a different video on the playlist
playlistaddlist: 1.5, // Add a list of videos to the playlist
oplaylistadd: -1, // Same as above, but for open (unlocked) playlist
oplaylistnext: 1.5,
oplaylistmove: 1.5,
oplaylistdelete: 2,
oplaylistjump: 1.5,
oplaylistaddlist: 1.5,
playlistaddcustom: 3, // Add custom embed to the playlist
playlistaddlive: 1.5, // Add a livestream to the playlist
exceedmaxlength: 2, // Add a video longer than the maximum length set
addnontemp: 2, // Add a permanent video to the playlist
settemp: 2, // Toggle temporary status of a playlist item
playlistgeturl: 1.5, // TODO is this even used?
playlistshuffle: 2, // Shuffle the playlist
playlistclear: 2, // Clear the playlist
pollctl: 1.5, // Open/close polls
pollvote: -1, // Vote in polls
viewhiddenpoll: 1.5, // View results of hidden polls
voteskip: -1, // Vote to skip the current video
mute: 1.5, // Mute other users
kick: 1.5, // Kick other users
ban: 2, // Ban other users
motdedit: 3, // Edit the MOTD
filteredit: 3, // Control chat filters
drink: 1.5, // Use the /d command
chat: 0 // Send chat messages
};
self.opts = {
allow_voteskip: true, // Allow users to voteskip
voteskip_ratio: 0.5, // Ratio of skip votes:non-afk users needed to skip the video
afk_timeout: 600, // Number of seconds before a user is automatically marked afk
pagetitle: self.name, // Title of the browser tab
maxlength: 0, // Maximum length (in seconds) of a video queued
externalcss: "", // Link to external stylesheet
externaljs: "", // Link to external script
chat_antiflood: false, // Throttle chat messages
chat_antiflood_params: {
burst: 4, // Number of messages to allow with no throttling
sustained: 1, // Throttle rate (messages/second)
cooldown: 4 // Number of seconds with no messages before burst is reset
},
show_public: false, // List the channel on the index page
enable_link_regex: true, // Use the built-in link filter
password: false // Channel password (false -> no password required for entry)
};
self.motd = {
motd: "", // Raw MOTD text
html: "" // Filtered MOTD text (XSS removed; \n replaced by <br>)
};
self.filters = DEFAULT_FILTERS;
self.banlist = new Banlist();
self.logger = new Logger.Logger(path.join(__dirname, "../chanlogs",
self.uniqueName + ".log"));
self.css = ""; // Up to 20KB of inline CSS
self.js = ""; // Up to 20KB of inline Javascript
self.error = false; // Set to true if something bad happens => don't save state
self.on("ready", function () {
self.ready = true;
});
// Load from database
db.channels.load(self, function (err) {
if (err && err !== "Channel is not registered") {
return;
} else {
// Load state from JSON blob
self.tryLoadState();
}
});
};
Channel.prototype = EventEmitter;
Channel.prototype.tryLoadState = function () {
var self = this;
if (self.name === "") {
return;
}
// Don't load state if the channel isn't registered
if (!self.registered) {
self.emit("ready");
return;
}
var file = path.join(__dirname, "../chandump", self.uniqueName);
fs.stat(file, function (err, stats) {
if (!err) {
var mb = stats.size / 1048576;
mb = Math.floor(mb * 100) / 100;
if (mb > 1) {
Logger.errlog.log("Large chandump detected: " + self.uniqueName +
" (" + mb + " MiB)");
self.setMOTD("Your channel file has exceeded the maximum size of 1MB " +
"and cannot be loaded. Please ask an administrator for " +
"assistance in restoring it.");
self.error = true;
self.emit("ready");
return;
}
}
self.loadState();
});
};
/**
* Load the channel state from disk.
*
* SHOULD ONLY BE CALLED FROM tryLoadState
*/
Channel.prototype.loadState = function () {
var self = this;
if (self.error) {
return;
}
fs.readFile(path.join(__dirname, "../chandump", self.name),
function (err, data) {
if (err) {
// File didn't exist => start fresh
if (err.code === "ENOENT") {
self.emit("ready");
self.saveState();
} else {
Logger.errlog.log("Failed to open channel dump " + self.uniqueName);
Logger.errlog.log(err);
self.setMOTD("Internal error when loading channel");
self.error = true;
self.emit("ready");
}
return;
}
try {
self.logger.log("*** Loading channel state");
data = JSON.parse(data);
// Load the playlist
if ("playlist" in data) {
}
// Playlist lock
self.setLock(data.playlistLock || false);
// Configurables
if ("opts" in data) {
for (var key in data.opts) {
self.opts[key] = data.opts;
}
}
// Permissions
if ("permissions" in data) {
for (var key in data.permissions) {
self.permissions[key] = data.permissions[key];
}
}
// Chat filters
if ("filters" in data) {
for (var i = 0; i < data.filters.length; i++) {
var f = data.filters[i];
var filt = new Filter(f.name, f.source, f.flags, f.replace);
filt.active = f.active;
filt.filterlinks = f.filterlinks;
self.updateFilter(filt, false);
}
}
} catch (e) {
}
});
};

View File

@ -1315,20 +1315,25 @@ Channel.prototype.calcVoteskipMax = function () {
}, 0);
};
Channel.prototype.broadcastVoteskipUpdate = function() {
Channel.prototype.getVoteskipPacket = function() {
var amt = this.voteskip ? this.voteskip.counts[0] : 0;
var count = this.calcVoteskipMax();
var need = this.voteskip ? Math.ceil(count * this.opts.voteskip_ratio) : 0;
for(var i = 0; i < this.users.length; i++) {
if(this.users[i].rank >= 1.5) {
this.users[i].socket.emit("voteskip", {
count: amt,
need: need
});
}
}
return {
count: amt,
need: need
};
}
Channel.prototype.broadcastVoteskipUpdate = function () {
var pkt = this.getVoteskipPacket();
this.users.forEach(function (u) {
if (u.rank >= 1.5) {
u.socket.emit("voteskip", pkt);
}
});
};
Channel.prototype.broadcastMotd = function() {
this.sendAll("setMotd", this.motd);
}

View File

@ -32,7 +32,8 @@ var User = function (socket) {
this.name = "";
this.meta = {
afk: false,
icon: false
icon: false,
aliases: []
};
this.queueLimiter = $util.newRateLimiter();
this.chatLimiter = $util.newRateLimiter();

View File

@ -67,7 +67,8 @@ function handleLoginPage(req, res) {
}
}
sendJade(res, 'login', {
loggedIn: false
loggedIn: false,
redirect: req.header('Referrer')
});
}

View File

@ -12,13 +12,15 @@ html(lang="en")
ul.nav.navbar-nav
mixin navdefaultlinks("/login")
if loggedIn
mixin navlogoutform("/login")
if redirect
mixin navlogoutform(redirect)
else
mixin navlogoutform("/login")
section#mainpage.container
if wasAlreadyLoggedIn
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
.alert.alert-info.messagebox.center
h3(style="margin: 5px auto") Logged in as #{loginName}
// TODO Link to My Account page
else if !loggedIn
.col-lg-6.col-lg-offset-3.col-md-6.col-md-offset-3
if loginError
@ -27,6 +29,8 @@ html(lang="en")
p= loginError
h2 Login
form(role="form", action="/login", method="post")
if redirect
input(type="hidden", name="redirect", value=redirect)
.form-group
label(for="username") Username
input#username.form-control(type="text", name="name")

View File

@ -55,7 +55,7 @@ mixin navloginform(redirect)
mixin navlogoutform(redirect)
if redirect
- url = "/logout?redirect=#{redirect}"
- url = "/logout?redirect=" + redirect
else
- url = "/logout"
p#logoutform.navbar-text.pull-right