2013-04-03 17:47:41 +00:00
|
|
|
|
2013-03-24 02:28:20 +00:00
|
|
|
/*
|
|
|
|
The MIT License (MIT)
|
|
|
|
Copyright (c) 2013 Calvin Montgomery
|
2013-04-03 17:47:41 +00:00
|
|
|
|
2013-03-24 02:28:20 +00:00
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
2013-04-03 17:47:41 +00:00
|
|
|
|
2013-03-24 02:28:20 +00:00
|
|
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
2013-04-03 17:47:41 +00:00
|
|
|
|
2013-03-24 02:28:20 +00:00
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
2013-02-16 17:19:59 +00:00
|
|
|
|
2013-03-28 23:51:08 +00:00
|
|
|
var fs = require("fs");
|
2013-04-03 17:47:41 +00:00
|
|
|
var Database = require("./database.js");
|
|
|
|
var Poll = require("./poll.js").Poll;
|
2013-03-24 17:23:48 +00:00
|
|
|
var Media = require("./media.js").Media;
|
2013-04-24 19:28:20 +00:00
|
|
|
var formatTime = require("./media.js").formatTime;
|
2013-03-27 19:28:51 +00:00
|
|
|
var Logger = require("./logger.js");
|
2013-04-03 17:47:41 +00:00
|
|
|
var InfoGetter = require("./get-info.js");
|
|
|
|
var io = require("./server.js").io;
|
|
|
|
var Rank = require("./rank.js");
|
2013-04-22 18:28:40 +00:00
|
|
|
var Auth = require("./auth.js");
|
2013-04-04 19:56:43 +00:00
|
|
|
var ChatCommand = require("./chatcommand.js");
|
2013-02-16 05:02:42 +00:00
|
|
|
|
|
|
|
var Channel = function(name) {
|
2013-03-27 19:28:51 +00:00
|
|
|
Logger.syslog.log("Opening channel " + name);
|
2013-04-03 17:47:41 +00:00
|
|
|
|
2013-02-16 05:02:42 +00:00
|
|
|
this.name = name;
|
2013-04-03 17:47:41 +00:00
|
|
|
// Initialize defaults
|
2013-02-16 05:02:42 +00:00
|
|
|
this.registered = false;
|
|
|
|
this.users = [];
|
|
|
|
this.queue = [];
|
|
|
|
this.library = {};
|
2013-04-03 17:47:41 +00:00
|
|
|
this.position = -1;
|
|
|
|
this.media = null;
|
2013-04-04 19:56:43 +00:00
|
|
|
this.drinks = 0;
|
2013-02-16 05:02:42 +00:00
|
|
|
this.leader = null;
|
2013-04-03 17:47:41 +00:00
|
|
|
this.chatbuffer = [];
|
|
|
|
this.openqueue = false;
|
2013-03-16 21:49:58 +00:00
|
|
|
this.poll = false;
|
2013-04-02 19:07:22 +00:00
|
|
|
this.voteskip = false;
|
2013-03-22 20:04:04 +00:00
|
|
|
this.opts = {
|
|
|
|
qopen_allow_qnext: false,
|
|
|
|
qopen_allow_move: false,
|
|
|
|
qopen_allow_playnext: false,
|
|
|
|
qopen_allow_delete: false,
|
2013-04-02 19:07:22 +00:00
|
|
|
allow_voteskip: true,
|
2013-04-21 01:17:38 +00:00
|
|
|
voteskip_ratio: 0.5,
|
2013-04-03 17:47:41 +00:00
|
|
|
pagetitle: this.name,
|
2013-04-16 16:11:10 +00:00
|
|
|
customcss: "",
|
|
|
|
customjs: ""
|
2013-03-22 20:04:04 +00:00
|
|
|
};
|
2013-03-29 18:15:46 +00:00
|
|
|
this.filters = [
|
2013-03-31 19:27:54 +00:00
|
|
|
[/`([^`]+)`/g , "<code>$1</code>" , true],
|
|
|
|
[/\*([^\*]+)\*/g , "<strong>$1</strong>", true],
|
|
|
|
[/(^| )_([^_]+)_/g , "$1<em>$2</em>" , true],
|
|
|
|
[/\\\\([-a-zA-Z0-9]+)/g, "[](/$1)" , true]
|
2013-03-29 18:15:46 +00:00
|
|
|
];
|
2013-04-01 21:02:09 +00:00
|
|
|
this.motd = {
|
|
|
|
motd: "",
|
|
|
|
html: ""
|
|
|
|
};
|
2013-03-27 19:28:51 +00:00
|
|
|
this.ipbans = {};
|
|
|
|
this.logger = new Logger.Logger("chanlogs/" + this.name + ".log");
|
2013-04-03 17:47:41 +00:00
|
|
|
this.i = 0;
|
|
|
|
this.time = new Date().getTime();
|
2013-04-24 19:28:20 +00:00
|
|
|
this.plmeta = {
|
|
|
|
count: 0,
|
|
|
|
time: "00:00"
|
|
|
|
};
|
2013-04-03 17:47:41 +00:00
|
|
|
|
|
|
|
Database.loadChannel(this);
|
|
|
|
if(this.registered) {
|
|
|
|
this.loadDump();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* REGION Channel data */
|
|
|
|
Channel.prototype.loadDump = function() {
|
2013-03-28 23:51:08 +00:00
|
|
|
fs.readFile("chandump/" + this.name, function(err, data) {
|
|
|
|
if(err) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
this.logger.log("*** Loading channel dump from disk");
|
|
|
|
data = JSON.parse(data);
|
|
|
|
for(var i = 0; i < data.queue.length; i++) {
|
|
|
|
var e = data.queue[i];
|
2013-04-24 18:10:08 +00:00
|
|
|
var m = new Media(e.id, e.title, e.seconds, e.type);
|
|
|
|
m.queueby = data.queue[i].queueby ? data.queue[i].queueby
|
|
|
|
: "";
|
|
|
|
this.queue.push(m);
|
2013-03-28 23:51:08 +00:00
|
|
|
}
|
|
|
|
this.sendAll("playlist", {
|
|
|
|
pl: this.queue
|
|
|
|
});
|
2013-04-24 19:28:20 +00:00
|
|
|
this.broadcastPlaylistMeta();
|
2013-04-03 17:47:41 +00:00
|
|
|
// Backwards compatibility
|
|
|
|
if(data.currentPosition != undefined) {
|
|
|
|
this.position = data.currentPosition - 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
this.position = data.position - 1;
|
|
|
|
}
|
|
|
|
if(this.position < -1)
|
|
|
|
this.position = -1;
|
2013-04-02 15:55:14 +00:00
|
|
|
if(this.queue.length > 0)
|
|
|
|
this.playNext();
|
2013-04-06 20:47:27 +00:00
|
|
|
if(this.media && data.currentTime) {
|
|
|
|
this.media.currentTime = data.currentTime;
|
|
|
|
}
|
2013-04-16 16:11:10 +00:00
|
|
|
for(var key in data.opts) {
|
|
|
|
this.opts[key] = data.opts[key];
|
|
|
|
}
|
2013-03-31 19:27:54 +00:00
|
|
|
if(data.filters) {
|
|
|
|
this.filters = new Array(data.filters.length);
|
|
|
|
for(var i = 0; i < data.filters.length; i++) {
|
2013-04-06 02:37:22 +00:00
|
|
|
this.filters[i] = [new RegExp(data.filters[i][0], "g"),
|
2013-03-31 19:27:54 +00:00
|
|
|
data.filters[i][1],
|
|
|
|
data.filters[i][2]];
|
|
|
|
}
|
|
|
|
}
|
2013-04-01 21:02:09 +00:00
|
|
|
if(data.motd) {
|
|
|
|
this.motd = data.motd;
|
|
|
|
}
|
2013-03-28 23:51:08 +00:00
|
|
|
}
|
|
|
|
catch(e) {
|
|
|
|
Logger.errlog.log("Channel dump load failed: ");
|
|
|
|
Logger.errlog.log(e);
|
|
|
|
}
|
|
|
|
}.bind(this));
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.saveDump = function() {
|
|
|
|
var filts = new Array(this.filters.length);
|
|
|
|
for(var i = 0; i < this.filters.length; i++) {
|
|
|
|
filts[i] = [this.filters[i][0].source,
|
|
|
|
this.filters[i][1],
|
|
|
|
this.filters[i][2]];
|
|
|
|
}
|
|
|
|
var dump = {
|
|
|
|
position: this.position,
|
2013-04-06 20:47:27 +00:00
|
|
|
currentTime: this.media ? this.media.currentTime : 0,
|
2013-04-03 17:47:41 +00:00
|
|
|
queue: this.queue,
|
|
|
|
opts: this.opts,
|
|
|
|
filters: filts,
|
|
|
|
motd: this.motd
|
|
|
|
};
|
|
|
|
var text = JSON.stringify(dump);
|
|
|
|
fs.writeFileSync("chandump/" + this.name, text);
|
|
|
|
this.logger.flush();
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
|
2013-03-17 17:14:34 +00:00
|
|
|
Channel.prototype.tryRegister = function(user) {
|
|
|
|
if(this.registered) {
|
2013-03-24 17:23:48 +00:00
|
|
|
user.socket.emit("registerChannel", {
|
2013-03-17 17:14:34 +00:00
|
|
|
success: false,
|
|
|
|
error: "This channel is already registered"
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else if(!user.loggedIn) {
|
2013-03-24 17:23:48 +00:00
|
|
|
user.socket.emit("registerChannel", {
|
2013-03-17 17:14:34 +00:00
|
|
|
success: false,
|
|
|
|
error: "You must log in to register a channel"
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
else if(!Rank.hasPermission(user, "registerChannel")) {
|
2013-03-24 17:23:48 +00:00
|
|
|
user.socket.emit("registerChannel", {
|
2013-03-17 17:14:34 +00:00
|
|
|
success: false,
|
|
|
|
error: "You don't have permission to register this channel"
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else {
|
2013-04-03 17:47:41 +00:00
|
|
|
if(Database.registerChannel(this)) {
|
2013-03-17 17:14:34 +00:00
|
|
|
this.registered = true;
|
|
|
|
this.saveRank(user);
|
2013-03-24 17:23:48 +00:00
|
|
|
user.socket.emit("registerChannel", {
|
2013-03-17 17:14:34 +00:00
|
|
|
success: true,
|
|
|
|
});
|
2013-03-27 19:28:51 +00:00
|
|
|
this.logger.log("*** " + user.name + " registered the channel");
|
|
|
|
Logger.syslog.log("Channel " + this.name + " was registered by " + user.name);
|
2013-03-17 17:14:34 +00:00
|
|
|
}
|
|
|
|
else {
|
2013-03-24 17:23:48 +00:00
|
|
|
user.socket.emit("registerChannel", {
|
2013-03-17 17:14:34 +00:00
|
|
|
success: false,
|
|
|
|
error: "Unable to register channel, see an admin"
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-04 20:41:41 +00:00
|
|
|
Channel.prototype.getRank = function(name) {
|
2013-04-22 18:28:40 +00:00
|
|
|
var global = Auth.getGlobalRank(name);
|
2013-04-03 17:47:41 +00:00
|
|
|
if(!this.registered) {
|
2013-04-22 18:28:40 +00:00
|
|
|
return global;
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
2013-04-22 18:28:40 +00:00
|
|
|
var local = Database.lookupChannelRank(this.name, name);
|
|
|
|
return local > global ? local : global;
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.saveRank = function(user) {
|
2013-04-03 17:47:41 +00:00
|
|
|
return Database.saveChannelRank(this.name, user);
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.cacheMedia = function(media) {
|
2013-02-16 05:02:42 +00:00
|
|
|
this.library[media.id] = media;
|
2013-04-03 17:47:41 +00:00
|
|
|
if(this.registered) {
|
|
|
|
return Database.cacheMedia(this.name, media);
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
2013-04-03 17:47:41 +00:00
|
|
|
return false;
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.banIP = function(actor, receiver) {
|
|
|
|
if(!Rank.hasPermission(actor, "ipban"))
|
2013-03-24 03:01:37 +00:00
|
|
|
return false;
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
this.ipbans[receiver.ip] = [receiver.name, actor.name];
|
2013-04-11 16:30:20 +00:00
|
|
|
receiver.socket.disconnect(true);
|
2013-04-03 17:47:41 +00:00
|
|
|
this.broadcastBanlist();
|
2013-04-11 00:51:30 +00:00
|
|
|
this.logger.log(receiver.ip + " (" + receiver.name + ") was banned by " + actor.name);
|
2013-03-24 03:45:10 +00:00
|
|
|
|
|
|
|
if(!this.registered)
|
|
|
|
return false;
|
2013-04-03 17:47:41 +00:00
|
|
|
|
|
|
|
// Update database ban table
|
|
|
|
return Database.addChannelBan(this.name, actor, receiver);
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.unbanIP = function(actor, ip) {
|
|
|
|
if(!Rank.hasPermission(actor, "ipban"))
|
2013-03-24 03:45:10 +00:00
|
|
|
return false;
|
|
|
|
|
2013-04-08 00:31:52 +00:00
|
|
|
this.ipbans[ip] = null;
|
2013-03-24 03:45:10 +00:00
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
if(!this.registered)
|
|
|
|
return false;
|
|
|
|
|
2013-04-11 00:51:30 +00:00
|
|
|
this.broadcastBanlist();
|
2013-04-03 17:47:41 +00:00
|
|
|
// Update database ban table
|
|
|
|
return Database.removeChannelBan(this.name, ip);
|
2013-03-24 03:45:10 +00:00
|
|
|
}
|
|
|
|
|
2013-04-04 16:39:43 +00:00
|
|
|
Channel.prototype.search = function(query, callback) {
|
|
|
|
// Search youtube
|
|
|
|
if(callback) {
|
|
|
|
if(query.trim() == "") {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
InfoGetter.getYTSearchResults(query, function(vids) {
|
|
|
|
callback(vids);
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-02-16 05:02:42 +00:00
|
|
|
query = query.toLowerCase();
|
|
|
|
var results = [];
|
|
|
|
for(var id in this.library) {
|
|
|
|
if(this.library[id].title.toLowerCase().indexOf(query) != -1) {
|
|
|
|
results.push(this.library[id]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
results.sort(function(a, b) {
|
|
|
|
var x = a.title.toLowerCase();
|
|
|
|
var y = b.title.toLowerCase();
|
|
|
|
|
|
|
|
return (x == y) ? 0 : (x < y ? -1 : 1);
|
|
|
|
});
|
2013-04-04 16:39:43 +00:00
|
|
|
|
2013-02-16 05:02:42 +00:00
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
/* REGION User interaction */
|
|
|
|
|
2013-02-16 05:02:42 +00:00
|
|
|
Channel.prototype.userJoin = function(user) {
|
2013-04-03 17:47:41 +00:00
|
|
|
// GTFO
|
2013-04-08 00:31:52 +00:00
|
|
|
if(user.ip in this.ipbans && this.ipbans[user.ip] != null) {
|
2013-04-03 17:47:41 +00:00
|
|
|
this.logger.log("--- Kicking " + user.ip + " - banned");
|
2013-04-14 17:38:00 +00:00
|
|
|
this.kick(user, "You're banned!");
|
2013-04-11 16:30:20 +00:00
|
|
|
user.socket.disconnect(true);
|
2013-03-24 03:01:37 +00:00
|
|
|
return;
|
2013-03-24 01:08:35 +00:00
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
// Join the socket pool for this channel
|
2013-03-20 18:03:32 +00:00
|
|
|
user.socket.join(this.name);
|
2013-04-03 17:47:41 +00:00
|
|
|
|
2013-03-17 14:39:22 +00:00
|
|
|
// Prevent duplicate login
|
|
|
|
if(user.name != "") {
|
|
|
|
for(var i = 0; i < this.users.length; i++) {
|
|
|
|
if(this.users[i].name == user.name) {
|
|
|
|
user.name = "";
|
|
|
|
user.loggedIn = false;
|
2013-03-24 17:23:48 +00:00
|
|
|
user.socket.emit("login", {
|
2013-03-17 14:39:22 +00:00
|
|
|
success: false,
|
|
|
|
error: "The username " + user.name + " is already in use on this channel"
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-04-03 17:47:41 +00:00
|
|
|
|
|
|
|
// If the channel is empty and isn't registered, the first person
|
2013-02-16 05:02:42 +00:00
|
|
|
// gets ownership of the channel (temporarily)
|
|
|
|
if(this.users.length == 0 && !this.registered) {
|
2013-03-17 17:14:34 +00:00
|
|
|
user.rank = (user.rank < Rank.Owner) ? Rank.Owner + 7 : user.rank;
|
2013-03-24 17:23:48 +00:00
|
|
|
user.socket.emit("channelNotRegistered");
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
this.users.push(user);
|
2013-04-21 01:17:38 +00:00
|
|
|
this.broadcastVoteskipUpdate();
|
2013-02-16 05:02:42 +00:00
|
|
|
if(user.name != "") {
|
|
|
|
this.broadcastNewUser(user);
|
|
|
|
}
|
2013-04-03 17:47:41 +00:00
|
|
|
this.broadcastUsercount();
|
|
|
|
|
2013-02-16 05:02:42 +00:00
|
|
|
// Set the new guy up
|
|
|
|
this.sendPlaylist(user);
|
2013-04-03 20:18:35 +00:00
|
|
|
this.sendMediaUpdate(user);
|
2013-04-03 22:56:03 +00:00
|
|
|
user.socket.emit("queueLock", {locked: !this.openqueue});
|
2013-02-16 05:02:42 +00:00
|
|
|
this.sendUserlist(user);
|
|
|
|
this.sendRecentChat(user);
|
2013-03-16 21:49:58 +00:00
|
|
|
if(this.poll) {
|
2013-03-24 17:23:48 +00:00
|
|
|
user.socket.emit("newPoll", this.poll.packUpdate());
|
2013-03-16 21:49:58 +00:00
|
|
|
}
|
2013-03-24 17:23:48 +00:00
|
|
|
user.socket.emit("channelOpts", this.opts);
|
2013-04-01 21:02:09 +00:00
|
|
|
user.socket.emit("updateMotd", this.motd);
|
2013-04-04 19:56:43 +00:00
|
|
|
user.socket.emit("drinkCount", {count: this.drinks});
|
2013-04-03 17:47:41 +00:00
|
|
|
|
|
|
|
// Send things that require special permission
|
|
|
|
this.sendRankStuff(user);
|
|
|
|
|
2013-03-27 19:28:51 +00:00
|
|
|
this.logger.log("+++ /" + user.ip + " joined");
|
|
|
|
Logger.syslog.log("/" + user.ip + " joined channel " + this.name);
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.userLeave = function(user) {
|
2013-04-03 17:47:41 +00:00
|
|
|
// Their socket might already be dead, so wrap in a try-catch
|
2013-03-20 18:03:32 +00:00
|
|
|
try {
|
|
|
|
user.socket.leave(this.name);
|
|
|
|
}
|
|
|
|
catch(e) {}
|
2013-04-03 17:47:41 +00:00
|
|
|
|
|
|
|
// Undo vote for people who leave
|
2013-03-16 21:49:58 +00:00
|
|
|
if(this.poll) {
|
|
|
|
this.poll.unvote(user.ip);
|
|
|
|
this.broadcastPollUpdate();
|
|
|
|
}
|
2013-04-03 17:47:41 +00:00
|
|
|
if(this.voteskip) {
|
|
|
|
this.voteskip.unvote(user.ip);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If they were leading, return control to the server
|
2013-03-16 21:49:58 +00:00
|
|
|
if(this.leader == user) {
|
|
|
|
this.changeLeader("");
|
|
|
|
}
|
2013-04-03 17:47:41 +00:00
|
|
|
|
|
|
|
// Remove the user from the client list for this channel
|
2013-03-24 02:05:13 +00:00
|
|
|
var idx = this.users.indexOf(user);
|
|
|
|
if(idx >= 0 && idx < this.users.length)
|
|
|
|
this.users.splice(idx, 1);
|
2013-04-21 01:17:38 +00:00
|
|
|
this.broadcastVoteskipUpdate();
|
2013-04-03 17:47:41 +00:00
|
|
|
this.broadcastUsercount();
|
2013-02-16 05:02:42 +00:00
|
|
|
if(user.name != "") {
|
2013-03-24 17:23:48 +00:00
|
|
|
this.sendAll("userLeave", {
|
2013-02-16 05:02:42 +00:00
|
|
|
name: user.name
|
|
|
|
});
|
|
|
|
}
|
2013-03-27 19:28:51 +00:00
|
|
|
this.logger.log("--- /" + user.ip + " (" + user.name + ") left");
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
|
2013-04-14 17:38:00 +00:00
|
|
|
Channel.prototype.kick = function(user, reason) {
|
|
|
|
user.socket.emit("kick", {
|
|
|
|
reason: reason
|
|
|
|
});
|
|
|
|
if(user.socket.disconnected) {
|
|
|
|
this.userLeave(user);
|
|
|
|
}
|
|
|
|
user.socket.disconnect(true);
|
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.sendRankStuff = function(user) {
|
|
|
|
if(Rank.hasPermission(user, "ipban")) {
|
|
|
|
var ents = [];
|
|
|
|
for(var ip in this.ipbans) {
|
|
|
|
if(this.ipbans[ip] != null) {
|
|
|
|
ents.push({
|
|
|
|
ip: ip,
|
|
|
|
name: this.ipbans[ip][0],
|
|
|
|
banner: this.ipbans[ip][1]
|
2013-03-21 03:29:26 +00:00
|
|
|
});
|
2013-03-25 19:39:03 +00:00
|
|
|
}
|
2013-04-03 17:47:41 +00:00
|
|
|
}
|
|
|
|
user.socket.emit("banlist", {entries: ents});
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
2013-04-03 17:47:41 +00:00
|
|
|
if(Rank.hasPermission(user, "chatFilter")) {
|
|
|
|
var filts = new Array(this.filters.length);
|
|
|
|
for(var i = 0; i < this.filters.length; i++) {
|
|
|
|
filts[i] = [this.filters[i][0].source, this.filters[i][1], this.filters[i][2]];
|
|
|
|
}
|
|
|
|
user.socket.emit("chatFilters", {filters: filts});
|
2013-03-16 22:17:36 +00:00
|
|
|
}
|
2013-04-23 18:47:09 +00:00
|
|
|
this.sendACL(user);
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.sendACL = function(user) {
|
2013-04-22 18:28:40 +00:00
|
|
|
if(Rank.hasPermission(user, "acl")) {
|
|
|
|
user.socket.emit("acl", Database.getChannelRanks(this.name));
|
|
|
|
}
|
2013-04-03 17:47:41 +00:00
|
|
|
}
|
2013-02-16 05:02:42 +00:00
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.sendPlaylist = function(user) {
|
|
|
|
user.socket.emit("playlist", {
|
|
|
|
pl: this.queue
|
|
|
|
});
|
|
|
|
user.socket.emit("updatePlaylistIdx", {
|
2013-04-03 22:56:03 +00:00
|
|
|
idx: this.position
|
2013-04-03 17:47:41 +00:00
|
|
|
});
|
2013-04-24 19:28:20 +00:00
|
|
|
user.socket.emit("updatePlaylistMeta", this.plmeta);
|
2013-04-03 17:47:41 +00:00
|
|
|
}
|
2013-02-16 05:02:42 +00:00
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.sendMediaUpdate = function(user) {
|
|
|
|
if(this.media != null) {
|
|
|
|
user.socket.emit("mediaUpdate", this.media.packupdate());
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
2013-04-03 17:47:41 +00:00
|
|
|
}
|
2013-02-16 05:02:42 +00:00
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.sendUserlist = function(user) {
|
|
|
|
var users = [];
|
|
|
|
for(var i = 0; i < this.users.length; i++) {
|
|
|
|
// Skip people who haven't logged in
|
|
|
|
if(this.users[i].name != "") {
|
|
|
|
users.push({
|
|
|
|
name: this.users[i].name,
|
|
|
|
rank: this.users[i].rank,
|
2013-04-21 01:17:38 +00:00
|
|
|
leader: this.users[i] == this.leader,
|
|
|
|
meta: this.users[i].meta
|
2013-04-03 17:47:41 +00:00
|
|
|
});
|
|
|
|
}
|
2013-03-23 18:17:39 +00:00
|
|
|
}
|
2013-04-03 17:47:41 +00:00
|
|
|
user.socket.emit("userlist", users);
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
// Send the last 15 messages for context
|
|
|
|
Channel.prototype.sendRecentChat = function(user) {
|
|
|
|
for(var i = 0; i < this.chatbuffer.length; i++) {
|
|
|
|
user.socket.emit("chatMsg", this.chatbuffer[i]);
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
/* REGION Broadcasts to all clients */
|
2013-02-16 05:02:42 +00:00
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.sendAll = function(message, data) {
|
|
|
|
io.sockets.in(this.name).emit(message, data);
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
|
2013-04-24 19:28:20 +00:00
|
|
|
Channel.prototype.broadcastPlaylistMeta = function() {
|
|
|
|
var total = 0;
|
|
|
|
for(var i = 0; i < this.queue.length; i++) {
|
|
|
|
total += this.queue[i].seconds;
|
|
|
|
}
|
|
|
|
var timestr = formatTime(total);
|
|
|
|
var packet = {
|
|
|
|
count: this.queue.length,
|
|
|
|
time: timestr
|
|
|
|
};
|
|
|
|
this.plmeta = packet;
|
|
|
|
this.sendAll("updatePlaylistMeta", packet);
|
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.broadcastUsercount = function() {
|
|
|
|
this.sendAll("usercount", {
|
|
|
|
count: this.users.length
|
|
|
|
});
|
|
|
|
}
|
2013-03-28 23:51:08 +00:00
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.broadcastNewUser = function(user) {
|
|
|
|
this.sendAll("addUser", {
|
|
|
|
name: user.name,
|
|
|
|
rank: user.rank,
|
2013-04-19 19:50:08 +00:00
|
|
|
leader: this.leader == user,
|
|
|
|
meta: user.meta
|
2013-03-28 23:51:08 +00:00
|
|
|
});
|
2013-04-03 17:47:41 +00:00
|
|
|
this.sendRankStuff(user);
|
2013-04-22 18:28:40 +00:00
|
|
|
if(user.rank > Rank.Guest) {
|
|
|
|
this.saveRank(user);
|
|
|
|
}
|
2013-03-28 23:51:08 +00:00
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.broadcastRankUpdate = function(user) {
|
|
|
|
this.sendAll("updateUser", {
|
|
|
|
name: user.name,
|
|
|
|
rank: user.rank,
|
2013-04-19 19:50:08 +00:00
|
|
|
leader: this.leader == user,
|
|
|
|
meta: user.meta
|
2013-04-03 17:47:41 +00:00
|
|
|
});
|
|
|
|
this.sendRankStuff(user);
|
2013-03-16 20:39:58 +00:00
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.broadcastPoll = function() {
|
|
|
|
this.sendAll("newPoll", this.poll.packUpdate());
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.broadcastPollUpdate = function() {
|
|
|
|
this.sendAll("updatePoll", this.poll.packUpdate());
|
|
|
|
}
|
2013-02-16 05:02:42 +00:00
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.broadcastPollClose = function() {
|
|
|
|
this.sendAll("closePoll");
|
|
|
|
}
|
2013-02-16 05:02:42 +00:00
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.broadcastOpts = function() {
|
|
|
|
this.sendAll("channelOpts", this.opts);
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.broadcastBanlist = function() {
|
|
|
|
var ents = [];
|
|
|
|
for(var ip in this.ipbans) {
|
|
|
|
if(this.ipbans[ip] != null) {
|
|
|
|
ents.push({
|
|
|
|
ip: ip,
|
|
|
|
name: this.ipbans[ip][0],
|
|
|
|
banner: this.ipbans[ip][1]
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for(var i = 0; i < this.users.length; i++) {
|
|
|
|
if(Rank.hasPermission(this.users[i], "ipban")) {
|
|
|
|
this.users[i].socket.emit("banlist", {entries: ents});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-22 18:28:40 +00:00
|
|
|
Channel.prototype.broadcastRankTable = function() {
|
|
|
|
var ranks = Database.getChannelRanks(this.name);
|
|
|
|
for(var i = 0; i < this.users.length; i++) {
|
2013-04-23 18:47:09 +00:00
|
|
|
this.sendACL(this.users[i]);
|
2013-04-22 18:28:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.broadcastChatFilters = function() {
|
|
|
|
var filts = new Array(this.filters.length);
|
|
|
|
for(var i = 0; i < this.filters.length; i++) {
|
|
|
|
filts[i] = [this.filters[i][0].source, this.filters[i][1], this.filters[i][2]];
|
|
|
|
}
|
|
|
|
for(var i = 0; i < this.users.length; i++) {
|
|
|
|
if(Rank.hasPermission(this.users[i], "chatFilter")) {
|
|
|
|
this.users[i].socket.emit("chatFilters", {filters: filts});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-21 01:17:38 +00:00
|
|
|
Channel.prototype.broadcastVoteskipUpdate = function() {
|
|
|
|
var amt = this.voteskip ? this.voteskip.counts[0] : 0;
|
2013-04-23 15:23:32 +00:00
|
|
|
var need = this.voteskip ? parseInt(this.users.length * this.opts.voteskip_ratio) : 0;
|
2013-04-21 01:17:38 +00:00
|
|
|
for(var i = 0; i < this.users.length; i++) {
|
|
|
|
if(Rank.hasPermission(this.users[i], "seeVoteskip") ||
|
|
|
|
this.leader == this.users[i]) {
|
|
|
|
this.users[i].socket.emit("voteskip", {
|
|
|
|
count: amt,
|
|
|
|
need: need
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.broadcastMotd = function() {
|
|
|
|
this.sendAll("updateMotd", this.motd);
|
|
|
|
}
|
|
|
|
|
2013-04-04 19:56:43 +00:00
|
|
|
Channel.prototype.broadcastDrinks = function() {
|
|
|
|
this.sendAll("drinkCount", {count: this.drinks});
|
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
/* REGION Playlist Stuff */
|
|
|
|
|
|
|
|
// The server autolead function
|
|
|
|
function mediaUpdate(chan, id) {
|
|
|
|
// Bail cases - video changed, someone's leader, no video playing
|
2013-04-03 20:18:35 +00:00
|
|
|
if(chan.media == null ||
|
|
|
|
id != chan.media.id ||
|
2013-04-03 17:47:41 +00:00
|
|
|
chan.leader != null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-04-03 20:18:35 +00:00
|
|
|
chan.media.currentTime += (new Date().getTime() - chan.time) / 1000.0;
|
2013-04-03 17:47:41 +00:00
|
|
|
chan.time = new Date().getTime();
|
|
|
|
|
|
|
|
// Show's over, move on to the next thing
|
2013-04-03 20:18:35 +00:00
|
|
|
if(chan.media.currentTime > chan.media.seconds) {
|
2013-04-03 17:47:41 +00:00
|
|
|
chan.playNext();
|
|
|
|
}
|
|
|
|
// Send updates about every 5 seconds
|
|
|
|
else if(chan.i % 5 == 0) {
|
2013-04-03 20:18:35 +00:00
|
|
|
chan.sendAll("mediaUpdate", chan.media.packupdate());
|
2013-04-03 17:47:41 +00:00
|
|
|
}
|
|
|
|
chan.i++;
|
|
|
|
|
2013-04-03 20:18:35 +00:00
|
|
|
setTimeout(function() { mediaUpdate(chan, id); }, 1000);
|
2013-04-03 17:47:41 +00:00
|
|
|
}
|
|
|
|
|
2013-04-24 18:10:08 +00:00
|
|
|
Channel.prototype.enqueue = function(data, user) {
|
2013-04-03 17:47:41 +00:00
|
|
|
var idx = data.pos == "next" ? this.position + 1 : this.queue.length;
|
|
|
|
|
|
|
|
// Prefer cache over looking up new data
|
|
|
|
if(data.id in this.library) {
|
2013-04-24 18:10:08 +00:00
|
|
|
var media = this.library[data.id];
|
|
|
|
media.queueby = user ? user.name : "";
|
|
|
|
this.queue.splice(idx, 0, media);
|
2013-04-03 17:47:41 +00:00
|
|
|
this.sendAll("queue", {
|
2013-04-24 18:10:08 +00:00
|
|
|
media: media.pack(),
|
2013-04-03 17:47:41 +00:00
|
|
|
pos: idx
|
|
|
|
});
|
2013-04-24 19:28:20 +00:00
|
|
|
this.broadcastPlaylistMeta();
|
2013-04-03 17:47:41 +00:00
|
|
|
this.logger.log("*** Queued from cache: id=" + data.id);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
switch(data.type) {
|
|
|
|
case "yt":
|
2013-04-03 18:18:10 +00:00
|
|
|
case "yp":
|
2013-04-03 17:47:41 +00:00
|
|
|
case "vi":
|
|
|
|
case "dm":
|
|
|
|
case "sc":
|
|
|
|
InfoGetter.getMedia(data.id, data.type, function(media) {
|
2013-04-24 18:10:08 +00:00
|
|
|
media.queueby = user ? user.name : "";
|
2013-04-03 17:47:41 +00:00
|
|
|
this.queue.splice(idx, 0, media);
|
|
|
|
this.sendAll("queue", {
|
|
|
|
media: media.pack(),
|
|
|
|
pos: idx
|
|
|
|
});
|
2013-04-24 19:28:20 +00:00
|
|
|
this.broadcastPlaylistMeta();
|
2013-04-03 17:47:41 +00:00
|
|
|
this.cacheMedia(media);
|
2013-04-03 18:18:10 +00:00
|
|
|
if(data.type == "yp")
|
|
|
|
idx++;
|
2013-04-03 17:47:41 +00:00
|
|
|
}.bind(this));
|
|
|
|
break;
|
|
|
|
case "li":
|
2013-04-24 18:10:08 +00:00
|
|
|
var media = new Media(data.id, "Livestream - " + data.id, "--:--", "li");
|
|
|
|
media.queueby = user ? user.name : "";
|
2013-04-03 17:47:41 +00:00
|
|
|
this.queue.splice(idx, 0, media);
|
|
|
|
this.sendAll("queue", {
|
|
|
|
media: media.pack(),
|
|
|
|
pos: idx
|
|
|
|
});
|
2013-04-24 19:28:20 +00:00
|
|
|
this.broadcastPlaylistMeta();
|
2013-04-03 17:47:41 +00:00
|
|
|
break;
|
|
|
|
case "tw":
|
2013-04-24 18:10:08 +00:00
|
|
|
var media = new Media(data.id, "Twitch - " + data.id, "--:--", "tw");
|
|
|
|
media.queueby = user ? user.name : "";
|
2013-04-03 17:47:41 +00:00
|
|
|
this.queue.splice(idx, 0, media);
|
|
|
|
this.sendAll("queue", {
|
|
|
|
media: media.pack(),
|
|
|
|
pos: idx
|
|
|
|
});
|
2013-04-24 19:28:20 +00:00
|
|
|
this.broadcastPlaylistMeta();
|
2013-04-03 17:47:41 +00:00
|
|
|
break;
|
2013-04-07 19:38:50 +00:00
|
|
|
case "rt":
|
|
|
|
var media = new Media(data.id, "Livestream", "--:--", "rt");
|
2013-04-24 18:10:08 +00:00
|
|
|
media.queueby = user ? user.name : "";
|
2013-04-07 19:38:50 +00:00
|
|
|
this.queue.splice(idx, 0, media);
|
|
|
|
this.sendAll("queue", {
|
|
|
|
media: media.pack(),
|
|
|
|
pos: idx
|
|
|
|
});
|
2013-04-24 19:28:20 +00:00
|
|
|
this.broadcastPlaylistMeta();
|
2013-04-07 19:38:50 +00:00
|
|
|
break;
|
2013-04-03 17:47:41 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.tryQueue = function(user, data) {
|
|
|
|
if(!Rank.hasPermission(user, "queue") &&
|
|
|
|
this.leader != user &&
|
|
|
|
!this.openqueue) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(data.pos == undefined || data.id == undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(data.type == undefined && !(data.id in this.library)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(data.pos == "next" && !Rank.hasPermission(user, "queue") &&
|
|
|
|
this.leader != user &&
|
|
|
|
!this.opts.qopen_allow_qnext) {
|
|
|
|
return;
|
|
|
|
}
|
2013-04-24 17:45:17 +00:00
|
|
|
|
2013-04-24 20:50:43 +00:00
|
|
|
if(user.noflood("queue", 1.5)) {
|
2013-04-24 17:45:17 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-04-24 18:10:08 +00:00
|
|
|
this.enqueue(data, user);
|
2013-04-03 17:47:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.dequeue = function(data) {
|
|
|
|
if(data.pos < 0 || data.pos >= this.queue.length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.queue.splice(data.pos, 1);
|
|
|
|
this.sendAll("unqueue", {
|
|
|
|
pos: data.pos
|
|
|
|
});
|
2013-04-24 19:28:20 +00:00
|
|
|
this.broadcastPlaylistMeta();
|
2013-04-03 17:47:41 +00:00
|
|
|
|
|
|
|
// If you remove the currently playing video, play the next one
|
|
|
|
if(data.pos == this.position) {
|
|
|
|
this.position--;
|
|
|
|
this.playNext();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// If you remove a video whose position is before the one currently
|
|
|
|
// playing, you have to reduce the position of the one playing
|
|
|
|
if(data.pos < this.position) {
|
|
|
|
this.position--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.tryDequeue = function(user, data) {
|
|
|
|
if(!Rank.hasPermission(user, "queue") &&
|
|
|
|
this.leader != user &&
|
|
|
|
(!this.openqueue ||
|
|
|
|
this.openqueue && !this.opts.qopen_allow_delete)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(data.pos == undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.dequeue(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.playNext = function() {
|
|
|
|
// Nothing to play
|
|
|
|
if(this.queue.length == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset voteskip
|
|
|
|
this.voteskip = false;
|
2013-04-21 01:17:38 +00:00
|
|
|
this.broadcastVoteskipUpdate();
|
2013-04-04 23:22:11 +00:00
|
|
|
this.drinks = 0;
|
|
|
|
this.broadcastDrinks();
|
2013-04-03 17:47:41 +00:00
|
|
|
|
|
|
|
var old = this.position;
|
|
|
|
// Wrap around if the end is hit
|
|
|
|
if(this.position + 1 >= this.queue.length) {
|
|
|
|
this.position = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.position++;
|
2013-04-24 02:33:53 +00:00
|
|
|
var oid = this.media ? this.media.id : "";
|
2013-04-03 17:47:41 +00:00
|
|
|
this.media = this.queue[this.position];
|
2013-04-24 02:33:53 +00:00
|
|
|
this.media.currentTime = -1;
|
2013-04-03 17:47:41 +00:00
|
|
|
|
|
|
|
this.sendAll("mediaUpdate", this.media.packupdate());
|
|
|
|
this.sendAll("updatePlaylistIdx", {
|
|
|
|
old: old,
|
|
|
|
idx: this.position
|
|
|
|
});
|
|
|
|
|
|
|
|
// If it's not a livestream, enable autolead
|
|
|
|
if(this.leader == null && this.media.type != "tw"
|
2013-04-07 19:38:50 +00:00
|
|
|
&& this.media.type != "li"
|
|
|
|
&& this.media.type != "rt") {
|
2013-04-03 17:47:41 +00:00
|
|
|
this.time = new Date().getTime();
|
2013-04-24 02:33:53 +00:00
|
|
|
if(this.media.id != oid) {
|
|
|
|
mediaUpdate(this, this.media.id);
|
|
|
|
}
|
2013-04-03 17:47:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.tryPlayNext = function(user) {
|
|
|
|
if(!Rank.hasPermission(user, "queue") &&
|
|
|
|
this.leader != user &&
|
|
|
|
(!this.openqueue ||
|
|
|
|
this.openqueue && !this.opts.qopen_allow_playnext)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.playNext();
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.jumpTo = function(pos) {
|
|
|
|
if(pos >= this.queue.length || pos < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset voteskip
|
|
|
|
this.voteskip = false;
|
2013-04-21 01:17:38 +00:00
|
|
|
this.broadcastVoteskipUpdate();
|
2013-04-04 23:22:11 +00:00
|
|
|
this.drinks = 0;
|
|
|
|
this.broadcastDrinks();
|
2013-04-03 17:47:41 +00:00
|
|
|
|
|
|
|
var old = this.position;
|
|
|
|
this.position = pos;
|
2013-04-24 02:33:53 +00:00
|
|
|
var oid = this.media ? this.media.id : "";
|
2013-04-03 17:47:41 +00:00
|
|
|
this.media = this.queue[this.position];
|
2013-04-24 02:33:53 +00:00
|
|
|
this.media.currentTime = -1;
|
2013-04-03 17:47:41 +00:00
|
|
|
|
|
|
|
this.sendAll("mediaUpdate", this.media.packupdate());
|
|
|
|
this.sendAll("updatePlaylistIdx", {
|
|
|
|
old: old,
|
|
|
|
idx: this.position
|
|
|
|
});
|
|
|
|
|
|
|
|
// If it's not a livestream, enable autolead
|
|
|
|
if(this.leader == null && this.media.type != "tw"
|
2013-04-07 19:38:50 +00:00
|
|
|
&& this.media.type != "li"
|
|
|
|
&& this.media.type != "rt") {
|
2013-04-03 17:47:41 +00:00
|
|
|
this.time = new Date().getTime();
|
2013-04-24 02:33:53 +00:00
|
|
|
if(this.media.id != oid) {
|
|
|
|
mediaUpdate(this, this.media.id);
|
|
|
|
}
|
2013-04-03 17:47:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.tryJumpTo = function(user, data) {
|
2013-04-04 16:39:43 +00:00
|
|
|
if(!Rank.hasPermission(user, "queue") &&
|
|
|
|
this.leader != user &&
|
|
|
|
(!this.openqueue ||
|
|
|
|
this.openqueue && !this.opts.qopen_allow_playnext)) {
|
2013-04-03 17:47:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(data.pos == undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.jumpTo(data.pos);
|
|
|
|
}
|
|
|
|
|
2013-04-22 20:37:42 +00:00
|
|
|
Channel.prototype.clearqueue = function() {
|
|
|
|
this.queue = [];
|
|
|
|
for(var i = 0; i < this.users.length; i++) {
|
|
|
|
this.sendPlaylist(this.users[i]);
|
|
|
|
}
|
2013-04-24 19:28:20 +00:00
|
|
|
this.broadcastPlaylistMeta();
|
2013-04-22 20:37:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.tryClearqueue = function(user) {
|
|
|
|
if(!Rank.hasPermission(user, "queue")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.clearqueue();
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.shufflequeue = function() {
|
|
|
|
var n = [];
|
|
|
|
var current = false;
|
|
|
|
while(this.queue.length > 0) {
|
|
|
|
var i = parseInt(Math.random() * this.queue.length);
|
|
|
|
n.push(this.queue[i]);
|
|
|
|
if(!current && i == this.position) {
|
|
|
|
this.position = n.length - 1;
|
|
|
|
current = true;
|
|
|
|
}
|
|
|
|
this.queue.splice(i, 1);
|
|
|
|
}
|
|
|
|
this.queue = n;
|
|
|
|
for(var i = 0; i < this.users.length; i++) {
|
|
|
|
this.sendPlaylist(this.users[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.tryShufflequeue = function(user) {
|
|
|
|
if(!Rank.hasPermission(user, "queue")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.shufflequeue();
|
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.tryUpdate = function(user, data) {
|
|
|
|
if(this.leader != user) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-04-04 19:56:43 +00:00
|
|
|
if(data == null ||
|
|
|
|
data.id == undefined || data.currentTime == undefined) {
|
2013-04-03 17:47:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-04-04 19:56:43 +00:00
|
|
|
if(this.media == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-04-07 19:38:50 +00:00
|
|
|
if(this.media.type == "li" || this.media.type == "tw" ||
|
|
|
|
this.media.type == "rt") {
|
2013-04-03 17:47:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-04-03 22:56:03 +00:00
|
|
|
if(this.media.id != data.id) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.media.currentTime = data.currentTime;
|
2013-04-03 17:47:41 +00:00
|
|
|
this.sendAll("mediaUpdate", this.media.packupdate());
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.move = function(data) {
|
|
|
|
if(data.src < 0 || data.src >= this.queue.length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(data.dest < 0 || data.dest > this.queue.length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var media = this.queue[data.src];
|
|
|
|
var dest = data.dest > data.src ? data.dest + 1 : data.dest;
|
|
|
|
var src = data.dest > data.src ? data.src : data.src + 1;
|
|
|
|
|
|
|
|
this.queue.splice(dest, 0, media);
|
|
|
|
this.queue.splice(src, 1);
|
|
|
|
this.sendAll("moveVideo", {
|
|
|
|
src: data.src,
|
|
|
|
dest: data.dest
|
|
|
|
});
|
|
|
|
|
|
|
|
// Account for moving things around the active video
|
2013-04-03 20:18:35 +00:00
|
|
|
if(data.src < this.position && data.dest >= this.position) {
|
2013-04-03 17:47:41 +00:00
|
|
|
this.position--;
|
|
|
|
}
|
2013-04-03 20:18:35 +00:00
|
|
|
else if(data.src > this.position && data.dest < this.position) {
|
2013-04-03 17:47:41 +00:00
|
|
|
this.position++
|
|
|
|
}
|
|
|
|
else if(data.src == this.position) {
|
|
|
|
this.position = data.dest;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.tryMove = function(user, data) {
|
|
|
|
if(!Rank.hasPermission(user, "queue") &&
|
2013-04-16 16:02:00 +00:00
|
|
|
this.leader != user &&
|
2013-04-03 17:47:41 +00:00
|
|
|
(!this.openqueue ||
|
2013-04-18 20:36:52 +00:00
|
|
|
this.openqueue && !this.opts.qopen_allow_move)) {
|
2013-04-03 17:47:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(data.src == undefined || data.dest == undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.move(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* REGION Polls */
|
|
|
|
|
2013-04-18 16:42:07 +00:00
|
|
|
Channel.prototype.tryOpenPoll = function(user, data) {
|
|
|
|
if(!Rank.hasPermission(user, "poll") && this.leader != user) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!data.title || !data.opts) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var poll = new Poll(user.name, data.title, data.opts);
|
|
|
|
this.poll = poll;
|
|
|
|
this.broadcastPoll();
|
|
|
|
this.logger.log("*** " + user.name + " Opened Poll: '" + poll.title + "'");
|
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.tryClosePoll = function(user) {
|
2013-04-14 17:38:00 +00:00
|
|
|
if(!Rank.hasPermission(user, "poll") && this.leader != user) {
|
2013-04-03 17:47:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this.poll) {
|
|
|
|
this.poll = false;
|
|
|
|
this.broadcastPollClose();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.tryVote = function(user, data) {
|
|
|
|
if(data.option == undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(this.poll) {
|
|
|
|
this.poll.vote(user.ip, data.option);
|
|
|
|
this.broadcastPollUpdate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.tryVoteskip = function(user) {
|
2013-04-04 16:05:01 +00:00
|
|
|
if(!this.opts.allow_voteskip) {
|
|
|
|
return;
|
|
|
|
}
|
2013-04-03 17:47:41 +00:00
|
|
|
if(!this.voteskip) {
|
|
|
|
this.voteskip = new Poll("voteskip", "voteskip", ["yes"]);
|
|
|
|
}
|
|
|
|
this.voteskip.vote(user.ip, 0);
|
2013-04-21 01:17:38 +00:00
|
|
|
this.broadcastVoteskipUpdate();
|
2013-04-23 15:23:32 +00:00
|
|
|
if(this.voteskip.counts[0] >= parseInt(this.users.length * this.opts.voteskip_ratio)) {
|
2013-04-03 17:47:41 +00:00
|
|
|
this.playNext();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* REGION Channel Option stuff */
|
|
|
|
|
|
|
|
Channel.prototype.setLock = function(locked) {
|
|
|
|
this.openqueue = !locked;
|
|
|
|
this.sendAll("queueLock", {locked: locked});
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.trySetLock = function(user, data) {
|
|
|
|
if(!Rank.hasPermission(user, "qlock")) {
|
|
|
|
return;
|
|
|
|
}
|
2013-04-16 16:02:00 +00:00
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
if(data.locked == undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setLock(data.locked);
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.updateFilter = function(filter) {
|
|
|
|
var found = false;
|
|
|
|
for(var i = 0; i < this.filters.length; i++) {
|
|
|
|
if(this.filters[i][0].source == filter[0].source) {
|
2013-03-31 19:27:54 +00:00
|
|
|
found = true;
|
|
|
|
this.filters[i][1] = filter[1];
|
|
|
|
this.filters[i][2] = filter[2];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!found) {
|
|
|
|
this.filters.push(filter);
|
|
|
|
}
|
|
|
|
this.broadcastChatFilters();
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.removeFilter = function(regex) {
|
|
|
|
for(var i = 0; i < this.filters.length; i++) {
|
|
|
|
if(this.filters[i][0].source == regex) {
|
|
|
|
this.filters.splice(i, 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.broadcastChatFilters();
|
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.tryChangeFilter = function(user, data) {
|
|
|
|
if(!Rank.hasPermission(user, "chatFilter")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(data.cmd == undefined || data.filter == undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(data.cmd == "update") {
|
2013-04-16 16:02:00 +00:00
|
|
|
var re = data.filter[0];
|
|
|
|
var flags = "g";
|
|
|
|
var slash = re.lastIndexOf("/");
|
|
|
|
if(slash > 0 && re[slash-1] != "\\") {
|
|
|
|
flags = re.substring(slash+1);
|
|
|
|
re = re.substring(0, slash);
|
|
|
|
}
|
2013-04-03 17:47:41 +00:00
|
|
|
try {
|
2013-04-16 16:02:00 +00:00
|
|
|
data.filter[0] = new RegExp(re, flags);
|
2013-04-03 17:47:41 +00:00
|
|
|
}
|
|
|
|
catch(e) {
|
|
|
|
return;
|
|
|
|
}
|
2013-04-04 23:22:11 +00:00
|
|
|
this.updateFilter(data.filter);
|
2013-04-03 17:47:41 +00:00
|
|
|
}
|
|
|
|
else if(data.cmd == "remove") {
|
|
|
|
this.removeFilter(data.filter[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.tryUpdateOptions = function(user, data) {
|
|
|
|
if(!Rank.hasPermission(user, "channelOpts")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(var key in this.opts) {
|
|
|
|
if(key in data) {
|
|
|
|
this.opts[key] = data[key];
|
|
|
|
}
|
|
|
|
}
|
2013-04-16 16:02:00 +00:00
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
this.broadcastOpts();
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.updateMotd = function(motd) {
|
|
|
|
var html = motd.replace(/\n/g, "<br>");
|
|
|
|
html = this.filterMessage(html);
|
|
|
|
this.motd = {
|
|
|
|
motd: motd,
|
|
|
|
html: html
|
|
|
|
};
|
|
|
|
this.broadcastMotd();
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.tryUpdateMotd = function(user, data) {
|
|
|
|
if(!Rank.hasPermission(user, "updateMotd")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(data.motd) {
|
|
|
|
this.updateMotd(data.motd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* REGION Chat */
|
|
|
|
|
|
|
|
Channel.prototype.tryChat = function(user, data) {
|
|
|
|
if(user.name == "") {
|
|
|
|
return;
|
|
|
|
}
|
2013-04-16 16:02:00 +00:00
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
if(data.msg == undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var msg = data.msg;
|
|
|
|
|
2013-03-05 18:51:58 +00:00
|
|
|
if(msg.indexOf("/") == 0)
|
2013-03-05 19:57:00 +00:00
|
|
|
ChatCommand.handle(this, user, msg);
|
2013-03-05 18:51:58 +00:00
|
|
|
|
2013-02-16 05:02:42 +00:00
|
|
|
else if(msg.indexOf(">") == 0)
|
2013-03-05 18:51:58 +00:00
|
|
|
this.sendMessage(user.name, msg, "greentext");
|
|
|
|
|
|
|
|
else
|
2013-03-05 19:57:00 +00:00
|
|
|
this.sendMessage(user.name, msg, "");
|
2013-03-05 18:51:58 +00:00
|
|
|
}
|
|
|
|
|
2013-04-01 21:02:09 +00:00
|
|
|
Channel.prototype.filterMessage = function(msg) {
|
2013-03-29 18:15:46 +00:00
|
|
|
msg = msg.replace(/(((https?)|(ftp))(:\/\/[0-9a-zA-Z\.]+(:[0-9]+)?[^\s$]+))/g, "<a href=\"$1\" target=\"_blank\">$1</a>");
|
|
|
|
// Apply other filters
|
|
|
|
for(var i = 0; i < this.filters.length; i++) {
|
|
|
|
if(!this.filters[i][2])
|
|
|
|
continue;
|
|
|
|
var regex = this.filters[i][0];
|
|
|
|
var replace = this.filters[i][1];
|
|
|
|
msg = msg.replace(regex, replace);
|
|
|
|
}
|
2013-04-01 21:02:09 +00:00
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2013-04-17 18:29:52 +00:00
|
|
|
Channel.prototype.sendMessage = function(username, msg, msgclass, data) {
|
2013-04-03 17:47:41 +00:00
|
|
|
// I don't want HTML from strangers
|
2013-04-01 21:02:09 +00:00
|
|
|
msg = msg.replace(/</g, "<").replace(/>/g, ">");
|
|
|
|
msg = this.filterMessage(msg);
|
2013-04-17 18:29:52 +00:00
|
|
|
var msgobj = {
|
2013-03-05 18:51:58 +00:00
|
|
|
username: username,
|
2013-02-16 05:02:42 +00:00
|
|
|
msg: msg,
|
|
|
|
msgclass: msgclass
|
2013-04-17 18:29:52 +00:00
|
|
|
};
|
|
|
|
if(data) {
|
|
|
|
for(var key in data) {
|
|
|
|
msgobj[key] = data[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.sendAll("chatMsg", msgobj);
|
|
|
|
this.chatbuffer.push(msgobj);
|
2013-04-03 17:47:41 +00:00
|
|
|
if(this.chatbuffer.length > 15)
|
|
|
|
this.chatbuffer.shift();
|
2013-03-27 19:28:51 +00:00
|
|
|
this.logger.log("<" + username + "." + msgclass + "> " + msg);
|
2013-03-05 18:51:58 +00:00
|
|
|
};
|
2013-02-16 05:02:42 +00:00
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
/* REGION Rank stuff */
|
|
|
|
|
|
|
|
Channel.prototype.tryPromoteUser = function(actor, data) {
|
|
|
|
if(!Rank.hasPermission(actor, "promote")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(data.name == undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var name = data.name;
|
|
|
|
|
2013-02-16 05:02:42 +00:00
|
|
|
var receiver;
|
|
|
|
for(var i = 0; i < this.users.length; i++) {
|
|
|
|
if(this.users[i].name == name) {
|
|
|
|
receiver = this.users[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-22 18:28:40 +00:00
|
|
|
var rank = receiver ? receiver.rank : this.getRank(data.name);
|
|
|
|
|
|
|
|
if(actor.rank > rank + 1) {
|
|
|
|
rank++;
|
|
|
|
if(receiver) {
|
2013-02-16 05:02:42 +00:00
|
|
|
receiver.rank++;
|
|
|
|
if(receiver.loggedIn) {
|
|
|
|
this.saveRank(receiver);
|
|
|
|
}
|
|
|
|
this.broadcastRankUpdate(receiver);
|
|
|
|
}
|
2013-04-22 18:28:40 +00:00
|
|
|
else {
|
|
|
|
Database.saveChannelRank(this.name, {
|
|
|
|
name: data.name,
|
|
|
|
rank: rank
|
|
|
|
});
|
|
|
|
}
|
|
|
|
this.logger.log("*** " + actor.name + " promoted " + data.name + " from " + (rank - 1) + " to " + rank);
|
|
|
|
this.broadcastRankTable();
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.tryDemoteUser = function(actor, data) {
|
|
|
|
if(!Rank.hasPermission(actor, "promote")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(data.name == undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var name = data.name;
|
2013-02-16 05:02:42 +00:00
|
|
|
var receiver;
|
|
|
|
for(var i = 0; i < this.users.length; i++) {
|
|
|
|
if(this.users[i].name == name) {
|
|
|
|
receiver = this.users[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-22 18:28:40 +00:00
|
|
|
var rank = receiver ? receiver.rank : this.getRank(data.name);
|
|
|
|
|
|
|
|
if(actor.rank > rank) {
|
|
|
|
rank--;
|
|
|
|
if(receiver) {
|
2013-02-16 05:02:42 +00:00
|
|
|
receiver.rank--;
|
|
|
|
if(receiver.loggedIn) {
|
|
|
|
this.saveRank(receiver);
|
|
|
|
}
|
|
|
|
this.broadcastRankUpdate(receiver);
|
|
|
|
}
|
2013-04-22 18:28:40 +00:00
|
|
|
else {
|
|
|
|
Database.saveChannelRank(this.name, {
|
|
|
|
name: data.name,
|
|
|
|
rank: rank
|
|
|
|
});
|
|
|
|
}
|
|
|
|
this.logger.log("*** " + actor.name + " demoted " + data.name + " from " + (rank + 1) + " to " + rank);
|
|
|
|
this.broadcastRankTable();
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel.prototype.changeLeader = function(name) {
|
|
|
|
if(this.leader != null) {
|
2013-02-17 05:00:33 +00:00
|
|
|
var old = this.leader;
|
2013-02-16 05:02:42 +00:00
|
|
|
this.leader = null;
|
2013-02-16 22:33:38 +00:00
|
|
|
this.broadcastRankUpdate(old);
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
if(name == "") {
|
2013-03-27 19:28:51 +00:00
|
|
|
this.logger.log("*** Resuming autolead");
|
2013-04-03 17:47:41 +00:00
|
|
|
if(this.media != null && this.media.type != "li"
|
2013-04-07 19:38:50 +00:00
|
|
|
&& this.media.type != "tw"
|
|
|
|
&& this.media.type != "rt") {
|
2013-03-20 18:03:32 +00:00
|
|
|
this.time = new Date().getTime();
|
|
|
|
this.i = 0;
|
2013-04-03 17:47:41 +00:00
|
|
|
mediaUpdate(this, this.media.id);
|
2013-03-20 18:03:32 +00:00
|
|
|
}
|
2013-02-16 05:02:42 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
for(var i = 0; i < this.users.length; i++) {
|
|
|
|
if(this.users[i].name == name) {
|
2013-03-27 19:28:51 +00:00
|
|
|
this.logger.log("*** Assigned leader: " + name);
|
2013-02-16 05:02:42 +00:00
|
|
|
this.leader = this.users[i];
|
|
|
|
this.broadcastRankUpdate(this.leader);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
Channel.prototype.tryChangeLeader = function(user, data) {
|
|
|
|
if(!Rank.hasPermission(user, "assignLeader")) {
|
|
|
|
return;
|
2013-04-02 19:07:22 +00:00
|
|
|
}
|
2013-02-16 05:02:42 +00:00
|
|
|
|
2013-04-03 17:47:41 +00:00
|
|
|
if(data.name == undefined) {
|
2013-02-16 05:02:42 +00:00
|
|
|
return;
|
|
|
|
}
|
2013-04-03 17:47:41 +00:00
|
|
|
|
|
|
|
this.changeLeader(data.name);
|
2013-02-16 05:02:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
exports.Channel = Channel;
|