canopy/src/app/channel/channelManager.js

184 lines
6.3 KiB
JavaScript

/*Canopy - The next generation of stoner streaming software
Copyright (C) 2024 Rainbownapkin and the TTN Community
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
//Local Imports
const channelModel = require('../../schemas/channel/channelSchema');
const {userModel} = require('../../schemas/userSchema');
const loggerUtils = require('../../utils/loggerUtils');
const activeChannel = require('./activeChannel');
const chatHandler = require('./chatHandler');
module.exports = class{
constructor(io){
//Set the socket.io server
this.io = io;
//Load
this.activeChannels = new Map;
//Load server components
this.chatHandler = new chatHandler(this);
//Handle connections from socket.io
io.on("connection", this.handleConnection.bind(this) );
}
async handleConnection(socket){
//Prevent logged out connections and authenticate socket
if(socket.request.session.user != null){
try{
//Authenticate socket
const userDB = await this.authSocket(socket);
//Get the active channel based on the socket
var {activeChan, chanDB} = await this.getActiveChan(socket);
//Check for ban
const ban = await chanDB.checkBanByUserDoc(userDB);
if(ban != null){
//Toss out banned user's
if(ban.expirationDays < 0){
socket.emit("kick", {type: "Banned", reason: "You have been permanently banned from this channel!"});
}else{
socket.emit("kick", {type: "Banned", reason: `You have been temporarily banned from this channel, and will be unbanned in ${ban.getDaysUntilExpiration()} day(s)!`});
}
socket.disconnect();
return;
}
//Define listeners
this.defineListeners(socket);
this.chatHandler.defineListeners(socket);
//Connect the socket to it's given channel
//Lil' hacky to pass chanDB like that, but why double up on DB calls?
activeChan.handleConnection(userDB, chanDB, socket);
}catch(err){
//Flip a table if something fucks up
return loggerUtils.socketCriticalExceptionHandler(socket, err);
}
}else{
//Toss out anon's
socket.emit("kick", {type: "Disconnected", reason: "You must log-in to join this channel!"});
socket.disconnect();
return;
}
}
async authSocket(socket){
//Find the user in the Database since the session won't store enough data to fulfill our needs :P
const userDB = await userModel.findOne({user: socket.request.session.user.user});
if(userDB == null){
throw new Error("User not found!");
}
//Set socket user and channel values
socket.user = {
id: userDB.id,
user: userDB.user,
};
return userDB;
}
async getActiveChan(socket){
socket.chan = socket.handshake.headers.referer.split('/c/')[1];
const chanDB = (await channelModel.findOne({name: socket.chan}))
//Check if channel exists
if(chanDB == null){
throw new Error("Channel not found!");
}
//Check if current channel is active
var activeChan = this.activeChannels.get(socket.chan);
if(!activeChan){
//If not, make it so
activeChan = new activeChannel(this, chanDB);
this.activeChannels.set(socket.chan, activeChan);
}
//Return whatever the active channel is (new or old)
return {activeChan, chanDB};
//return activeChan;
}
defineListeners(socket){
//Socket Listeners
socket.conn.on("close", (reason) => {this.handleDisconnect(socket, reason)});
}
handleDisconnect(socket, reason){
var activeChan = this.activeChannels.get(socket.chan);
activeChan.handleDisconnect(socket, reason);
}
getSocketInfo(socket){
const channel = this.activeChannels.get(socket.chan);
return channel.userList.get(socket.user.user);
}
getConnectedChannels(socket){
//Create a list to hold connected channels
var chanList = [];
//For each channel
this.activeChannels.forEach((channel) => {
//Check and see if the user is connected
const foundUser = channel.userList.get(socket.user.user);
//If we found a user and this channel hasn't been added to the list
if(foundUser){
chanList.push(channel);
}
});
//return the channels this user is connected to
return chanList;
}
crawlConnections(user, cb){
//For each channel
this.activeChannels.forEach((channel) => {
//Check and see if the user is connected
const foundUser = channel.userList.get(user);
//If we found a user and this channel hasn't been added to the list
if(foundUser){
cb(foundUser);
}
});
}
getConnections(user){
//Create a list to store our connections
var connections = [];
//crawl through connections
//this.crawlConnections(user,(foundUser)=>{connections.push(foundUser)});
this.crawlConnections(user,(foundUser)=>{connections.push(foundUser)});
//return connects
return connections;
}
kickConnections(user, reason){
//crawl through connections and kick user
this.crawlConnections(user,(foundUser)=>{foundUser.disconnect(reason)});
}
}