mpp-server-dev2/oldsrc/Channel.js

849 lines
25 KiB
JavaScript

const createKeccakHash = require("keccak");
const Crown = require("./Crown.js");
const Database = require("./Database.js");
const Logger = require("./Logger.js");
const Quota = require("./Quota.js");
const RoomSettings = require("./RoomSettings.js");
const ftc = require("fancy-text-converter");
const Notification = require("./Notification");
const Color = require("./Color");
const { getTimeColor } = require("./ColorEncoder.js");
const { InternalBot } = require("./InternalBot");
function ansiRegex({ onlyFirst = false } = {}) {
const pattern = [
"[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"
].join("|");
return new RegExp(pattern, onlyFirst ? undefined : "g");
}
const LOGGER_PARTICIPANT = {
name: "Logger",
color: "#72f1b8",
_id: "logger",
id: "logger"
};
const SERVER_PARTICIPANT = {
name: "mpp",
color: "#ffffff",
_id: "0",
id: "0"
};
const LOGGING_CHANNEL = "lolwutsecretloggingchannel";
const BAN_CHANNEL = "test/awkward";
class Channel extends EventEmitter {
static loggingChannel = LOGGING_CHANNEL;
static loggerParticipant = LOGGER_PARTICIPANT;
static serverParticipant = SERVER_PARTICIPANT;
static banChannel = BAN_CHANNEL;
constructor(server, _id, settings, cl) {
super();
this.logger = new Logger(`Room - ${_id}`);
this._id = _id;
this.server = server;
this.crown;
this.crowndropped = false;
if (this.isLobby(this._id)) {
this.settings = new RoomSettings(this.server.lobbySettings);
// this.settings.lobby = true;
// this.settings.color = this.server.lobbySettings.color;
// this.settings.color2 = this.server.lobbySettings.color2;
} else {
this.settings = new RoomSettings(settings, "user");
}
this.chatmsgs = [];
this.ppl = new Map();
this.connections = [];
this.bindEventListeners();
this.server.channels.set(_id, this);
this.bans = new Map();
this.flags = {};
this.destroyed = false;
this.logger.log("Created");
if (this._id == LOGGING_CHANNEL) {
if (cl.user.hasFlag("admin")) {
delete this.crown;
Logger.buffer.forEach(str => {
this.chatmsgs.push({
m: "a",
p: LOGGER_PARTICIPANT,
a: str.replace(ansiRegex(), "")
});
});
Logger.on("buffer update", str => {
this.chatmsgs.push({
m: "a",
p: LOGGER_PARTICIPANT,
a: str.replace(ansiRegex(), "")
});
this.sendChatArray();
});
this.emit("update");
let c = new Color(LOGGER_PARTICIPANT.color);
c.add(-0x40, -0x40, -0x40);
let c2 = new Color(c.toHexa());
c2.add(-0x40, -0x40, -0x40);
this.settings = RoomSettings.changeSettings(
{
color: c.toHexa(),
color2: c2.toHexa(),
chat: true,
crownsolo: true,
lobby: false,
owner_id: LOGGER_PARTICIPANT._id
},
true
);
} else {
cl.setChannel("test/awkward");
}
} else {
Database.getRoomSettings(this._id, (err, set) => {
if (err) {
return;
}
this.settings = RoomSettings.changeSettings(
this.settings,
true
);
this.chatmsgs = set.chat;
this.sendChatArray();
this.setData();
});
}
if (this.isLobby(this._id)) {
this.colorInterval = setInterval(() => {
this.setDefaultLobbyColorBasedOnDate();
}, 5000);
this.setDefaultLobbyColorBasedOnDate();
}
}
setChatArray(arr) {
this.chatmsgs = arr || [];
this.sendArray([
{
m: "c",
c: this.chatmsgs.slice(-1 * 32)
}
]);
this.setData();
}
sendChatArray() {
this.connections.forEach(cl => {
cl.sendArray([
{
m: "c",
c: this.chatmsgs.slice(-1 * 32)
}
]);
});
}
setDefaultLobbyColorBasedOnDate() {
let col = getTimeColor();
let col2 = new Color(col.r - 0x40, col.g - 0x40, col.b - 0x40);
if (!this.settings) {
this.settings = new RoomSettings(this.server.lobbySettings);
}
this.settings.changeSettings({
color: col.toHexa(),
color2: col.toHexa()
});
for (let key in this.settings) {
this.server.lobbySettings[key] = this.settings[key];
}
this.emit("update");
}
join(cl, set) {
//this stuff is complicated
let otheruser = this.connections.find(a => a.user._id == cl.user._id);
if (!otheruser) {
// we don't exist yet
// create id hash
let participantId = createKeccakHash("keccak256")
.update(Math.random().toString() + cl.ip)
.digest("hex")
.substr(0, 24);
// set id
cl.user.id = participantId;
cl.participantId = participantId;
// init quotas (TODO pass type of room in?)
cl.initParticipantQuotas();
// no users / already had crown? give crown
if (
(this.connections.length == 0 &&
Array.from(this.ppl.values()).length == 0 &&
this.isLobby(this._id) == false) ||
(this.crown &&
(this.crown.userId == cl.user._id ||
this.settings["owner_id"] == cl.user._id))
) {
// user owns the room
// we need to switch the crown to them
//cl.quotas.a.setParams(Quota.PARAMS_A_CROWNED);
this.emit("add crown", {
participantId: cl.participantId,
userId: cl.user._id
});
this.crowndropped = false;
// this.settings = new RoomSettings(set, 'user');
} else {
//cl.quotas.a.setParams(Quota.PARAMS_A_NORMAL);
if (this.isLobby(this._id) && this.settings.lobby !== true) {
// fix lobby setting
this.settings.changeSettings({ lobby: true });
// this.settings.visible = true;
// this.settings.crownsolo = false;
// this.settings.lobby = true;
// this.settings.color = this.server.lobbySettings.color;
// this.settings.color2 = this.server.lobbySettings.color2;
} else {
if (!this.isLobby) {
if (typeof set == "undefined") {
if (typeof this.settings == "undefined") {
this.settings = new RoomSettings(
this.server.defaultRoomSettings,
"user"
);
} else {
this.settings = new RoomSettings(
cl.channel.settings,
"user"
);
}
} else {
this.settings = new RoomSettings(set, "user");
}
}
}
}
this.ppl.set(participantId, cl);
this.connections.push(cl);
cl.sendArray([
{
m: "c",
c: this.chatmsgs.slice(-1 * 32)
}
]);
// this.updateCh(cl, this.settings);
if (!cl.user.hasFlag("hidden", true)) {
this.sendArray(
[
{
m: "p",
_id: cl.user._id,
name: cl.user.name,
color: cl.user.color,
id: cl.participantId,
x: this.ppl.get(cl.participantId).x || 200,
y: this.ppl.get(cl.participantId).y || 100
}
],
cl,
false
);
}
this.updateCh(cl, this.settings);
} else {
cl.user.id = otheruser.participantId;
cl.participantId = otheruser.participantId;
cl.quotas = otheruser.quotas;
this.connections.push(cl);
cl.sendArray([
{
m: "c",
c: this.chatmsgs.slice(-1 * 32)
}
]);
this.updateCh(cl, this.settings);
}
}
remove(p) {
// remove user
if (!p) return;
if (!p.user) return;
let otheruser = this.connections.filter(a => a.user._id == p.user._id);
if (!(otheruser.length > 1)) {
this.ppl.delete(p.participantId);
this.connections.splice(
this.connections.findIndex(
a => a.connectionid == p.connectionid
),
1
);
this.sendArray(
[
{
m: "bye",
p: p.participantId
}
],
p,
false
);
if (this.crown)
if (this.crown.userId == p.user._id && !this.crowndropped) {
this.chown();
}
this.updateCh();
} else {
this.connections.splice(
this.connections.findIndex(
a => a.connectionid == p.connectionid
),
1
);
}
}
updateCh(cl, set) {
//update channel for all people in channel
if (Array.from(this.ppl.values()).length <= 0) {
setTimeout(() => {
this.destroy();
}, 3000);
return;
}
this.connections.forEach(usr => {
let u = this.fetchChannelData(usr, cl);
this.server.connections.get(usr.connectionid).sendArray([u]);
});
this.server.updateChannelList([this.fetchChannelData()]);
}
updateParticipant(pid, options) {
let p;
Array.from(this.ppl).map(rpg => {
if (rpg[1].user._id == pid) p = rpg[1];
});
if (typeof p == "undefined") return;
options.name ? (p.user.name = options.name) : {};
options._id ? (p.user._id = options._id) : {};
options.color ? (p.user.color = options.color) : {};
this.connections
.filter(ofo => ofo.participantId == p.participantId)
.forEach(usr => {
options.name ? (usr.user.name = options.name) : {};
options._id ? (usr.user._id = options._id) : {};
options.color ? (usr.user.color = options.color) : {};
});
if (!p.hidden) {
this.sendArray([
{
color: p.user.color,
id: p.participantId,
m: "p",
name: p.user.name,
x: p.x || 200,
y: p.y || 100,
_id: p.user._id
}
]);
}
}
destroy() {
//destroy room
if (this.destroyed) return;
if (this.ppl.size > 0) return;
if (this._id == "lobby") return;
this.destroyed = true;
this._id;
this.logger.log(`Deleted room ${this._id}`);
this.settings = undefined;
this.ppl;
this.connnections;
this.chatmsgs;
this.server.channels.delete(this._id);
}
sendArray(arr, not, onlythisparticipant) {
this.connections.forEach(usr => {
if (
!not ||
(usr.participantId != not.participantId &&
!onlythisparticipant) ||
(usr.connectionid != not.connectionid && onlythisparticipant)
) {
try {
let cl = this.server.connections.get(usr.connectionid);
if (!cl) return;
this.server.connections
.get(usr.connectionid)
.sendArray(arr);
} catch (e) {
this.logger.error(e);
}
}
});
}
fetchChannelData(usr, cl) {
let chppl = [];
[...this.ppl.values()].forEach(c => {
if (cl) {
if (c.hidden == true && c.user._id !== cl.user._id) {
// client is hidden and we are that client
return;
} else if (c.user._id == cl.user._id) {
// let u = {
// _id: c.user._id,
// name: c.user.name + ' [HIDDEN]',
// color: c.user.color,
// id: c.participantId
// }
// chppl.push(u);
}
}
let u = {
_id: c.user._id,
name: c.user.name,
color: c.user.color,
id: c.participantId
};
chppl.push(u);
});
let data = {
m: "ch",
p: "ofo",
ch: {
count: chppl.length,
crown: this.crown,
settings: this.settings,
_id: this._id
},
ppl: chppl
};
if (cl) {
if (usr.connectionid == cl.connectionid) {
data.p = cl.participantId;
} else {
delete data.p;
}
} else {
delete data.p;
}
if (data.ch.crown == null) {
delete data.ch.crown;
} else {
}
return data;
}
verifyColor(strColor) {
var test2 = /^#[0-9A-F]{6}$/i.test(strColor);
if (test2 == true) {
return strColor;
} else {
return false;
}
}
isLobby(_id) {
if (_id.startsWith("lobby")) {
if (_id == "lobby") {
return true;
}
let lobbynum = _id.split("lobby")[1];
if (!(parseInt(lobbynum).toString() == lobbynum)) return false;
for (let i in lobbynum) {
if (parseInt(lobbynum[i]) >= 0) {
if (parseInt(i) + 1 == lobbynum.length) return true;
} else {
return false;
}
}
} else if (
_id.startsWith("test/") ||
_id.toLowerCase().includes("grant")
) {
if (_id == "test/") {
return false;
} else {
return true;
}
} else {
return false;
}
}
chown(id) {
let prsn = this.ppl.get(id);
if (prsn) {
this.crown = new Crown(id, prsn.user._id);
this.crowndropped = false;
} else {
if (this.crown) {
this.crown = new Crown(id, this.crown.userId);
this.crowndropped = true;
}
}
this.updateCh();
}
setCoords(p, x, y) {
if (p.participantId && this.ppl.get(p.participantId)) {
x ? (this.ppl.get(p.participantId).x = x) : {};
y ? (this.ppl.get(p.participantId).y = y) : {};
this.sendArray(
[
{
m: "m",
id: p.participantId,
x: this.ppl.get(p.participantId).x,
y: this.ppl.get(p.participantId).y
}
],
p,
false
);
}
}
chat(p, msg) {
if (msg.message.length > 512) return;
if (p.participantId == 0) {
let message = {};
message.m = "a";
message.t = Date.now();
message.a = msg.message;
if (message.a.length > 0 && message.a.length <= 512) {
message.p = {
color: "#ffffff",
id: "0",
name: "mpp",
_id: "0"
};
this.sendArray([message]);
this.chatmsgs.push(message);
this.setData();
return;
}
}
let filter = ["AMIGHTYWIND", "CHECKLYHQ"];
let regexp = new RegExp("\\b(" + filter.join("|") + ")\\b", "i");
if (regexp.test(msg.message.split(" ").join(""))) return;
let prsn = this.ppl.get(p.participantId);
if (!prsn) return;
let message = {};
message.m = "a";
message.a = msg.message;
if (prsn.user.hasFlag("chat_curse_1")) {
if (prsn.user.flags["chat_curse_1"] != false)
message.a = message.a
.split(/[aeiou]/)
.join("o")
.split(/[AEIOU]/)
.join("O");
}
if (prsn.user.hasFlag("chat_curse_2")) {
}
message.p = {
color: p.user.color,
id: p.participantId,
name: p.user.name,
_id: p.user._id
};
message.t = Date.now();
this.sendArray([message]);
this.chatmsgs.push(message);
this.setData();
InternalBot.emit("receive message", message, prsn, this);
}
adminChat(str) {
this.chat(
{
participantId: 0
},
{
message: str
}
);
}
hasUser(id) {
// return this.ppl.has(id);
for (const p of this.ppl.values()) {
if (p.id == id) return true;
}
}
playNote(cl, note) {
if (cl.user.hasFlag("mute", true)) {
return;
}
if (cl.user.hasFlag("mute")) {
if (Array.isArray(cl.user.flags["mute"])) {
if (cl.user.flags["mute"].includes(this._id)) return;
}
}
let vol;
if (cl.user.hasFlag("volume")) {
vol = Math.round(cl.user.flags["volume"]) / 100;
}
if (typeof vol == "number") {
for (let no of note.n) {
if (no.v) {
if (vol == 0) {
no.v = vol;
} else {
no.v *= vol;
}
}
}
}
this.sendArray(
[
{
m: "n",
n: note.n,
p: cl.participantId,
t: note.t
}
],
cl,
true
);
}
kickban(_id, ms) {
ms = parseInt(ms);
if (ms >= 1000 * 60 * 60) return;
if (ms < 0) return;
ms = Math.round(ms / 1000) * 1000;
let user = this.connections.find(usr => usr.user._id == _id);
if (!user) return;
let asd = true;
let pthatbanned = this.ppl.get(this.crown.participantId);
this.connections
.filter(usr => usr.participantId == user.participantId)
.forEach(u => {
user.bantime = Math.floor(Math.floor(ms / 1000) / 60);
user.bannedtime = Date.now();
user.msbanned = ms;
this.bans.set(user.user._id, user);
//if (this.crown && (this.crown.userId)) {
u.setChannel(Channel.banChannel, {});
if (asd)
new Notification(this.server, {
id: "",
title: "Notice",
text: `Banned from "${this._id}" for ${Math.floor(
Math.floor(ms / 1000) / 60
)} minutes.`,
duration: 7000,
target: "#room",
class: "short",
targetUser: user.participantId,
targetChannel: "all",
cl: user
}).send();
new Notification(this.server, {
id: "",
title: "Notice",
text: `Banned from "${this._id}" for ${Math.floor(
Math.floor(ms / 1000) / 60
)} minutes.`,
duration: 7000,
target: "#room",
class: "short",
targetUser: user.participantId,
targetChannel: "all",
cl: user
}).send();
if (asd)
new Notification(this.server, {
id: "",
class: "short",
target: "#room",
title: "Notice",
text: `${pthatbanned.user.name} banned ${
user.user.name
} from the channel for ${Math.floor(
Math.floor(ms / 1000) / 60
)} minutes.`,
duration: 7000,
targetChannel: "room",
cl: pthatbanned
}).send();
if (this.crown && this.crown.userId == _id) {
new Notification(this.server, {
id: "",
class: "short",
target: "#room",
title: "Certificate of Award",
text: `Let it be known that ${user.user.name} kickbanned him/her self.`,
targetChannel: "room",
duration: 7000,
cl: pthatbanned
}).send();
}
//}
});
}
unban(_id) {
let ban = this.bans.get(_id);
if (!ban) return;
if (ban.bantime) {
delete ban.bantime;
}
if (ban.bannedtime) {
delete ban.bannedtime;
}
this.bans.delete(ban.user._id);
}
bindEventListeners() {
this.on("bye", participant => {
this.remove(participant);
});
this.on("m", msg => {
let p = this.ppl.get(msg.p);
if (!p) return;
this.setCoords(p, msg.x, msg.y);
});
this.on("a", (participant, msg) => {
this.chat(participant, msg);
});
this.on("update", (cl, set) => {
this.updateCh(cl, set);
});
this.on("remove crown", () => {
this.crown = undefined;
delete this.crown;
this.emit("update");
});
this.on("add crown", msg => {
this.crown = new Crown(msg.participantId, msg.userId);
this.emit("update");
});
this.on("kickban", msg => {
if (!msg._id) return;
if (!msg.ms) msg.ms = 30 * 60 * 1000;
this.kickban(msg._id, msg.ms);
});
}
verifySet(_id, msg) {
if (typeof msg.set !== "object") {
msg.set = {
visible: true,
color: this.server.defaultSettings.color,
chat: true,
crownsolo: false
};
}
msg.set = RoomSettings.changeSettings(msg.set);
if (typeof msg.set.lobby !== "undefined") {
if (msg.set.lobby == true) {
if (!this.isLobby(_id)) delete msg.set.lobby;
} else {
if (this.isLobby(_id)) {
msg.set = this.server.lobbySettings;
}
}
}
}
setData() {
Database.setRoomSettings(this._id, this.settings, this.chatmsgs);
}
hasFlag(flag, val) {
if (!val) return this.flags.hasOwnProperty(flag);
return this.flags.hasOwnProperty(flag) && this.flags[flag] == val;
}
}
module.exports = Channel;