diff --git a/acp.js b/acp.js
index 899a058a..e5993ab6 100644
--- a/acp.js
+++ b/acp.js
@@ -150,6 +150,12 @@ module.exports = function (Server) {
}
});
+ user.socket.on("acp-actionlog-list", function () {
+ user.socket.emit("acp-actionlog-list",
+ ActionLog.getLogTypes()
+ );
+ });
+
user.socket.on("acp-actionlog-clear", function(data) {
ActionLog.clear(data);
ActionLog.record(user.ip, user.name, "acp-actionlog-clear", data);
diff --git a/actionlog.js b/actionlog.js
index e5c18df4..312a2f74 100644
--- a/actionlog.js
+++ b/actionlog.js
@@ -105,12 +105,42 @@ exports.tooManyRegistrations = function (ip) {
return rows.length > 4;
}
-exports.readLog = function () {
+exports.getLogTypes = function () {
+ var db = Database.getConnection();
+ if(!db)
+ return false;
+
+ var query = "SELECT DISTINCT action FROM actionlog";
+ var result = db.querySync(query);
+ if(!result) {
+ Logger.errlog.log("! Failed to read action log");
+ return [];
+ }
+
+ result = result.fetchAllSync();
+ var actions = [];
+ for(var i in result)
+ actions.push(result[i].action);
+
+ return actions;
+}
+
+exports.readLog = function (actions) {
var db = Database.getConnection();
if(!db)
return false;
var query = "SELECT * FROM actionlog";
+ if(actions !== undefined) {
+ var list = new Array(actions.length);
+ for(var i in actions)
+ list[i] = "?";
+ list = list.join(",");
+ query += Database.createQuery(
+ " WHERE action IN ("+list+")",
+ actions
+ );
+ }
var result = db.querySync(query);
if(!result) {
Logger.errlog.log("! Failed to read action log");
diff --git a/api.js b/api.js
index 7b9c38e8..3f9dfc25 100644
--- a/api.js
+++ b/api.js
@@ -12,22 +12,22 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
var Auth = require("./auth");
var Logger = require("./logger");
var apilog = new Logger.Logger("api.log");
-var Config = require("./config");
var ActionLog = require("./actionlog");
var fs = require("fs");
-function getIP(req) {
- var raw = req.connection.remoteAddress;
- var forward = req.header("x-forwarded-for");
- if(Config.REVERSE_PROXY && forward) {
- var ip = forward.split(",")[0];
- Logger.syslog.log("REVPROXY " + raw + " => " + ip);
- return ip;
- }
- return raw;
-}
module.exports = function (Server) {
+ function getIP(req) {
+ var raw = req.connection.remoteAddress;
+ var forward = req.header("x-forwarded-for");
+ if(Server.cfg["trust-x-forward"] && forward) {
+ var ip = forward.split(",")[0];
+ Logger.syslog.log("REVPROXY " + raw + " => " + ip);
+ return ip;
+ }
+ return raw;
+ }
+
var API = function () {
}
@@ -189,7 +189,8 @@ module.exports = function (Server) {
var row = Auth.login(name, pw, session);
if(row) {
- ActionLog.record(getIP(req), name, "login-success");
+ if(row.global_rank >= 255)
+ ActionLog.record(getIP(req), name, "login-success");
this.sendJSON(res, {
success: true,
session: row.session_hash
@@ -251,7 +252,7 @@ module.exports = function (Server) {
return;
}
- if(!Config.MAIL) {
+ if(!Server.cfg["enable-mail"]) {
this.sendJSON(res, {
success: false,
error: "This server does not have email enabled. Contact an administrator"
@@ -269,24 +270,24 @@ module.exports = function (Server) {
"A password reset request was issued for your account `",
name,
"` on ",
- Config.DOMAIN,
+ Server.cfg["domain"],
". This request is valid for 24 hours. ",
"If you did not initiate this, there is no need to take action. ",
"To reset your password, copy and paste the following link into ",
"your browser: ",
- Config.DOMAIN,
+ Server.cfg["domain"],
"/reset.html?",
hash
].join("");
var mail = {
- from: "CyTube Services <" + Config.MAIL_FROM + ">",
+ from: "CyTube Services <" + Server.cfg["mail-from"] + ">",
to: email,
subject: "Password reset request",
text: msg
};
var api = this;
- Config.MAIL.sendMail(mail, function(err, response) {
+ Server.cfg["nodemailer"].sendMail(mail, function(err, response) {
if(err) {
Logger.errlog.log("Mail fail: " + err);
api.sendJSON(res, {
@@ -299,7 +300,7 @@ module.exports = function (Server) {
success: true
});
- if(Config.DEBUG) {
+ if(Server.cfg["debug"]) {
Logger.syslog.log(response);
}
}
@@ -317,12 +318,12 @@ module.exports = function (Server) {
name: info[0],
pw: info[1]
});
- ActionLog.record(ip, name, "password-recover-success");
- Logger.syslog.log(ip + " recovered password for " + name);
+ ActionLog.record(ip, info[0], "password-recover-success");
+ Logger.syslog.log(ip + " recovered password for " + info[0]);
return;
}
catch(e) {
- ActionLog.record(ip, name, "password-recover-failure");
+ ActionLog.record(ip, "", "password-recover-failure");
this.sendJSON(res, {
success: false,
error: e
@@ -510,13 +511,15 @@ module.exports = function (Server) {
var name = params.name || "";
var pw = params.pw || "";
var session = params.session || "";
+ var types = params.actions || "";
var row = Auth.login(name, pw, session);
if(!row || row.global_rank < 255) {
res.send(403);
return;
}
- var actions = ActionLog.readLog();
+ var actiontypes = types.split(",");
+ var actions = ActionLog.readLog(actiontypes);
this.sendJSON(res, actions);
},
diff --git a/auth.js b/auth.js
index 171c7532..89fb8efc 100644
--- a/auth.js
+++ b/auth.js
@@ -11,7 +11,6 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
var mysql = require("mysql-libmysqlclient");
var Database = require("./database.js");
-var Config = require("./config.js");
var bcrypt = require("bcrypt");
var hashlib = require("node_hash");
var Logger = require("./logger.js");
diff --git a/channel.js b/channel.js
index cca26a27..8881597b 100644
--- a/channel.js
+++ b/channel.js
@@ -34,6 +34,7 @@ var Channel = function(name, Server) {
// Initialize defaults
this.registered = false;
this.users = [];
+ this.afkcount = 0;
this.playlist = new Playlist(this);
this.library = {};
this.position = -1;
@@ -76,6 +77,7 @@ var Channel = function(name, Server) {
this.opts = {
allow_voteskip: true,
voteskip_ratio: 0.5,
+ afk_timeout: 180,
pagetitle: this.name,
maxlength: 0,
externalcss: "",
@@ -201,6 +203,9 @@ Channel.prototype.loadDump = function() {
}
this.sendAll("setPermissions", this.permissions);
this.broadcastOpts();
+ this.users.forEach(function (u) {
+ u.autoAFK();
+ });
if(data.filters) {
for(var i = 0; i < data.filters.length; i++) {
var f = data.filters[i];
@@ -263,7 +268,6 @@ Channel.prototype.saveDump = function() {
};
var text = JSON.stringify(dump);
fs.writeFileSync("chandump/" + this.name, text);
- this.logger.flush();
}
// Save channel dumps every 5 minutes, in case of crash
@@ -958,6 +962,7 @@ Channel.prototype.broadcastChatFilters = function() {
Channel.prototype.broadcastVoteskipUpdate = function() {
var amt = this.voteskip ? this.voteskip.counts[0] : 0;
var need = this.voteskip ? parseInt(this.users.length * this.opts.voteskip_ratio) : 0;
+ need -= this.afkcount;
for(var i = 0; i < this.users.length; i++) {
if(Rank.hasPermission(this.users[i], "seeVoteskip") ||
this.leader == this.users[i]) {
@@ -1536,12 +1541,18 @@ Channel.prototype.tryVoteskip = function(user) {
if(!this.opts.allow_voteskip) {
return;
}
+ // Voteskip = auto-unafk
+ if(user.meta.afk) {
+ user.setAFK(false);
+ }
if(!this.voteskip) {
this.voteskip = new Poll("voteskip", "voteskip", ["yes"]);
}
this.voteskip.vote(user.ip, 0);
this.broadcastVoteskipUpdate();
- if(this.voteskip.counts[0] >= parseInt(this.users.length * this.opts.voteskip_ratio)) {
+ var need = parseInt(this.users.length * this.opts.voteskip_ratio);
+ need -= this.afkcount;
+ if(this.voteskip.counts[0] >= need) {
this.playNext();
}
}
@@ -1681,6 +1692,11 @@ Channel.prototype.tryUpdateOptions = function(user, data) {
if(key in adminonly && user.rank < Rank.Owner) {
continue;
}
+ if(key === "afk_timeout" && this.opts[key] != data[key]) {
+ this.users.forEach(function (u) {
+ u.autoAFK();
+ });
+ }
this.opts[key] = data[key];
}
}
@@ -1829,7 +1845,8 @@ Channel.prototype.sendMessage = function(username, msg, msgclass, data) {
this.chatbuffer.push(msgobj);
if(this.chatbuffer.length > 15)
this.chatbuffer.shift();
- this.logger.log("<" + username + "." + msgclass + "> " + msg);
+ var unescaped = sanitize(msg).entityDecode();
+ this.logger.log("<" + username + "." + msgclass + "> " + unescaped);
};
/* REGION Rank stuff */
diff --git a/chatcommand.js b/chatcommand.js
index 9c7233b1..388bee42 100644
--- a/chatcommand.js
+++ b/chatcommand.js
@@ -24,8 +24,7 @@ function handle(chan, user, msg, data) {
}
}
else if(msg.indexOf("/afk") == 0) {
- user.meta.afk = !user.meta.afk;
- chan.broadcastUserUpdate(user);
+ user.setAFK(!user.meta.afk);
}
else if(msg.indexOf("/m ") == 0) {
if(user.rank >= Rank.Moderator) {
diff --git a/config.js b/config.js
index 3e18c403..f313b27c 100644
--- a/config.js
+++ b/config.js
@@ -9,38 +9,88 @@ The above copyright notice and this permission notice shall be included in all c
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.
*/
-exports.MYSQL_SERVER = "";
-exports.MYSQL_DB = "";
-exports.MYSQL_USER = "";
-exports.MYSQL_PASSWORD = "";
-exports.IO_PORT = 1337; // Socket.IO port, DO NOT USE PORT 80.
-exports.WEBSERVER_PORT = 8080; // Webserver port. Binding port 80 requires root permissions
-exports.MAX_PER_IP = 10;
-exports.GUEST_LOGIN_DELAY = 60; // Seconds
-
-/*
- Set to true if your IO_URL and WEB_URL are behind a reverse proxy
- (e.g. Cloudflare) so that client IPs are passed through correctly.
-
- If you are not behind a reverse proxy, leave it as false, otherwise
- clients can fake their IP address in the x-forwarded-for header
-*/
-exports.REVERSE_PROXY = false;
-
+var fs = require("fs");
+var Logger = require("./logger");
var nodemailer = require("nodemailer");
-exports.MAIL = false;
-/* Example for setting up email:
-exports.MAIL = nodemailer.createTransport("SMTP", {
- service: "Gmail",
- auth: {
- user: "some.user@gmail.com",
- pass: "supersecretpassword"
- }
-});
-See https://github.com/andris9/Nodemailer
-*/
-exports.MAIL_FROM = "some.user@gmail.com";
-// Domain for password reset link
-// Email sent goes to exports.DOMAIN/reset.html?resethash
-exports.DOMAIN = "http://localhost";
+var defaults = {
+ "mysql-server" : "localhost",
+ "mysql-db" : "cytube",
+ "mysql-user" : "cytube",
+ "mysql-pw" : "supersecretpass",
+ "express-host" : "0.0.0.0",
+ "asset-cache-ttl" : 0,
+ "web-port" : 8080,
+ "io-port" : 1337,
+ "ip-connection-limit" : 10,
+ "guest-login-delay" : 60,
+ "trust-x-forward" : false,
+ "enable-mail" : false,
+ "mail-transport" : "SMTP",
+ "mail-config" : {
+ "service" : "Gmail",
+ "auth" : {
+ "user" : "some.user@gmail.com",
+ "pass" : "supersecretpassword"
+ }
+ },
+ "mail-from" : "some.user@gmail.com",
+ "domain" : "http://localhost"
+}
+
+function save(cfg, file) {
+ var x = {};
+ for(var k in cfg) {
+ if(k !== "nodemailer")
+ x[k] = cfg[k];
+ }
+ fs.writeFile(file, JSON.stringify(x, null, 4), function (err) {
+ if(err) {
+ Logger.errlog.log("Failed to save config");
+ Logger.errlog.log(err);
+ }
+ });
+}
+
+exports.load = function (Server, file, callback) {
+ var cfg = {};
+ for(var k in defaults)
+ cfg[k] = defaults[k];
+
+ fs.readFile(file, function (err, data) {
+ if(err) {
+ if(err.code == "ENOENT") {
+ Logger.syslog.log("Config file not found, generating default");
+ Logger.syslog.log("Edit cfg.json to configure");
+ data = "{}";
+ }
+ else {
+ Logger.errlog.log("Config load failed");
+ Logger.errlog.log(err);
+ return;
+ }
+ }
+
+ try {
+ data = JSON.parse(data + "");
+ } catch(e) {
+ Logger.errlog.log("Config JSON is invalid: ");
+ Logger.errlog.log(e);
+ return;
+ }
+
+ for(var k in data)
+ cfg[k] = data[k];
+
+ if(cfg["enable-mail"]) {
+ cfg["nodemailer"] = nodemailer.createTransport(
+ cfg["mail-transport"],
+ cfg["mail-config"]
+ );
+ }
+
+ save(cfg, file);
+ Server.cfg = cfg;
+ callback();
+ });
+}
diff --git a/database.js b/database.js
index f1068610..c57bebac 100644
--- a/database.js
+++ b/database.js
@@ -24,10 +24,10 @@ var CONFIG = {};
var global_bans = {};
function setup(cfg) {
- SERVER = cfg.MYSQL_SERVER;
- USER = cfg.MYSQL_USER;
- DATABASE = cfg.MYSQL_DB;
- PASSWORD = cfg.MYSQL_PASSWORD;
+ SERVER = cfg["mysql-server"];
+ USER = cfg["mysql-user"];
+ DATABASE = cfg["mysql-db"];
+ PASSWORD = cfg["mysql-pw"];
CONFIG = cfg;
}
@@ -41,7 +41,7 @@ function getConnection() {
Logger.errlog.log("DB connection failed");
return false;
}
- if(CONFIG.DEBUG) {
+ if(CONFIG["debug"]) {
db._querySync = db.querySync;
db.querySync = function(q) {
Logger.syslog.log("DEBUG: " + q);
@@ -900,6 +900,8 @@ function saveUserPlaylist(pl, user, name) {
for(var i = 0; i < pl.length; i++) {
var e = {
id: pl[i].media.id,
+ title: pl[i].media.title,
+ seconds: pl[i].media.seconds,
type: pl[i].media.type
};
time += pl[i].media.seconds;
diff --git a/logger.js b/logger.js
index f1129336..85d5748d 100644
--- a/logger.js
+++ b/logger.js
@@ -18,28 +18,37 @@ function getTimeString() {
var Logger = function(filename) {
this.filename = filename;
- this.buffer = [];
-
- setInterval(function() {
- this.flush();
- }.bind(this), 15000);
+ this.writer = fs.createWriteStream(filename, {
+ flags: "a",
+ encoding: "utf-8"
+ });
}
-Logger.prototype.log = function(what) {
- this.buffer.push("[" + getTimeString() + "] " + what);
-}
+Logger.prototype.log = function () {
+ var msg = "";
+ for(var i in arguments)
+ msg += arguments[i];
-Logger.prototype.flush = function() {
- if(this.buffer.length == 0)
+ if(this.dead) {
return;
- var text = this.buffer.join("\n") + "\n";
- this.buffer = [];
- fs.appendFile(this.filename, text, function(err) {
- if(err) {
- errlog.log("Append to " + this.filename + " failed: ");
- errlog.log(err);
- }
- }.bind(this));
+ }
+
+ var str = "[" + getTimeString() + "] " + msg + "\n";
+ try {
+ this.writer.write(str);
+ } catch(e) {
+ errlog.log("WARNING: Attempted logwrite failed: " + this.filename);
+ errlog.log("Message was: " + msg);
+ errlog.log(e);
+ }
+}
+
+Logger.prototype.close = function () {
+ try {
+ this.writer.end();
+ } catch(e) {
+ errlog.log("Log close failed: " + this.filename);
+ }
}
var errlog = new Logger("error.log");
diff --git a/package.json b/package.json
index e7dde87a..cc33e28a 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"author": "Calvin Montgomery",
"name": "CyTube",
"description": "Online media synchronizer and chat",
- "version": "2.1.2",
+ "version": "2.1.4",
"repository": {
"url": "http://github.com/calzoneman/sync"
},
diff --git a/playlist.js b/playlist.js
index e6bfd727..f6f412f9 100644
--- a/playlist.js
+++ b/playlist.js
@@ -239,6 +239,19 @@ Playlist.prototype.addMedia = function(data, callback) {
};
this.queueAction(action);
+ // Pre-cached data
+ if(typeof data.title === "string" &&
+ typeof data.seconds === "number") {
+ if(data.maxlength && data.seconds > data.maxlength) {
+ action.expire = 0;
+ callback("Media is too long!", null);
+ return;
+ }
+ it.media = new Media(data.id, data.title, data.seconds, data.type);
+ action.waiting = false;
+ return;
+ }
+
InfoGetter.getMedia(data.id, data.type, function(err, media) {
if(err) {
action.expire = 0;
diff --git a/server.js b/server.js
index 54d71fc3..d0648cd2 100644
--- a/server.js
+++ b/server.js
@@ -1,16 +1,17 @@
var path = require("path");
+var fs = require("fs");
var express = require("express");
var Config = require("./config");
var Logger = require("./logger");
var Channel = require("./channel");
var User = require("./user");
-const VERSION = "2.1.2";
+const VERSION = "2.1.4";
function getIP(req) {
var raw = req.connection.remoteAddress;
var forward = req.header("x-forwarded-for");
- if(Config.REVERSE_PROXY && forward) {
+ if(Server.cfg["trust-x-forward"] && forward) {
var ip = forward.split(",")[0];
Logger.syslog.log("REVPROXY " + raw + " => " + ip);
return ip;
@@ -20,7 +21,7 @@ function getIP(req) {
function getSocketIP(socket) {
var raw = socket.handshake.address.address;
- if(Config.REVERSE_PROXY) {
+ if(Server.cfg["trust-x-forward"]) {
if(typeof socket.handshake.headers["x-forwarded-for"] == "string") {
var ip = socket.handshake.headers["x-forwarded-for"]
.split(",")[0];
@@ -54,6 +55,7 @@ var Server = {
if(chan.registered)
chan.saveDump();
chan.playlist.die();
+ chan.logger.close();
for(var i in this.channels) {
if(this.channels[i].canonical_name == chan.canonical_name) {
this.channels.splice(i, 1);
@@ -70,34 +72,57 @@ var Server = {
db: null,
ips: {},
acp: null,
+ httpaccess: null,
+ logHTTP: function (req, status) {
+ if(status === undefined)
+ status = 200;
+ var ip = req.connection.remoteAddress;
+ var ip2 = false;
+ if(this.cfg["trust-x-forward"])
+ ip2 = req.header("x-forwarded-for") || req.header("cf-connecting-ip");
+ var ipstr = !ip2 ? ip : ip + " (X-Forwarded-For " + ip2 + ")";
+ var url = req.url;
+ // Remove query
+ if(url.indexOf("?") != -1)
+ url = url.substring(0, url.lastIndexOf("?"));
+ this.httpaccess.log([ipstr, req.method, url, status, req.headers["user-agent"]].join(" "));
+ },
init: function () {
+ this.httpaccess = new Logger.Logger("httpaccess.log");
this.app = express();
// channel path
this.app.get("/r/:channel(*)", function (req, res, next) {
var c = req.params.channel;
- if(!c.match(/^[\w-_]+$/))
+ if(!c.match(/^[\w-_]+$/)) {
res.redirect("/" + c);
- else
+ }
+ else {
+ this.logHTTP(req);
res.sendfile(__dirname + "/www/channel.html");
- });
+ }
+ }.bind(this));
// api path
this.api = require("./api")(this);
this.app.get("/api/:apireq(*)", function (req, res, next) {
+ this.logHTTP(req);
this.api.handle(req.url.substring(5), req, res);
}.bind(this));
this.app.get("/", function (req, res, next) {
+ this.logHTTP(req);
res.sendfile(__dirname + "/www/index.html");
- });
+ }.bind(this));
// default path
this.app.get("/:thing(*)", function (req, res, next) {
var opts = {
root: __dirname + "/www",
+ maxAge: this.cfg["asset-cache-ttl"]
}
res.sendfile(req.params.thing, opts, function (err) {
if(err) {
+ this.logHTTP(req, err.status);
// Damn path traversal attacks
if(req.params.thing.indexOf("%2e") != -1) {
res.send("Don't try that again, I'll ban you");
@@ -113,21 +138,27 @@ var Server = {
res.send(err.status);
}
}
- });
- });
+ else {
+ this.logHTTP(req);
+ }
+ }.bind(this));
+ }.bind(this));
// fallback
this.app.use(function (err, req, res, next) {
+ this.logHTTP(req, err.status);
if(err.status == 404) {
res.send(404);
} else {
next(err);
}
- });
+ }.bind(this));
// bind servers
- this.httpserv = this.app.listen(Config.WEBSERVER_PORT);
- this.ioserv = express().listen(Config.IO_PORT);
+ this.httpserv = this.app.listen(Server.cfg["web-port"],
+ Server.cfg["express-host"]);
+ this.ioserv = express().listen(Server.cfg["io-port"],
+ Server.cfg["express-host"]);
// init socket.io
this.io = require("socket.io").listen(this.ioserv);
@@ -152,7 +183,7 @@ var Server = {
this.ips[ip] = 0;
this.ips[ip]++;
- if(this.ips[ip] > Config.MAX_PER_IP) {
+ if(this.ips[ip] > Server.cfg["ip-connection-limit"]) {
socket.emit("kick", {
reason: "Too many connections from your IP address"
});
@@ -167,7 +198,7 @@ var Server = {
// init database
this.db = require("./database");
- this.db.setup(Config);
+ this.db.setup(Server.cfg);
this.db.init();
// init ACP
@@ -190,15 +221,25 @@ var Server = {
};
Logger.syslog.log("Starting CyTube v" + VERSION);
-Server.init();
-if(!Config.DEBUG) {
- process.on("uncaughtException", function (err) {
- Logger.errlog.log("[SEVERE] Uncaught Exception: " + err);
- Logger.errlog.log(err.stack);
- });
+fs.exists("chanlogs", function (exists) {
+ exists || fs.mkdir("chanlogs");
+});
- process.on("SIGINT", function () {
- Server.shutdown();
- });
-}
+fs.exists("chandump", function (exists) {
+ exists || fs.mkdir("chandump");
+});
+
+Config.load(Server, "cfg.json", function () {
+ Server.init();
+ if(!Server.cfg["debug"]) {
+ process.on("uncaughtException", function (err) {
+ Logger.errlog.log("[SEVERE] Uncaught Exception: " + err);
+ Logger.errlog.log(err.stack);
+ });
+
+ process.on("SIGINT", function () {
+ Server.shutdown();
+ });
+ }
+});
diff --git a/user.js b/user.js
index 9207ebe6..d5637b67 100644
--- a/user.js
+++ b/user.js
@@ -14,7 +14,6 @@ var Auth = require("./auth.js");
var Channel = require("./channel.js").Channel;
var formatTime = require("./media.js").formatTime;
var Logger = require("./logger.js");
-var Config = require("./config.js");
var ActionLog = require("./actionlog");
// Represents a client connected via socket.io
@@ -38,6 +37,8 @@ var User = function(socket, Server) {
image: "",
text: ""
};
+ this.awaytimer = false;
+ this.autoAFK();
this.initCallbacks();
if(Server.announcement != null) {
@@ -79,6 +80,46 @@ User.prototype.noflood = function(name, hz) {
}
}
+User.prototype.setAFK = function (afk) {
+ if(this.channel === null)
+ return;
+ var changed = this.meta.afk != afk;
+ var chan = this.channel;
+ this.meta.afk = afk;
+ if(!afk)
+ this.autoAFK();
+ if(changed) {
+ if(this.meta.afk)
+ chan.afkcount++;
+ else
+ chan.afkcount--;
+ }
+ if(chan.voteskip) {
+ chan.voteskip.unvote(this.ip);
+ var need = parseInt(chan.users.length * chan.opts.voteskip_ratio);
+ need -= chan.afkcount;
+ if(chan.voteskip.counts[0] >= need) {
+ chan.playNext();
+ }
+ else {
+ chan.broadcastVoteskipUpdate();
+ }
+ }
+ chan.broadcastUserUpdate(this);
+}
+
+User.prototype.autoAFK = function () {
+ if(this.awaytimer)
+ clearTimeout(this.awaytimer);
+
+ if(this.channel === null || this.channel.opts.afk_timeout == 0)
+ return;
+
+ this.awaytimer = setTimeout(function () {
+ this.setAFK(true);
+ }.bind(this), this.channel.opts.afk_timeout * 1000);
+}
+
User.prototype.initCallbacks = function() {
this.socket.on("disconnect", function() {
if(this.channel != null)
@@ -166,6 +207,10 @@ User.prototype.initCallbacks = function() {
this.socket.on("chatMsg", function(data) {
if(this.channel != null) {
+ if(data.msg.indexOf("/afk") == -1) {
+ this.setAFK(false);
+ this.autoAFK();
+ }
this.channel.tryChat(this, data);
}
}.bind(this));
@@ -506,11 +551,12 @@ User.prototype.login = function(name, pw, session) {
if(pw == "" && session == "") {
if(this.ip in lastguestlogin) {
var diff = (Date.now() - lastguestlogin[this.ip])/1000;
- if(diff < Config.GUEST_LOGIN_DELAY) {
+ if(diff < this.server.cfg["guest-login-delay"]) {
this.socket.emit("login", {
success: false,
error: ["Guest logins are restricted to one per ",
- Config.GUEST_LOGIN_DELAY + " seconds per IP. ",
+ this.server.cfg["guest-login-delay"]
+ + " seconds per IP. ",
"This restriction does not apply to registered users."
].join("")
});
@@ -580,7 +626,8 @@ User.prototype.login = function(name, pw, session) {
}
}
}
- ActionLog.record(this.ip, name, "login-success");
+ if(this.global_rank >= 255)
+ ActionLog.record(this.ip, name, "login-success");
this.loggedIn = true;
this.socket.emit("login", {
success: true,
diff --git a/www/acp.html b/www/acp.html
index 04063d84..ff72d796 100644
--- a/www/acp.html
+++ b/www/acp.html
@@ -254,6 +254,7 @@
+