sync/lib/user.js

486 lines
12 KiB
JavaScript
Raw Normal View History

var Logger = require("./logger");
2013-10-11 16:31:40 -05:00
var Server = require("./server");
var util = require("./utilities");
var MakeEmitter = require("./emitter");
2014-01-06 09:55:12 -06:00
var db = require("./database");
var InfoGetter = require("./get-info");
2014-01-22 17:11:26 -06:00
var Config = require("./config");
function User(socket) {
var self = this;
MakeEmitter(self);
self.socket = socket;
self.ip = socket._ip;
self.loggedIn = false;
self.loggingIn = false;
self.rank = -1;
self.global_rank = -1;
self.channel = null;
self.name = "";
self.canonicalName = "";
self.profile = {
image: "",
text: ""
};
self.meta = {
afk: false,
muted: false,
smuted: false,
aliases: []
};
self.queueLimiter = util.newRateLimiter();
self.chatLimiter = util.newRateLimiter();
self.awaytimer = false;
2013-02-15 23:02:42 -06:00
self.socket.once("initChannelCallbacks", function () {
self.initChannelCallbacks();
});
2014-01-06 09:55:12 -06:00
self.socket.on("login", function (data) {
data = (typeof data === "object") ? data : {};
var name = data.name;
if (typeof name !== "string") {
return;
}
var pw = data.pw || "";
if (typeof pw !== "string") {
pw = "";
}
if (!pw) {
self.guestLogin(name);
}
});
var announcement = Server.getServer().announcement;
if (announcement != null) {
self.socket.emit("announcement", announcement);
2013-03-20 13:35:06 -05:00
}
2014-01-26 00:13:33 -06:00
self.on("login", function () {
db.recordVisit(self.ip, self.name);
db.users.getProfile(self.name, function (err, profile) {
if (!err) {
self.profile = profile;
if (self.inChannel()) {
self.channel.sendUserProfile(self.channel.users, self);
}
}
});
});
}
2013-02-15 23:02:42 -06:00
/**
* Checks whether the user is in a valid channel
*/
2013-09-18 18:27:42 -05:00
User.prototype.inChannel = function () {
return this.channel != null && !this.channel.dead;
2013-11-25 16:20:15 -06:00
};
/**
* Changes a user's AFK status, updating the channel voteskip if necessary
*/
User.prototype.setAFK = function (afk) {
if (!this.inChannel()) {
return;
}
if (this.meta.afk === afk) {
2013-08-01 15:12:57 -04:00
return;
}
2014-01-18 20:18:00 -06:00
this.meta.afk = afk;
var chan = this.channel;
2013-09-26 13:29:36 -05:00
if (afk) {
if (chan.voteskip) {
2013-08-07 13:11:58 -04:00
chan.voteskip.unvote(this.ip);
}
2013-09-11 22:16:56 -05:00
} else {
2013-08-01 09:39:10 -04:00
this.autoAFK();
}
2013-08-01 09:39:10 -04:00
chan.checkVoteskipPass();
2013-08-01 15:12:57 -04:00
chan.sendAll("setAFK", {
name: this.name,
afk: afk
});
2013-09-26 13:29:36 -05:00
};
/**
* Sets a timer to automatically mark the user as AFK after
* a period of inactivity
*/
User.prototype.autoAFK = function () {
2013-09-26 13:29:36 -05:00
var self = this;
if (self.awaytimer) {
2013-09-26 13:29:36 -05:00
clearTimeout(self.awaytimer);
}
2013-11-29 21:09:19 -06:00
if (!self.inChannel()) {
return;
2013-11-29 21:09:19 -06:00
}
var timeout = parseFloat(self.channel.opts.afk_timeout);
if (isNaN(timeout) || timeout <= 0) {
2013-11-29 21:09:19 -06:00
return;
}
2013-09-26 13:29:36 -05:00
self.awaytimer = setTimeout(function () {
self.setAFK(true);
2013-11-29 21:09:19 -06:00
}, timeout * 1000);
2013-09-26 13:29:36 -05:00
};
/**
* Sends a kick message and disconnects the user
*/
2013-10-12 18:59:50 -05:00
User.prototype.kick = function (reason) {
this.socket.emit("kick", { reason: reason });
this.socket.disconnect(true);
};
/**
* Initializes socket message callbacks for a channel user
*/
User.prototype.initChannelCallbacks = function () {
2013-08-18 12:21:34 -05:00
var self = this;
// Verifies datatype before calling a function
// Passes a default value if the typecheck fails
var typecheck = function (type, def, fn) {
return function (data) {
if (typeof data !== type) {
fn(def);
} else {
fn(data);
}
};
};
// Verify that the user is in a channel, and that the passed data is an Object
var wrapTypecheck = function (msg, fn) {
self.socket.on(msg, typecheck("object", {}, function (data) {
if (self.inChannel()) {
fn(data);
}
}));
};
// Verify that the user is in a channel, but don't typecheck the data
var wrap = function (msg, fn) {
self.socket.on(msg, function (data) {
if (self.inChannel()) {
fn(data);
}
});
};
2013-02-15 23:02:42 -06:00
self.socket.on("disconnect", function () {
if (self.awaytimer) {
clearTimeout(self.awaytimer);
2013-02-15 23:02:42 -06:00
}
2013-09-26 13:29:36 -05:00
if (self.inChannel()) {
2014-01-06 09:55:12 -06:00
self.channel.part(self);
}
2013-08-18 12:21:34 -05:00
});
2014-01-25 21:29:56 -06:00
self.socket.once("joinChannel", typecheck("object", {}, function (data) {
2014-01-25 17:44:32 -06:00
if (self.inChannel()) {
return;
}
if (typeof data.name !== "string") {
return;
2013-05-21 12:17:01 -04:00
}
if (!util.isValidChannelName(data.name)) {
self.socket.emit("errorMsg", {
msg: "Invalid channel name. Channel names may consist of 1-30 " +
"characters in the set a-z, A-Z, 0-9, -, and _"
});
self.kick("Invalid channel name");
return;
2013-02-15 23:02:42 -06:00
}
data.name = data.name.toLowerCase();
var chan = Server.getServer().getChannel(data.name);
2014-01-25 21:29:56 -06:00
chan.preJoin(self, data.pw);
}));
wrapTypecheck("assignLeader", function (data) {
self.channel.handleChangeLeader(self, data);
2013-08-18 12:21:34 -05:00
});
2013-02-15 23:02:42 -06:00
wrapTypecheck("setChannelRank", function (data) {
self.channel.handleSetRank(self, data);
2013-08-18 12:21:34 -05:00
});
2013-04-17 14:05:45 -05:00
2014-01-11 23:55:52 -06:00
wrapTypecheck("unban", function (data) {
self.channel.handleUnban(self, data);
});
wrapTypecheck("chatMsg", function (data) {
if (typeof data.msg !== "string") {
return;
2013-02-15 23:02:42 -06:00
}
if (data.msg.indexOf("/afk") !== 0) {
self.setAFK(false);
self.autoAFK();
2013-05-04 17:54:28 -05:00
}
self.channel.handleChat(self, data);
2013-08-18 12:21:34 -05:00
});
2013-02-15 23:02:42 -06:00
wrapTypecheck("newPoll", function (data) {
2014-01-08 10:52:00 -06:00
self.channel.handleOpenPoll(self, data);
2013-08-18 12:21:34 -05:00
});
wrapTypecheck("vote", function (data) {
2014-01-08 10:52:00 -06:00
self.channel.handlePollVote(self, data);
2013-08-18 12:21:34 -05:00
});
2013-02-15 23:02:42 -06:00
wrap("closePoll", function () {
self.channel.handleClosePoll(self);
2013-08-18 12:21:34 -05:00
});
wrap("playerReady", function () {
self.channel.sendMediaUpdate([self]);
2013-08-18 12:21:34 -05:00
});
2013-02-15 23:02:42 -06:00
wrap("requestPlaylist", function () {
self.channel.sendPlaylist([self]);
2013-08-18 12:21:34 -05:00
});
2013-04-22 15:37:42 -05:00
wrapTypecheck("queue", function (data) {
2014-01-14 00:52:56 -06:00
console.log("queue", data);
self.channel.handleQueue(self, data);
2013-08-18 12:21:34 -05:00
});
2013-04-22 15:37:42 -05:00
wrapTypecheck("setTemp", function (data) {
self.channel.handleSetTemp(self, data);
2013-08-18 12:21:34 -05:00
});
2013-03-16 15:39:58 -05:00
wrapTypecheck("moveMedia", function (data) {
self.channel.handleMove(self, data);
2013-08-18 12:21:34 -05:00
});
2013-02-15 23:02:42 -06:00
2014-01-14 00:52:56 -06:00
wrap("delete", function (data) {
self.channel.handleDelete(self, data);
2013-08-18 12:21:34 -05:00
});
2013-02-15 23:02:42 -06:00
wrapTypecheck("uncache", function (data) {
self.channel.handleUncache(self, data);
2013-08-18 12:21:34 -05:00
});
2013-03-16 16:49:58 -05:00
2014-01-14 00:52:56 -06:00
wrap("jumpTo", function (data) {
self.channel.handleJumpTo(self, data);
2013-08-18 12:21:34 -05:00
});
2013-03-16 16:49:58 -05:00
wrap("playNext", function () {
self.channel.handlePlayNext(self);
2013-08-18 12:21:34 -05:00
});
2013-03-17 12:14:34 -05:00
wrap("clearPlaylist", function () {
self.channel.handleClear(self);
2013-08-18 12:21:34 -05:00
});
2013-05-13 15:41:29 -04:00
wrap("shufflePlaylist", function () {
self.channel.handleShuffle(self);
2013-08-18 12:21:34 -05:00
});
wrap("togglePlaylistLock", function () {
self.channel.handleToggleLock(self);
2013-08-18 12:21:34 -05:00
});
2013-05-22 15:38:16 -04:00
wrapTypecheck("mediaUpdate", function (data) {
self.channel.handleMediaUpdate(self, data);
2013-08-18 12:21:34 -05:00
});
2013-05-15 11:34:27 -04:00
wrapTypecheck("searchMedia", function (data) {
if (typeof data.query !== "string") {
return;
2013-05-15 11:34:27 -04:00
}
data.query = data.query.substring(0, 255);
2013-05-15 11:34:27 -04:00
var searchYT = function () {
InfoGetter.Getters.ytSearch(data.query.split(" "), function (e, vids) {
if (!e) {
self.socket.emit("searchResults", {
source: "yt",
results: vids
});
}
});
};
2013-06-18 11:51:42 -04:00
if (data.source === "yt") {
searchYT();
} else {
self.channel.search(data.query, function (vids) {
if (vids.length === 0) {
searchYT();
} else {
self.socket.emit("searchResults", {
source: "library",
results: vids
});
}
});
2013-06-18 11:51:42 -04:00
}
2013-08-18 12:21:34 -05:00
});
2013-04-01 16:02:09 -05:00
wrapTypecheck("setOptions", function (data) {
self.channel.handleUpdateOptions(self, data);
2013-08-18 12:21:34 -05:00
});
wrapTypecheck("setPermissions", function (data) {
self.channel.handleSetPermissions(self, data);
2013-08-18 12:21:34 -05:00
});
2013-06-17 23:57:29 -04:00
wrapTypecheck("setChannelCSS", function (data) {
self.channel.handleSetCSS(self, data);
2013-08-18 12:21:34 -05:00
});
wrapTypecheck("setChannelJS", function (data) {
self.channel.handleSetJS(self, data);
2013-08-18 12:21:34 -05:00
});
wrapTypecheck("setMotd", function (data) {
2014-01-16 11:53:34 -06:00
self.channel.handleSetMotd(self, data);
2013-08-18 12:21:34 -05:00
});
2013-04-29 18:59:51 -05:00
wrapTypecheck("updateFilter", function (data) {
self.channel.handleUpdateFilter(self, data);
2013-08-18 12:21:34 -05:00
});
wrap("importFilters", function (data) {
self.channel.handleImportFilters(self, data);
});
// REMOVE FILTER
// https://www.youtube.com/watch?v=SxUU3zncVmI
wrapTypecheck("removeFilter", function (data) {
self.channel.handleRemoveFilter(self, data);
2013-08-18 12:21:34 -05:00
});
2013-06-01 11:59:04 -04:00
wrapTypecheck("moveFilter", function (data) {
self.channel.handleMoveFilter(self, data);
2013-08-18 12:21:34 -05:00
});
2013-06-01 15:42:08 -04:00
wrap("requestBanlist", function () {
self.channel.sendBanlist([self]);
2013-08-18 12:21:34 -05:00
});
2013-06-01 16:56:23 -04:00
2014-01-08 20:12:02 -06:00
wrap("requestChannelRanks", function () {
self.channel.sendChannelRanks([self]);
});
2014-01-16 11:53:34 -06:00
wrap("requestChatFilters", function () {
self.channel.sendChatFilters([self]);
2013-08-18 12:21:34 -05:00
});
2013-06-20 19:45:21 -04:00
wrap("voteskip", function () {
self.channel.handleVoteskip(self);
2013-08-18 12:21:34 -05:00
});
wrap("readChanLog", function () {
self.channel.handleReadLog(self);
2013-08-18 12:21:34 -05:00
});
};
2013-06-23 10:25:49 -04:00
User.prototype.whenLoggedIn = function (fn) {
if (this.loggedIn) {
fn();
} else {
this.once("login", fn);
}
2013-09-26 13:29:36 -05:00
};
2013-02-15 23:02:42 -06:00
2013-05-02 22:13:46 -05:00
var lastguestlogin = {};
2013-09-26 13:18:37 -05:00
User.prototype.guestLogin = function (name) {
2013-08-17 15:54:23 -05:00
var self = this;
2013-09-26 13:18:37 -05:00
if (self.ip in lastguestlogin) {
var diff = (Date.now() - lastguestlogin[self.ip]) / 1000;
2014-01-22 17:11:26 -06:00
if (diff < Config.get("guest-login-delay")) {
2013-08-17 15:54:23 -05:00
self.socket.emit("login", {
success: false,
error: "Guest logins are restricted to one per IP address per " +
2014-01-22 17:11:26 -06:00
Config.get("guest-login-delay") + " seconds."
2013-08-17 15:54:23 -05:00
});
return;
2013-08-17 15:54:23 -05:00
}
2013-09-26 13:18:37 -05:00
}
2013-08-17 15:54:23 -05:00
if (!util.isValidUserName(name)) {
2013-09-26 13:18:37 -05:00
self.socket.emit("login", {
success: false,
error: "Invalid username. Usernames must be 1-20 characters long and " +
"consist only of characters a-z, A-Z, 0-9, -, _, and accented " +
"letters."
2013-09-26 13:18:37 -05:00
});
return;
}
2013-08-17 15:54:23 -05:00
// Prevent duplicate logins
2013-09-26 13:18:37 -05:00
self.loggingIn = true;
db.users.isUsernameTaken(name, function (err, taken) {
2013-09-26 13:18:37 -05:00
self.loggingIn = false;
2013-09-26 13:29:36 -05:00
if (err) {
2013-09-26 13:18:37 -05:00
self.socket.emit("login", {
success: false,
error: err
2013-09-26 13:18:37 -05:00
});
return;
}
2013-08-17 15:54:23 -05:00
2013-09-26 13:29:36 -05:00
if (taken) {
2013-08-17 15:54:23 -05:00
self.socket.emit("login", {
2013-09-26 13:18:37 -05:00
success: false,
error: "That username is registered."
2013-02-15 23:02:42 -06:00
});
2013-09-26 13:18:37 -05:00
return;
}
2013-09-26 13:29:36 -05:00
if (self.inChannel()) {
var nameLower = name.toLowerCase();
for (var i = 0; i < self.channel.users.length; i++) {
if (self.channel.users[i].name.toLowerCase() === nameLower) {
2013-09-26 13:18:37 -05:00
self.socket.emit("login", {
success: false,
error: "That name is already in use on this channel."
2013-09-26 13:18:37 -05:00
});
return;
}
2013-06-19 17:54:27 -04:00
}
2013-09-26 13:18:37 -05:00
}
// Login succeeded
2013-09-26 13:18:37 -05:00
lastguestlogin[self.ip] = Date.now();
self.rank = 0;
self.global_rank = 0;
self.socket.emit("rank", 0);
2013-09-26 13:18:37 -05:00
self.name = name;
self.loggedIn = true;
2013-09-26 13:18:37 -05:00
self.socket.emit("login", {
success: true,
2014-01-18 20:18:00 -06:00
name: name,
guest: true
});
// TODO you shouldn't be able to guest login without being in a channel
2013-09-26 13:29:36 -05:00
if (self.inChannel()) {
2013-09-26 13:18:37 -05:00
self.channel.logger.log(self.ip + " signed in as " + name);
}
2013-09-26 13:29:36 -05:00
self.emit("login");
});
2013-09-26 13:29:36 -05:00
};
2013-02-15 23:02:42 -06:00
2013-07-15 23:01:12 -04:00
module.exports = User;