CyTube/channel.js

1950 lines
54 KiB
JavaScript
Raw Normal View History

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
var fs = require("fs");
2013-04-03 17:47:41 +00:00
var Database = require("./database.js");
var Poll = require("./poll.js").Poll;
var Media = require("./media.js").Media;
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");
2013-05-03 18:15:05 +00:00
var Server = require("./server.js");
var io = Server.io;
var NWS = require("./notwebsocket");
2013-04-03 17:47:41 +00:00
var Rank = require("./rank.js");
var Auth = require("./auth.js");
2013-04-04 19:56:43 +00:00
var ChatCommand = require("./chatcommand.js");
2013-04-29 17:29:31 +00:00
var Filter = require("./filter.js").Filter;
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-06-15 20:07:38 +00:00
this.initialized = false;
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-05-22 19:38:16 +00:00
this.permissions = {
oplaylistadd: -1,
oplaylistnext: 1.5,
oplaylistmove: 1.5,
oplaylistdelete: 2,
oplaylistjump: 1.5,
2013-06-01 20:56:23 +00:00
oplaylistaddlist: 1.5,
2013-05-22 19:38:16 +00:00
playlistadd: 1.5,
playlistnext: 1.5,
playlistmove: 1.5,
playlistdelete: 2,
playlistjump: 1.5,
2013-06-01 20:56:23 +00:00
playlistaddlist: 1.5,
2013-06-05 02:51:41 +00:00
playlistaddlive: 1.5,
2013-05-22 19:38:16 +00:00
addnontemp: 2,
settemp: 2,
playlistgeturl: 1.5,
playlistshuffle: 2,
playlistclear: 2,
pollctl: 1.5,
pollvote: -1,
kick: 1.5,
ban: 2,
motdedit: 3,
filteredit: 3,
2013-06-20 19:02:53 +00:00
drink: 1.5,
chat: 0
2013-05-22 19:38:16 +00:00
};
this.opts = {
2013-04-02 19:07:22 +00:00
allow_voteskip: true,
voteskip_ratio: 0.5,
2013-04-03 17:47:41 +00:00
pagetitle: this.name,
2013-06-17 22:16:59 +00:00
externalcss: "",
externaljs: "",
2013-05-01 22:49:34 +00:00
chat_antiflood: false,
2013-06-05 20:49:54 +00:00
show_public: false,
enable_link_regex: true
};
2013-03-29 18:15:46 +00:00
this.filters = [
2013-04-29 17:29:31 +00:00
new Filter("monospace", "`([^`]+)`", "g", "<code>$1</code>"),
new Filter("bold", "(.)\\*([^\\*]+)\\*", "g", "$1<strong>$2</strong>"),
2013-04-29 17:29:31 +00:00
new Filter("italic", "(^| )_([^_]+)_", "g", "$1<em>$2</em>"),
2013-05-22 19:38:16 +00:00
new Filter("strikethrough", "~~([^~]+)~~", "g", "<s>$1</s>"),
new Filter("inline spoiler", "\\[spoiler\\](.*)\\[\\/spoiler\\]", "ig", "<span class=\"spoiler\">$1</span>"),
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 = {};
2013-05-21 16:17:01 +00:00
this.namebans = {};
2013-06-18 03:57:29 +00:00
this.ip_alias = {};
this.name_alias = {};
this.login_hist = [];
2013-03-27 19:28:51 +00:00
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();
this.plmeta = {
count: 0,
time: "00:00"
};
2013-04-03 17:47:41 +00:00
2013-05-15 15:34:27 +00:00
this.css = "";
this.js = "";
2013-05-21 16:17:01 +00:00
this.ipkey = "";
for(var i = 0; i < 15; i++) {
this.ipkey += "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[parseInt(Math.random() * 65)]
}
2013-04-03 17:47:41 +00:00
Database.loadChannel(this);
if(this.registered) {
this.loadDump();
}
}
2013-05-22 19:38:16 +00:00
/* REGION Permissions */
Channel.prototype.hasPermission = function(user, key) {
if(key.indexOf("playlist") == 0 && this.openqueue) {
var key2 = "o" + key;
var v = this.permissions[key2];
2013-05-23 04:03:37 +00:00
if(typeof v == "number" && user.rank >= v) {
return true;
2013-05-22 19:38:16 +00:00
}
}
var v = this.permissions[key];
if(typeof v != "number") {
return false;
}
return user.rank >= v;
}
2013-04-03 17:47:41 +00:00
/* REGION Channel data */
Channel.prototype.loadDump = function() {
fs.readFile("chandump/" + this.name, function(err, data) {
if(err) {
2013-06-19 21:54:27 +00:00
if(err.code == "ENOENT") {
Logger.errlog.log("WARN: missing dump for " + this.name);
this.initialized = true;
this.saveDump();
}
else {
Logger.errlog.log("Failed to open channel dump " + this.name);
Logger.errlog.log(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];
var m = new Media(e.id, e.title, e.seconds, e.type);
m.queueby = data.queue[i].queueby ? data.queue[i].queueby
: "";
2013-05-04 22:54:28 +00:00
if(e.temp !== undefined) {
m.temp = e.temp;
}
this.queue.push(m);
}
2013-06-11 15:29:21 +00:00
this.sendAll("playlist", this.queue);
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();
if(this.media && data.currentTime) {
this.media.currentTime = data.currentTime;
}
for(var key in data.opts) {
2013-06-17 22:16:59 +00:00
// Gotta love backwards compatibility
if(key == "customcss" || key == "customjs") {
var k = key.substring(6);
this.opts[k] = data.opts[key];
}
else {
this.opts[key] = data.opts[key];
}
}
2013-05-22 19:38:16 +00:00
for(var key in data.permissions) {
this.permissions[key] = data.permissions[key];
}
2013-05-26 16:47:25 +00:00
this.sendAll("setPermissions", this.permissions);
2013-04-27 17:13:01 +00:00
this.broadcastOpts();
if(data.filters) {
for(var i = 0; i < data.filters.length; i++) {
2013-04-29 17:29:31 +00:00
var f = data.filters[i];
// Backwards compatibility
if(f[0] != undefined) {
var filt = new Filter("", f[0], "g", f[1]);
filt.active = f[2];
this.updateFilter(filt);
2013-04-29 17:29:31 +00:00
}
else {
var filt = new Filter(f.name, f.source, f.flags, f.replace);
filt.active = f.active;
this.updateFilter(filt);
2013-04-29 17:29:31 +00:00
}
}
2013-05-06 16:24:55 +00:00
this.broadcastChatFilters();
}
2013-04-01 21:02:09 +00:00
if(data.motd) {
this.motd = data.motd;
2013-05-04 17:02:38 +00:00
this.broadcastMotd();
2013-04-01 21:02:09 +00:00
}
2013-05-04 17:02:38 +00:00
this.setLock(!(data.openqueue || false));
this.chatbuffer = data.chatbuffer || [];
for(var i = 0; i < this.chatbuffer.length; i++) {
this.sendAll("chatMsg", this.chatbuffer[i]);
}
2013-05-15 15:34:27 +00:00
this.css = data.css || "";
this.js = data.js || "";
this.sendAll("channelCSSJS", {css: this.css, js: this.js});
2013-06-15 20:07:38 +00:00
this.initialized = true;
2013-05-17 02:37:33 +00:00
setTimeout(function() { incrementalDump(this); }.bind(this), 300000);
}
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() {
2013-06-15 20:07:38 +00:00
if(!this.initialized)
return;
2013-04-03 17:47:41 +00:00
var filts = new Array(this.filters.length);
for(var i = 0; i < this.filters.length; i++) {
2013-04-29 17:29:31 +00:00
filts[i] = this.filters[i].pack();
2013-04-03 17:47:41 +00:00
}
var dump = {
position: this.position,
currentTime: this.media ? this.media.currentTime : 0,
2013-04-03 17:47:41 +00:00
queue: this.queue,
opts: this.opts,
2013-05-22 19:38:16 +00:00
permissions: this.permissions,
2013-04-03 17:47:41 +00:00
filters: filts,
2013-04-29 23:59:51 +00:00
motd: this.motd,
2013-05-04 17:02:38 +00:00
openqueue: this.openqueue,
2013-05-15 15:34:27 +00:00
chatbuffer: this.chatbuffer,
css: this.css,
js: this.js
2013-04-03 17:47:41 +00:00
};
var text = JSON.stringify(dump);
fs.writeFileSync("chandump/" + this.name, text);
this.logger.flush();
2013-02-16 05:02:42 +00:00
}
2013-05-04 17:02:38 +00:00
// Save channel dumps every 5 minutes, in case of crash
function incrementalDump(chan) {
2013-05-04 17:30:38 +00:00
if(chan && chan.users && chan.users.length > 0) {
2013-05-04 17:02:38 +00:00
chan.saveDump();
2013-05-04 17:30:38 +00:00
setTimeout(function() { incrementalDump(chan); }, 300000);
2013-05-04 17:02:38 +00:00
}
}
2013-03-17 17:14:34 +00:00
Channel.prototype.tryRegister = function(user) {
if(this.registered) {
user.socket.emit("registerChannel", {
2013-03-17 17:14:34 +00:00
success: false,
error: "This channel is already registered"
});
}
else if(!user.loggedIn) {
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")) {
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-05-28 01:16:18 +00:00
if(Database.registerChannel(this.name)) {
2013-03-17 17:14:34 +00:00
this.registered = true;
this.initialized = true;
this.saveDump();
2013-03-17 17:14:34 +00:00
this.saveRank(user);
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 {
user.socket.emit("registerChannel", {
2013-03-17 17:14:34 +00:00
success: false,
error: "Unable to register channel, see an admin"
});
}
}
}
2013-05-13 19:41:29 +00:00
Channel.prototype.unregister = function() {
2013-05-26 01:01:32 +00:00
if(Database.deleteChannel(this.name)) {
2013-05-13 19:41:29 +00:00
this.registered = false;
return true;
}
return false;
}
2013-04-04 20:41:41 +00:00
Channel.prototype.getRank = function(name) {
var global = Auth.getGlobalRank(name);
2013-04-03 17:47:41 +00:00
if(!this.registered) {
return global;
2013-02-16 05:02:42 +00:00
}
2013-05-26 01:01:32 +00:00
var local = Database.getChannelRank(this.name, name);
return local > global ? local : global;
2013-02-16 05:02:42 +00:00
}
Channel.prototype.saveRank = function(user) {
2013-05-26 01:01:32 +00:00
return Database.setChannelRank(this.name, user.name, user.rank);
2013-02-16 05:02:42 +00:00
}
2013-05-19 21:23:35 +00:00
Channel.prototype.getIPRank = function(ip) {
2013-06-15 20:07:38 +00:00
var names = [];
2013-06-18 03:57:29 +00:00
if(!(ip in this.ip_alias))
this.ip_alias = Database.getAliases(ip);
this.ip_alias[ip].forEach(function(name) {
2013-06-15 20:07:38 +00:00
names.push(name);
});
2013-05-26 01:01:32 +00:00
var ranks = Database.getChannelRank(this.name, names);
2013-05-19 21:23:35 +00:00
var rank = 0;
2013-05-26 01:01:32 +00:00
for(var i = 0; i < ranks.length; i++) {
rank = (ranks[i] > rank) ? ranks[i] : rank;
2013-05-19 21:23:35 +00:00
}
return rank;
}
2013-04-03 17:47:41 +00:00
Channel.prototype.cacheMedia = function(media) {
2013-06-12 03:44:16 +00:00
// Prevent the copy in the playlist from messing with this one
media = media.dup();
2013-05-04 22:54:28 +00:00
if(media.temp) {
return;
}
2013-02-16 05:02:42 +00:00
this.library[media.id] = media;
2013-04-03 17:47:41 +00:00
if(this.registered) {
2013-05-26 01:01:32 +00:00
return Database.addToLibrary(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-06-18 03:57:29 +00:00
Channel.prototype.tryNameBan = function(actor, name) {
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(actor, "ban")) {
2013-05-21 16:17:01 +00:00
return false;
}
name = name.toLowerCase();
var rank = this.getRank(name);
if(rank >= actor.rank) {
actor.socket.emit("errorMsg", {msg: "You don't have permission to ban this person."});
return false;
}
this.namebans[name] = actor.name;
for(var i = 0; i < this.users.length; i++) {
if(this.users[i].name.toLowerCase() == name) {
this.kick(this.users[i], "You're banned!");
break;
}
}
this.logger.log(name + " was banned by " + actor.name);
2013-06-18 14:46:28 +00:00
var chan = this;
this.users.forEach(function(u) {
chan.sendBanlist(u);
});
2013-05-21 16:17:01 +00:00
if(!this.registered) {
return false;
}
2013-05-26 01:01:32 +00:00
return Database.channelBan(this.name, "*", name, actor.name);
2013-05-21 16:17:01 +00:00
}
Channel.prototype.unbanName = function(actor, name) {
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(actor, "ban")) {
2013-05-21 16:17:01 +00:00
return false;
}
this.namebans[name] = null;
delete this.namebans[name];
this.logger.log(name + " was unbanned by " + actor.name);
2013-06-18 14:46:28 +00:00
var chan = this;
this.users.forEach(function(u) {
chan.sendBanlist(u);
});
2013-05-26 01:01:32 +00:00
return Database.channelUnbanName(this.name, name);
2013-05-21 16:17:01 +00:00
}
2013-06-18 03:57:29 +00:00
Channel.prototype.tryIPBan = function(actor, name, range) {
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(actor, "ban")) {
2013-05-21 16:17:01 +00:00
return false;
}
2013-06-18 03:57:29 +00:00
if(typeof name != "string") {
2013-05-21 16:17:01 +00:00
return false;
}
2013-06-18 03:57:29 +00:00
var ips = Database.ipForName(name);
var chan = this;
ips.forEach(function(ip) {
if(chan.getIPRank(ip) >= actor.rank) {
actor.socket.emit("errorMsg", {msg: "You don't have permission to ban IP: x.x." + ip.replace(/\d+\.\d+\.(\d+\.\d+)/, "$1")});
return false;
}
2013-05-21 16:17:01 +00:00
2013-06-18 03:57:29 +00:00
if(range) {
ip = ip.replace(/(\d+)\.(\d+)\.(\d+)\.(\d+)/, "$1.$2.$3");
for(var ip2 in chan.ip_alias) {
if(ip2.indexOf(ip) == 0 && chan.getIPRank(ip2) >= actor.rank) {
actor.socket.emit("errorMsg", {msg: "You don't have permission to ban IP: x.x." + ip2.replace(/\d+\.\d+\.(\d+\.\d+)/, "$1")});
return false;
}
2013-05-21 16:17:01 +00:00
}
}
2013-06-18 03:57:29 +00:00
chan.ipbans[ip] = [name, actor.name];
//chan.broadcastBanlist();
2013-06-18 03:57:29 +00:00
chan.logger.log(ip + " (" + name + ") was banned by " + actor.name);
for(var i = 0; i < chan.users.length; i++) {
if(chan.users[i].ip.indexOf(ip) == 0) {
chan.kick(chan.users[i], "Your IP is banned!");
i--;
}
2013-05-21 16:17:01 +00:00
}
2013-06-18 03:57:29 +00:00
if(!chan.registered)
return false;
2013-05-21 16:17:01 +00:00
2013-06-18 03:57:29 +00:00
// Update database ban table
return Database.channelBan(chan.name, ip, name, actor.name);
});
2013-06-18 14:46:28 +00:00
var chan = this;
this.users.forEach(function(u) {
chan.sendBanlist(u);
});
2013-05-21 16:17:01 +00:00
}
2013-04-03 17:47:41 +00:00
Channel.prototype.banIP = function(actor, receiver) {
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(actor, "ban"))
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-28 04:10:48 +00:00
try {
receiver.socket.disconnect(true);
}
catch(e) {
// Socket already disconnected
}
//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
2013-05-26 01:01:32 +00:00
return Database.channelBanIP(this.name, receiver.ip, receiver.name, actor.name);
2013-04-03 17:47:41 +00:00
}
Channel.prototype.unbanIP = function(actor, ip) {
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(actor, "ban"))
2013-05-21 16:17:01 +00:00
return false;
2013-04-08 00:31:52 +00:00
this.ipbans[ip] = null;
2013-06-18 14:46:28 +00:00
var chan = this;
this.users.forEach(function(u) {
chan.sendBanlist(u);
});
2013-03-24 03:45:10 +00:00
2013-04-03 17:47:41 +00:00
if(!this.registered)
return false;
//this.broadcastBanlist();
2013-04-03 17:47:41 +00:00
// Update database ban table
2013-05-26 01:01:32 +00:00
return Database.channelUnbanIP(this.name, ip);
2013-03-24 03:45:10 +00:00
}
2013-05-21 16:17:01 +00:00
Channel.prototype.tryUnban = function(actor, data) {
2013-06-18 03:57:29 +00:00
if(data.ip_hidden) {
var ip = this.hideIP(data.ip_hidden);
2013-05-21 16:17:01 +00:00
this.unbanIP(actor, ip);
}
else if(data.name) {
this.unbanName(actor, data.name);
}
}
Channel.prototype.search = function(query, callback) {
// Search youtube
if(callback) {
if(query.trim() == "") {
return;
}
2013-05-21 16:18:15 +00:00
InfoGetter.getYTSearchResults(query, function(err, vids) {
if(!err) {
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-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-29 23:59:51 +00:00
var parts = user.ip.split(".");
var slash24 = parts[0] + "." + parts[1] + "." + parts[2];
2013-04-03 17:47:41 +00:00
// GTFO
2013-04-29 23:59:51 +00:00
if((user.ip in this.ipbans && this.ipbans[user.ip] != null) ||
(slash24 in this.ipbans && this.ipbans[slash24] != 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-03-24 03:01:37 +00:00
return;
2013-03-24 01:08:35 +00:00
}
2013-05-21 16:17:01 +00:00
if(user.name && user.name.toLowerCase() in this.namebans &&
this.namebans[user.name.toLowerCase()] != null) {
this.kick(user, "You're banned!");
return;
}
2013-03-24 01:08:35 +00:00
2013-04-03 17:47:41 +00:00
// Join the socket pool for this channel
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++) {
2013-05-19 21:23:35 +00:00
if(this.users[i].name.toLowerCase() == user.name.toLowerCase()) {
2013-06-04 22:33:51 +00:00
this.kick(this.users[i], "Duplicate login");
2013-03-17 14:39:22 +00:00
}
}
}
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-05-22 19:38:16 +00:00
user.rank = (user.rank < Rank.Owner) ? 10 : user.rank;
user.socket.emit("channelNotRegistered");
2013-02-16 05:02:42 +00:00
}
this.users.push(user);
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-06-11 15:29:21 +00:00
user.socket.emit("setPlaylistLocked", {locked: !this.openqueue});
2013-02-16 05:02:42 +00:00
this.sendUserlist(user);
this.sendRecentChat(user);
2013-05-15 15:34:27 +00:00
user.socket.emit("channelCSSJS", {css: this.css, js: this.js});
2013-03-16 21:49:58 +00:00
if(this.poll) {
user.socket.emit("newPoll", this.poll.packUpdate());
2013-03-16 21:49:58 +00:00
}
user.socket.emit("channelOpts", this.opts);
2013-05-22 19:38:16 +00:00
user.socket.emit("setPermissions", this.permissions);
2013-06-12 17:07:58 +00:00
user.socket.emit("setMotd", this.motd);
2013-06-11 15:29:21 +00:00
user.socket.emit("drinkCount", 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
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);
this.broadcastVoteskipUpdate();
2013-04-03 17:47:41 +00:00
this.broadcastUsercount();
2013-02-16 05:02:42 +00:00
if(user.name != "") {
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-05-03 18:15:05 +00:00
if(this.users.length == 0) {
this.logger.log("*** Channel empty, unloading");
Server.unload(this);
}
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-05-21 16:17:01 +00:00
Channel.prototype.hideIP = function(ip) {
var chars = new Array(15);
for(var i = 0; i < ip.length; i++) {
chars[i] = String.fromCharCode(ip.charCodeAt(i) ^ this.ipkey.charCodeAt(i));
}
return chars.join("");
}
2013-06-18 03:57:29 +00:00
Channel.prototype.sendLoginHistory = function(user) {
if(user.rank < 2)
return;
user.socket.emit("recentLogins", this.login_hist);
}
Channel.prototype.sendBanlist = function(user) {
2013-05-22 19:38:16 +00:00
if(this.hasPermission(user, "ban")) {
2013-04-03 17:47:41 +00:00
var ents = [];
for(var ip in this.ipbans) {
if(this.ipbans[ip] != null) {
2013-06-18 03:57:29 +00:00
var name = this.ipbans[ip][0];
var ip_hidden = this.hideIP(ip);
2013-05-21 16:17:01 +00:00
var disp = ip;
if(user.rank < Rank.Siteadmin) {
2013-06-18 03:57:29 +00:00
disp = "x.x." + ip.replace(/\d+\.\d+\.(\d+\.\d+)/, "$1");
2013-05-21 16:17:01 +00:00
}
2013-04-03 17:47:41 +00:00
ents.push({
2013-06-18 03:57:29 +00:00
ip_displayed: disp,
ip_hidden: ip_hidden,
2013-05-19 21:23:35 +00:00
name: name,
2013-06-18 03:57:29 +00:00
aliases: this.ip_alias[ip] || [],
2013-04-03 17:47:41 +00:00
banner: this.ipbans[ip][1]
});
2013-03-25 19:39:03 +00:00
}
2013-04-03 17:47:41 +00:00
}
2013-05-21 16:17:01 +00:00
for(var name in this.namebans) {
if(this.namebans[name] != null) {
ents.push({
2013-06-18 03:57:29 +00:00
ip_displayed: "*",
ip_hidden: false,
2013-05-21 16:17:01 +00:00
name: name,
2013-06-18 03:57:29 +00:00
aliases: this.name_alias[name] || [],
2013-05-21 16:17:01 +00:00
banner: this.namebans[name]
});
}
}
2013-06-18 03:57:29 +00:00
user.socket.emit("banlist", ents);
2013-02-16 05:02:42 +00:00
}
}
2013-06-18 14:57:53 +00:00
Channel.prototype.sendChatFilters = function(user) {
2013-05-22 19:38:16 +00:00
if(this.hasPermission(user, "filteredit")) {
2013-04-03 17:47:41 +00:00
var filts = new Array(this.filters.length);
for(var i = 0; i < this.filters.length; i++) {
2013-04-29 17:29:31 +00:00
filts[i] = this.filters[i].pack();
2013-04-03 17:47:41 +00:00
}
2013-06-18 14:57:53 +00:00
user.socket.emit("chatFilters", filts);
2013-03-16 22:17:36 +00:00
}
2013-06-18 14:57:53 +00:00
}
Channel.prototype.sendRankStuff = function(user) {
this.sendBanlist(user);
this.sendChatFilters(user);
2013-06-18 14:46:28 +00:00
this.sendChannelRanks(user);
2013-06-15 20:07:38 +00:00
}
2013-06-18 14:46:28 +00:00
Channel.prototype.sendChannelRanks = function(user) {
if(Rank.hasPermission(user, "acl")) {
2013-06-18 14:46:28 +00:00
user.socket.emit("channelRanks", Database.listChannelRanks(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) {
2013-06-11 15:29:21 +00:00
user.socket.emit("playlist", this.queue);
user.socket.emit("setPosition", this.position);
user.socket.emit("setPlaylistMeta", 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) {
2013-05-07 02:09:33 +00:00
user.socket.emit("changeMedia", this.media.fullupdate());
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,
leader: this.users[i] == this.leader,
meta: this.users[i].meta,
profile: this.users[i].profile
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);
NWS.inRoom(this.name).emit(message, data);
2013-02-16 05:02:42 +00:00
}
Channel.prototype.sendAllWithPermission = function(perm, msg, data) {
for(var i = 0; i < this.users.length; i++) {
if(Rank.hasPermission(this.users[i], perm)) {
this.users[i].socket.emit(msg, data);
}
}
}
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;
2013-06-11 15:29:21 +00:00
this.sendAll("setPlaylistMeta", packet);
}
2013-04-03 17:47:41 +00:00
Channel.prototype.broadcastUsercount = function() {
2013-06-11 15:29:21 +00:00
this.sendAll("usercount", this.users.length);
2013-04-03 17:47:41 +00:00
}
2013-04-03 17:47:41 +00:00
Channel.prototype.broadcastNewUser = function(user) {
2013-06-18 03:57:29 +00:00
var aliases = Database.getAliases(user.ip);
var chan = this;
this.ip_alias[user.ip] = aliases;
aliases.forEach(function(alias) {
chan.name_alias[alias] = aliases;
});
this.login_hist.unshift({
name: user.name,
aliases: this.ip_alias[user.ip],
time: Date.now()
});
if(this.login_hist.length > 20)
this.login_hist.pop();
2013-05-21 16:17:01 +00:00
if(user.name.toLowerCase() in this.namebans &&
this.namebans[user.name.toLowerCase()] != null) {
this.kick(user, "You're banned!");
return;
}
2013-04-03 17:47:41 +00:00
this.sendAll("addUser", {
name: user.name,
rank: user.rank,
leader: this.leader == user,
meta: user.meta,
profile: user.profile
});
//this.sendRankStuff(user);
if(user.rank > Rank.Guest) {
this.saveRank(user);
}
var msg = user.name + " joined (aliases: ";
msg += this.ip_alias[user.ip].join(", ") + ")";
var pkt = {
username: "[server]",
msg: msg,
msgclass: "server-whisper",
time: Date.now()
};
this.users.forEach(function(u) {
if(u.rank >= 2) {
2013-06-18 14:46:28 +00:00
u.socket.emit("joinMessage", pkt);
}
});
}
Channel.prototype.broadcastUserUpdate = function(user) {
2013-04-03 17:47:41 +00:00
this.sendAll("updateUser", {
name: user.name,
rank: user.rank,
leader: this.leader == user,
meta: user.meta,
profile: user.profile
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 = [];
2013-05-21 16:17:01 +00:00
var adminents = [];
2013-04-03 17:47:41 +00:00
for(var ip in this.ipbans) {
if(this.ipbans[ip] != null) {
2013-06-18 03:57:29 +00:00
var name = this.ipbans[ip][0];
var ip_hidden = this.hideIP(ip);
2013-04-03 17:47:41 +00:00
ents.push({
2013-06-18 03:57:29 +00:00
ip_displayed: "x.x." + ip.replace(/\d+\.\d+\.(\d+\.\d+)/, "$1"),
ip_hidden: ip_hidden,
2013-05-21 16:17:01 +00:00
name: name,
2013-06-18 03:57:29 +00:00
aliases: this.ip_alias[ip] || [],
2013-05-21 16:17:01 +00:00
banner: this.ipbans[ip][1]
});
adminents.push({
2013-06-18 03:57:29 +00:00
ip_displayed: ip,
ip_hidden: ip_hidden,
2013-05-19 21:23:35 +00:00
name: name,
2013-06-18 03:57:29 +00:00
aliases: this.ip_alias[ip] || [],
2013-04-03 17:47:41 +00:00
banner: this.ipbans[ip][1]
});
}
}
2013-05-21 16:17:01 +00:00
for(var name in this.namebans) {
if(this.namebans[name] != null) {
ents.push({
2013-06-18 03:57:29 +00:00
ip_displayed: "*",
ip_hidden: false,
2013-05-21 16:17:01 +00:00
name: name,
2013-06-18 03:57:29 +00:00
aliases: this.name_alias[name] || [],
2013-05-21 16:17:01 +00:00
banner: this.namebans[name]
});
adminents.push({
2013-06-18 03:57:29 +00:00
ip_displayed: "*",
ip_hidden: false,
2013-05-21 16:17:01 +00:00
name: name,
2013-06-18 03:57:29 +00:00
aliases: this.name_alias[name] || [],
2013-05-21 16:17:01 +00:00
banner: this.namebans[name]
});
}
}
2013-04-03 17:47:41 +00:00
for(var i = 0; i < this.users.length; i++) {
2013-05-22 19:38:16 +00:00
if(this.hasPermission(this.users[i], "ban")) {
2013-05-21 16:17:01 +00:00
if(this.users[i].rank >= Rank.Siteadmin) {
2013-06-18 03:57:29 +00:00
this.users[i].socket.emit("banlist", adminents);
2013-05-21 16:17:01 +00:00
}
else {
2013-06-18 03:57:29 +00:00
this.users[i].socket.emit("banlist", ents);
2013-05-21 16:17:01 +00:00
}
2013-04-03 17:47:41 +00:00
}
}
}
Channel.prototype.broadcastRankTable = function() {
2013-05-26 01:01:32 +00:00
var ranks = Database.listChannelRanks(this.name);
for(var i = 0; i < this.users.length; i++) {
this.sendACL(this.users[i]);
}
}
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++) {
2013-04-29 17:29:31 +00:00
filts[i] = this.filters[i].pack();
2013-04-03 17:47:41 +00:00
}
for(var i = 0; i < this.users.length; i++) {
2013-05-22 19:38:16 +00:00
if(this.hasPermission(this.users[i], "filteredit")) {
2013-06-18 14:57:53 +00:00
this.users[i].socket.emit("chatFilters", filts);
2013-04-03 17:47:41 +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;
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() {
2013-06-11 19:41:03 +00:00
this.sendAll("setMotd", this.motd);
2013-04-03 17:47:41 +00:00
}
2013-04-04 19:56:43 +00:00
Channel.prototype.broadcastDrinks = function() {
2013-06-11 15:29:21 +00:00
this.sendAll("drinkCount", this.drinks);
2013-04-04 19:56:43 +00:00
}
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-05-03 18:15:05 +00:00
chan.leader != null ||
chan.users.length == 0) {
2013-04-03 17:47:41 +00:00
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-05-04 22:54:28 +00:00
if(chan.media.currentTime > chan.media.seconds + 1) {
2013-04-03 17:47:41 +00:00
chan.playNext();
}
// Send updates about every 5 seconds
else if(chan.i % 5 == 0) {
2013-05-04 22:54:28 +00:00
chan.sendAll("mediaUpdate", chan.media.timeupdate());
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-28 22:06:58 +00:00
function isLive(type) {
2013-06-05 02:47:02 +00:00
return type == "li" // Livestream.com
|| type == "tw" // Twitch.tv
|| type == "jt" // Justin.tv
|| type == "rt" // RTMP
|| type == "jw" // JWPlayer
|| type == "us" // Ustream.tv
|| type == "im";// Imgur album
2013-04-28 22:06:58 +00:00
}
2013-05-02 15:34:12 +00:00
Channel.prototype.queueAdd = function(media, idx) {
this.queue.splice(idx, 0, media);
this.sendAll("queue", {
media: media.pack(),
pos: idx
});
this.broadcastPlaylistMeta();
if(this.queue.length == 1) {
this.playNext();
}
}
2013-05-04 22:54:28 +00:00
Channel.prototype.autoTemp = function(media, user) {
if(isLive(media.type)) {
media.temp = true;
}
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(user, "addnontemp")) {
2013-05-04 22:54:28 +00:00
media.temp = true;
}
}
2013-06-20 03:08:57 +00:00
Channel.prototype.enqueue = function(data, user, callback) {
2013-04-03 17:47:41 +00:00
var idx = data.pos == "next" ? this.position + 1 : this.queue.length;
2013-06-05 02:51:41 +00:00
if(isLive(data.type) && !this.hasPermission(user, "playlistaddlive")) {
user.socket.emit("queueFail", "You don't have permission to queue livestreams");
return;
}
2013-04-03 17:47:41 +00:00
// Prefer cache over looking up new data
if(data.id in this.library) {
2013-05-04 22:54:28 +00:00
var media = this.library[data.id].dup();
media.queueby = user ? user.name : "";
2013-05-04 22:54:28 +00:00
this.autoTemp(media, user);
2013-05-02 15:34:12 +00:00
this.queueAdd(media, idx);
2013-04-03 17:47:41 +00:00
this.logger.log("*** Queued from cache: id=" + data.id);
2013-06-20 03:08:57 +00:00
if(callback)
callback();
2013-04-03 17:47:41 +00:00
}
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":
2013-05-19 17:06:39 +00:00
InfoGetter.getMedia(data.id, data.type, function(err, media) {
if(err) {
if(callback)
callback();
2013-05-19 17:06:39 +00:00
user.socket.emit("queueFail");
return;
}
media.queueby = user ? user.name : "";
2013-05-04 22:54:28 +00:00
this.autoTemp(media, user);
2013-05-02 15:34:12 +00:00
this.queueAdd(media, idx);
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-06-20 03:08:57 +00:00
if(callback)
callback();
2013-04-03 17:47:41 +00:00
}.bind(this));
break;
case "li":
var media = new Media(data.id, "Livestream - " + data.id, "--:--", "li");
media.queueby = user ? user.name : "";
2013-05-04 22:54:28 +00:00
this.autoTemp(media, user);
2013-05-02 15:34:12 +00:00
this.queueAdd(media, idx);
2013-06-20 03:08:57 +00:00
if(callback)
callback();
2013-04-03 17:47:41 +00:00
break;
case "tw":
var media = new Media(data.id, "Twitch - " + data.id, "--:--", "tw");
media.queueby = user ? user.name : "";
2013-05-04 22:54:28 +00:00
this.autoTemp(media, user);
2013-05-02 15:34:12 +00:00
this.queueAdd(media, idx);
2013-06-20 03:08:57 +00:00
if(callback)
callback();
2013-04-03 17:47:41 +00:00
break;
2013-05-17 18:39:58 +00:00
case "jt":
var media = new Media(data.id, "JustinTV - " + data.id, "--:--", "jt");
media.queueby = user ? user.name : "";
this.autoTemp(media, user);
this.queueAdd(media, idx);
2013-06-20 03:08:57 +00:00
if(callback)
callback();
2013-05-17 18:39:58 +00:00
break;
2013-05-14 21:22:47 +00:00
case "us":
InfoGetter.getUstream(data.id, function(id) {
var media = new Media(id, "Ustream - " + data.id, "--:--", "us");
media.queueby = user ? user.name : "";
this.autoTemp(media, user);
this.queueAdd(media, idx);
2013-06-20 03:08:57 +00:00
if(callback)
callback();
2013-05-14 21:22:47 +00:00
}.bind(this));
break;
2013-04-07 19:38:50 +00:00
case "rt":
var media = new Media(data.id, "Livestream", "--:--", "rt");
media.queueby = user ? user.name : "";
2013-05-04 22:54:28 +00:00
this.autoTemp(media, user);
2013-05-02 15:34:12 +00:00
this.queueAdd(media, idx);
2013-06-20 03:08:57 +00:00
if(callback)
callback();
2013-04-07 19:38:50 +00:00
break;
2013-04-28 22:06:58 +00:00
case "jw":
var media = new Media(data.id, "JWPlayer Stream - " + data.id, "--:--", "jw");
media.queueby = user ? user.name : "";
2013-05-04 22:54:28 +00:00
this.autoTemp(media, user);
2013-05-02 15:34:12 +00:00
this.queueAdd(media, idx);
2013-06-20 03:08:57 +00:00
if(callback)
callback();
2013-04-28 22:06:58 +00:00
break;
2013-06-05 02:47:02 +00:00
case "im":
var media = new Media(data.id, "Imgur Album", "--:--", "im");
media.queueby = user ? user.name : "";
this.autoTemp(media, user);
this.queueAdd(media, idx);
2013-06-20 03:08:57 +00:00
if(callback)
callback();
2013-06-05 02:47:02 +00:00
break;
2013-04-03 17:47:41 +00:00
default:
break;
}
}
}
Channel.prototype.tryQueue = function(user, data) {
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(user, "playlistadd")) {
2013-04-03 17:47:41 +00:00
return;
}
if(typeof data.pos !== "string") {
2013-04-03 17:47:41 +00:00
return;
}
if(typeof data.id !== "string" && data.id !== false) {
return;
}
if(typeof data.type !== "string" && !(data.id in this.library)) {
2013-04-03 17:47:41 +00:00
return;
}
2013-05-22 19:38:16 +00:00
if(data.pos == "next" && !this.hasPermission(user, "playlistnext")) {
2013-04-03 17:47:41 +00:00
return;
}
2013-04-27 17:13:01 +00:00
if(user.rank < Rank.Moderator
2013-05-22 19:38:16 +00:00
&& this.leader != user
2013-04-27 17:13:01 +00:00
&& user.noflood("queue", 1.5)) {
2013-04-24 17:45:17 +00:00
return;
}
if(data.list)
this.enqueueList(data, user);
else
this.enqueue(data, user);
2013-04-03 17:47:41 +00:00
}
Channel.prototype.enqueueList = function(data, user) {
var pl = data.list;
2013-06-20 03:08:57 +00:00
var chan = this;
2013-06-01 19:42:08 +00:00
// Queue in reverse order for qnext
if(data.pos == "next") {
2013-06-20 03:08:57 +00:00
var i = pl.length;
var cback = function() {
i--;
if(i > 0) {
pl[i].pos = "next";
chan.enqueue(pl[i], user, cback);
}
2013-06-01 19:42:08 +00:00
}
2013-06-20 03:08:57 +00:00
this.enqueue(pl[0], user, cback);
2013-06-01 19:42:08 +00:00
}
else {
2013-06-20 03:08:57 +00:00
var i = 0;
var cback = function() {
i++;
if(i < pl.length) {
pl[i].pos = "end";
chan.enqueue(pl[i], user, cback);
}
2013-06-01 19:42:08 +00:00
}
2013-06-20 03:08:57 +00:00
this.enqueue(pl[i], user, cback);
2013-06-01 19:42:08 +00:00
}
}
Channel.prototype.tryQueuePlaylist = function(user, data) {
if(!this.hasPermission(user, "playlistaddlist")) {
return;
}
if(typeof data.name != "string" ||
typeof data.pos != "string") {
return;
}
if(data.pos == "next" && !this.hasPermission(user, "playlistnext")) {
return;
}
var pl = Database.loadUserPlaylist(user.name, data.name);
data.list = pl;
this.enqueueList(data, user);
}
2013-05-04 22:54:28 +00:00
Channel.prototype.setTemp = function(idx, temp) {
var med = this.queue[idx];
med.temp = temp;
this.sendAll("setTemp", {
2013-06-11 15:29:21 +00:00
position: idx,
2013-05-04 22:54:28 +00:00
temp: temp
});
if(!temp) {
2013-05-04 22:54:28 +00:00
this.cacheMedia(med);
}
}
Channel.prototype.trySetTemp = function(user, data) {
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(user, "settemp")) {
2013-05-04 22:54:28 +00:00
return;
}
2013-06-11 15:29:21 +00:00
if(typeof data.position != "number" || typeof data.temp != "boolean") {
2013-05-04 22:54:28 +00:00
return;
}
2013-06-11 15:29:21 +00:00
if(data.position < 0 || data.position >= this.queue.length) {
2013-05-04 22:54:28 +00:00
return;
}
2013-06-11 15:29:21 +00:00
this.setTemp(data.position, data.temp);
2013-05-04 22:54:28 +00:00
}
2013-06-11 15:29:21 +00:00
2013-06-11 19:41:03 +00:00
Channel.prototype.dequeue = function(position, removeonly) {
2013-06-11 15:29:21 +00:00
if(position < 0 || position >= this.queue.length) {
2013-04-03 17:47:41 +00:00
return;
}
2013-06-11 15:29:21 +00:00
this.queue.splice(position, 1);
this.sendAll("delete", {
position: position
2013-04-03 17:47:41 +00:00
});
this.broadcastPlaylistMeta();
2013-04-03 17:47:41 +00:00
2013-06-11 19:41:03 +00:00
if(removeonly)
return;
2013-04-03 17:47:41 +00:00
// If you remove the currently playing video, play the next one
2013-06-11 15:29:21 +00:00
if(position == this.position) {
2013-04-03 17:47:41 +00:00
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
2013-06-11 15:29:21 +00:00
if(position < this.position) {
2013-04-03 17:47:41 +00:00
this.position--;
}
}
Channel.prototype.tryDequeue = function(user, data) {
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(user, "playlistdelete")) {
2013-04-03 17:47:41 +00:00
return;
}
2013-06-11 15:29:21 +00:00
if(typeof data !== "number")
2013-04-03 17:47:41 +00:00
return;
this.dequeue(data);
}
Channel.prototype.tryUncache = function(user, data) {
if(!Rank.hasPermission(user, "uncache")) {
return;
}
if(typeof data.id != "string") {
return;
}
2013-05-26 01:01:32 +00:00
if(Database.removeFromLibrary(this.name, data.id)) {
delete this.library[data.id];
}
}
2013-04-03 17:47:41 +00:00
Channel.prototype.playNext = function() {
2013-05-04 22:54:28 +00:00
var pos = this.position + 1 >= this.queue.length ? 0 : this.position + 1;
this.jumpTo(pos);
2013-04-03 17:47:41 +00:00
}
Channel.prototype.tryPlayNext = function(user) {
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(user, "playlistjump")) {
2013-04-03 17:47:41 +00:00
return;
}
this.playNext();
}
Channel.prototype.jumpTo = function(pos) {
if(pos >= this.queue.length || pos < 0) {
return;
}
// Reset voteskip
this.voteskip = false;
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;
2013-05-04 22:54:28 +00:00
if(this.media && this.media.temp && old != pos) {
2013-06-11 19:41:03 +00:00
this.dequeue(old, true);
2013-05-06 16:24:55 +00:00
if(pos > old && pos > 0) {
2013-05-04 22:54:28 +00:00
pos--;
}
}
if(pos >= this.queue.length || pos < 0) {
return;
}
if(this.media) {
delete this.media["currentTime"];
2013-05-13 23:44:51 +00:00
delete this.media["paused"];
2013-05-04 22:54:28 +00:00
}
2013-04-03 17:47:41 +00:00
this.position = pos;
var oid = this.media ? this.media.id : "";
2013-04-03 17:47:41 +00:00
this.media = this.queue[this.position];
this.media.currentTime = -1;
2013-05-13 23:44:51 +00:00
this.media.paused = false;
2013-04-03 17:47:41 +00:00
2013-05-04 22:54:28 +00:00
this.sendAll("changeMedia", this.media.fullupdate());
2013-06-11 15:29:21 +00:00
this.sendAll("setPosition", this.position);
2013-04-03 17:47:41 +00:00
// If it's not a livestream, enable autolead
2013-04-28 22:06:58 +00:00
if(this.leader == null && !isLive(this.media.type)) {
2013-04-03 17:47:41 +00:00
this.time = new Date().getTime();
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-05-22 19:38:16 +00:00
if(!this.hasPermission(user, "playlistjump")) {
2013-04-03 17:47:41 +00:00
return;
}
2013-06-11 15:29:21 +00:00
if(typeof data !== "number") {
2013-04-03 17:47:41 +00:00
return;
}
2013-06-11 15:29:21 +00:00
this.jumpTo(data);
2013-04-03 17:47:41 +00:00
}
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]);
}
this.broadcastPlaylistMeta();
2013-04-22 20:37:42 +00:00
}
Channel.prototype.tryClearqueue = function(user) {
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(user, "playlistclear")) {
2013-04-22 20:37:42 +00:00
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;
2013-06-11 19:41:03 +00:00
this.sendAll("playlist", this.queue);
this.sendAll("setPosition", this.position);
this.sendAll("setPlaylistMeta", this.plmeta);
2013-04-22 20:37:42 +00:00
}
Channel.prototype.tryShufflequeue = function(user) {
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(user, "playlistshuffle")) {
2013-04-22 20:37:42 +00:00
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-06-02 23:19:51 +00:00
if(isLive(this.media.type) && this.media.type != "jw") {
2013-04-03 17:47:41 +00:00
return;
}
if(this.media.id != data.id) {
return;
}
this.media.currentTime = data.currentTime;
2013-05-13 22:05:01 +00:00
this.media.paused = data.paused;
2013-05-04 22:54:28 +00:00
this.sendAll("mediaUpdate", this.media.timeupdate());
2013-04-03 17:47:41 +00:00
}
2013-06-11 15:29:21 +00:00
Channel.prototype.move = function(data, user) {
if(data.from < 0 || data.from >= this.queue.length) {
2013-04-03 17:47:41 +00:00
return;
}
2013-06-11 15:29:21 +00:00
if(data.to < 0 || data.to > this.queue.length) {
2013-04-03 17:47:41 +00:00
return;
}
2013-06-11 15:29:21 +00:00
var media = this.queue[data.from];
var to = data.to > data.from ? data.to + 1 : data.to;
var from = data.to > data.from ? data.from : data.from + 1;
2013-06-11 19:41:03 +00:00
var moveby = user && user.name ? user.name : null;
if(typeof data.moveby !== "undefined")
moveby = data.moveby;
2013-04-03 17:47:41 +00:00
2013-06-11 15:29:21 +00:00
this.queue.splice(to, 0, media);
this.queue.splice(from, 1);
2013-04-03 17:47:41 +00:00
this.sendAll("moveVideo", {
2013-06-11 15:29:21 +00:00
from: data.from,
to: data.to,
2013-06-11 19:41:03 +00:00
moveby: moveby
2013-04-03 17:47:41 +00:00
});
// Account for moving things around the active video
2013-06-11 15:29:21 +00:00
if(data.from < this.position && data.to >= this.position) {
2013-04-03 17:47:41 +00:00
this.position--;
}
2013-06-11 15:29:21 +00:00
else if(data.from > this.position && data.to < this.position) {
2013-04-03 17:47:41 +00:00
this.position++
}
2013-06-11 15:29:21 +00:00
else if(data.from == this.position) {
this.position = data.to;
2013-04-03 17:47:41 +00:00
}
}
Channel.prototype.tryMove = function(user, data) {
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(user, "playlistmove")) {
2013-04-03 17:47:41 +00:00
return;
2013-05-22 19:38:16 +00:00
}
2013-04-03 17:47:41 +00:00
2013-06-11 19:41:03 +00:00
if(typeof data.from !== "number" || typeof data.to !== "number") {
2013-04-03 17:47:41 +00:00
return;
}
2013-06-11 19:41:03 +00:00
this.move(data, user);
2013-04-03 17:47:41 +00:00
}
/* REGION Polls */
Channel.prototype.tryOpenPoll = function(user, data) {
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(user, "pollctl") && 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-05-22 19:38:16 +00:00
if(!this.hasPermission(user, "pollctl")) {
2013-04-03 17:47:41 +00:00
return;
}
if(this.poll) {
this.poll = false;
this.broadcastPollClose();
}
}
Channel.prototype.tryVote = function(user, data) {
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(user, "pollvote")) {
return;
}
2013-06-19 23:39:40 +00:00
if(typeof data.option !== "number") {
2013-04-03 17:47:41 +00:00
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);
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;
2013-06-11 15:29:21 +00:00
this.sendAll("setPlaylistLocked", {locked: locked});
2013-04-03 17:47:41 +00:00
}
Channel.prototype.trySetLock = function(user, data) {
if(!Rank.hasPermission(user, "qlock")) {
return;
}
2013-04-03 17:47:41 +00:00
if(data.locked == undefined) {
return;
}
this.setLock(data.locked);
}
2013-06-11 15:29:21 +00:00
Channel.prototype.tryToggleLock = function(user) {
if(!Rank.hasPermission(user, "qlock")) {
return;
}
this.setLock(this.openqueue);
}
2013-06-18 15:51:42 +00:00
Channel.prototype.tryRemoveFilter = function(user, f) {
if(!this.hasPermission(user, "filteredit"))
return false;
this.removeFilter(f);
}
Channel.prototype.removeFilter = function(filter) {
2013-04-03 17:47:41 +00:00
for(var i = 0; i < this.filters.length; i++) {
2013-06-18 15:51:42 +00:00
if(this.filters[i].name == filter.name) {
this.filters.splice(i, 1);
break;
}
}
this.broadcastChatFilters();
}
2013-06-18 15:51:42 +00:00
Channel.prototype.updateFilter = function(filter) {
if(filter.name == "")
filter.name = filter.source;
var found = false;
for(var i = 0; i < this.filters.length; i++) {
2013-06-18 15:51:42 +00:00
if(this.filters[i].name == filter.name) {
found = true;
this.filters[i] = filter;
break;
}
}
2013-06-18 15:51:42 +00:00
if(!found) {
this.filters.push(filter);
}
this.broadcastChatFilters();
}
2013-06-18 15:51:42 +00:00
Channel.prototype.tryUpdateFilter = function(user, f) {
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(user, "filteredit")) {
2013-04-03 17:47:41 +00:00
return;
}
2013-06-18 15:51:42 +00:00
var re = f.source;
var flags = f.flags;
try {
new RegExp(re, flags);
}
catch(e) {
2013-04-03 17:47:41 +00:00
return;
}
2013-06-18 15:51:42 +00:00
var filter = new Filter(f.name, f.source, f.flags, f.replace);
filter.active = f.active;
filter.filterlinks = f.filterlinks;
this.updateFilter(filter);
}
2013-04-03 17:47:41 +00:00
2013-06-18 15:51:42 +00:00
Channel.prototype.moveFilter = function(data) {
if(data.from < 0 || data.to < 0 || data.from >= this.filters.length ||
data.to > this.filters.length) {
return;
2013-04-03 17:47:41 +00:00
}
2013-06-18 15:51:42 +00:00
var f = this.filters[data.from];
var to = data.to > data.from ? data.to + 1 : data.to;
var from = data.to > data.from ? data.from : data.from + 1;
this.filters.splice(to, 0, f);
this.filters.splice(from, 1);
this.broadcastChatFilters();
}
Channel.prototype.tryMoveFilter = function(user, data) {
if(!this.hasPermission(user, "filteredit"))
return;
if(typeof data.to !== "number" || typeof data.from !== "number")
return;
this.moveFilter(data);
2013-04-03 17:47:41 +00:00
}
2013-05-22 19:38:16 +00:00
Channel.prototype.tryUpdatePermissions = function(user, perms) {
if(!Rank.hasPermission(user, "channelperms")) {
return;
}
for(var key in perms) {
this.permissions[key] = perms[key];
}
this.sendAll("setPermissions", this.permissions);
}
2013-04-03 17:47:41 +00:00
Channel.prototype.tryUpdateOptions = function(user, data) {
if(!Rank.hasPermission(user, "channelOpts")) {
return;
}
const adminonly = {
2013-05-01 22:49:34 +00:00
pagetitle: true,
2013-06-20 18:43:37 +00:00
externalcss: true,
externaljs: true,
2013-05-01 22:49:34 +00:00
show_public: true
};
2013-04-03 17:47:41 +00:00
for(var key in this.opts) {
if(key in data) {
if(key in adminonly && user.rank < Rank.Owner) {
continue;
}
2013-04-03 17:47:41 +00:00
this.opts[key] = data[key];
}
}
2013-04-03 17:47:41 +00:00
this.broadcastOpts();
}
2013-05-15 15:34:27 +00:00
Channel.prototype.trySetCSS = function(user, data) {
if(!Rank.hasPermission(user, "setcss")) {
return;
}
var css = data.css || "";
if(css.length > 20000) {
css = css.substring(0, 20000);
}
this.css = css;
this.sendAll("channelCSSJS", {
css: this.css,
js: this.js
});
}
Channel.prototype.trySetJS = function(user, data) {
if(!Rank.hasPermission(user, "setjs")) {
return;
}
var js = data.js || "";
if(js.length > 20000) {
js = js.substring(0, 20000);
}
this.js = js;
this.sendAll("channelCSSJS", {
css: this.css,
js: this.js
});
}
2013-04-03 17:47:41 +00:00
Channel.prototype.updateMotd = function(motd) {
var html = motd.replace(/\n/g, "<br>");
2013-05-12 15:48:41 +00:00
//html = this.filterMessage(html);
2013-04-03 17:47:41 +00:00
this.motd = {
motd: motd,
html: html
};
this.broadcastMotd();
}
Channel.prototype.tryUpdateMotd = function(user, data) {
2013-05-22 19:38:16 +00:00
if(!this.hasPermission(user, "motdedit")) {
2013-04-03 17:47:41 +00:00
return;
}
2013-04-28 22:10:00 +00:00
data.motd = data.motd || "";
this.updateMotd(data.motd);
2013-04-03 17:47:41 +00:00
}
/* REGION Chat */
Channel.prototype.tryChat = function(user, data) {
if(user.name == "") {
return;
}
2013-06-20 19:02:53 +00:00
if(!this.hasPermission(user, "chat"))
return;
if(typeof data.msg !== "string") {
2013-04-03 17:47:41 +00:00
return;
}
var msg = data.msg;
2013-04-30 15:30:59 +00:00
if(msg.length > 240) {
msg = msg.substring(0, 240);
}
2013-04-27 17:13:01 +00:00
if(this.opts.chat_antiflood && user.noflood("chat", 2.0)) {
return;
}
2013-04-03 17:47:41 +00:00
2013-04-28 18:04:15 +00:00
this.chainMessage(user, msg);
}
Channel.prototype.chainMessage = function(user, msg, data) {
2013-03-05 18:51:58 +00:00
if(msg.indexOf("/") == 0)
2013-04-28 18:04:15 +00:00
ChatCommand.handle(this, user, msg, data);
2013-03-05 18:51:58 +00:00
2013-02-16 05:02:42 +00:00
else if(msg.indexOf(">") == 0)
2013-04-28 18:04:15 +00:00
this.sendMessage(user.name, msg, "greentext", data);
2013-03-05 18:51:58 +00:00
else
2013-04-28 18:04:15 +00:00
this.sendMessage(user.name, msg, "", data);
2013-03-05 18:51:58 +00:00
}
2013-04-01 21:02:09 +00:00
Channel.prototype.filterMessage = function(msg) {
2013-05-27 18:32:24 +00:00
const link = /((?:(?:https?)|(?:ftp))(?::\/\/[0-9a-zA-Z\.]+(?::[0-9]+)?[^\s'"$]+))/g;
2013-05-13 23:54:52 +00:00
var subs = msg.split(link);
2013-03-29 18:15:46 +00:00
// Apply other filters
2013-05-13 23:54:52 +00:00
for(var j = 0; j < subs.length; j++) {
2013-06-05 20:49:54 +00:00
if(this.opts.enable_link_regex && subs[j].match(link)) {
2013-05-27 18:32:24 +00:00
subs[j] = subs[j].replace(link, "<a href=\"$1\" target=\"_blank\">$1</a>");
2013-06-18 15:51:42 +00:00
for(var i = 0; i < this.filters.length; i++) {
if(!this.filters[i].filterlinks || !this.filters[i].active)
continue;
subs[j] = this.filters[i].filter(subs[j]);
}
2013-03-29 18:15:46 +00:00
continue;
2013-05-13 23:54:52 +00:00
}
for(var i = 0; i < this.filters.length; i++) {
if(!this.filters[i].active)
continue;
subs[j] = this.filters[i].filter(subs[j]);
}
2013-03-29 18:15:46 +00:00
}
2013-05-13 23:54:52 +00:00
return subs.join("");
2013-04-01 21:02:09 +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, "&lt;").replace(/>/g, "&gt;");
msg = this.filterMessage(msg);
var msgobj = {
2013-03-05 18:51:58 +00:00
username: username,
2013-02-16 05:02:42 +00:00
msg: msg,
msgclass: msgclass,
time: Date.now()
};
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.trySetRank = function(user, data) {
if(!Rank.hasPermission(user, "promote"))
return;
if(typeof data.user !== "string" || typeof data.rank !== "number")
return;
if(data.rank >= user.rank)
return;
if(data.rank < 1)
return;
var receiver;
for(var i = 0; i < this.users.length; i++) {
if(this.users[i].name == data.user) {
receiver = this.users[i];
break;
}
}
if(receiver) {
if(receiver.rank >= user.rank)
return;
receiver.rank = data.rank;
if(receiver.loggedIn) {
this.saveRank(receiver);
}
this.broadcastUserUpdate(receiver);
}
else {
var rrank = this.getRank(data.user);
if(rrank >= user.rank)
return;
Database.setChannelRank(this.name, data.user, data.rank);
}
this.sendAllWithPermission("acl", "setChannelRank", data);
}
2013-04-03 17:47:41 +00:00
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;
}
}
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.broadcastUserUpdate(receiver);
2013-02-16 05:02:42 +00:00
}
else {
2013-05-26 01:01:32 +00:00
Database.setChannelRank(this.name, data.name, 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;
}
}
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.broadcastUserUpdate(receiver);
2013-02-16 05:02:42 +00:00
}
else {
2013-05-26 01:01:32 +00:00
Database.setChannelRank(this.name, data.name, 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-05-22 19:38:16 +00:00
if(old.rank == 1.5) {
old.rank = old.oldrank;
}
this.broadcastUserUpdate(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-28 22:06:58 +00:00
if(this.media != null && !isLive(this.media.type)) {
2013-05-23 04:03:37 +00:00
this.media.paused = false;
this.time = new Date().getTime();
this.i = 0;
2013-04-03 17:47:41 +00:00
mediaUpdate(this, this.media.id);
}
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];
2013-05-22 19:38:16 +00:00
if(this.users[i].rank < 1.5) {
this.users[i].oldrank = this.users[i].rank;
this.users[i].rank = 1.5;
}
this.broadcastUserUpdate(this.leader);
2013-02-16 05:02:42 +00:00
}
}
}
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;