Added build step to auto-generate Documentation pages at the /doc endpoint from JSDoc.
This commit is contained in:
parent
944d91377b
commit
7d31cc9e8a
|
|
@ -22,7 +22,8 @@
|
|||
},
|
||||
"scripts": {
|
||||
"start": "node ./src/server.js",
|
||||
"start:dev": "nodemon ./src/server.js"
|
||||
"start:dev": "nodemon ./src/server.js",
|
||||
"build": "node node_modules/jsdoc/jsdoc.js --recurse src/ --destination www/doc/"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.10",
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ module.exports = class{
|
|||
/**
|
||||
* Validates client requests to change default titles for a given playlist
|
||||
* @param {Object} data - Data handed over from the client
|
||||
* @returns
|
||||
* @returns {Array} Array of strings containing valid titles from the output
|
||||
*/
|
||||
changeDefaultTitlesValidator(data){
|
||||
//Create empty array to hold titles
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ module.exports = class extends media{
|
|||
* @param {media} media - Media object to queue
|
||||
* @param {Number} startTime - Start time formatted as a JS Epoch
|
||||
* @param {Number} startTimeStamp - Start time stamp in seconds
|
||||
* @returns
|
||||
* @returns {queuedMedia} queuedMedia object created from given media object
|
||||
*/
|
||||
static fromMedia(media, startTime, startTimeStamp){
|
||||
//Create and return queuedMedia object from given media object and arguments
|
||||
|
|
@ -75,7 +75,7 @@ module.exports = class extends media{
|
|||
|
||||
/**
|
||||
* Converts array of media objects into array of queuedMedia objects
|
||||
* @param {[media]} mediaList - Array of media objects to queue
|
||||
* @param {Array} mediaList - Array of media objects to queue
|
||||
* @param {Number} start - Start time formatted as JS Epoch
|
||||
* @returns Array of converted queued media objects
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const ytdlpUtil = require('./ytdlpUtils');
|
|||
* Checks a given URL and runs the proper metadata fetching function to create a media object from any supported URL
|
||||
* @param {String} url - URL to yank media against
|
||||
* @param {String} title - Title to apply to yanked media
|
||||
* @returns
|
||||
* @returns {Array} Returns list of yanked media objects on success
|
||||
*/
|
||||
module.exports.yankMedia = async function(url, title){
|
||||
//Get pull type
|
||||
|
|
|
|||
203
www/doc/app_channel_activeChannel.js.html
Normal file
203
www/doc/app_channel_activeChannel.js.html
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: app/channel/activeChannel.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: app/channel/activeChannel.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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 connectedUser = require('./connectedUser');
|
||||
const chatBuffer = require('./chatBuffer');
|
||||
const queue = require('./media/queue');
|
||||
const channelModel = require('../../schemas/channel/channelSchema');
|
||||
const playlistHandler = require('./media/playlistHandler')
|
||||
|
||||
/**
|
||||
* Class representing a single active channel
|
||||
*/
|
||||
module.exports = class{
|
||||
|
||||
/**
|
||||
* Instantiates an activeChannel object
|
||||
* @param {channelManager} server - Parent Server Object
|
||||
* @param {Mongoose.Document} chanDB - chanDB to rehydrate buffer from
|
||||
*/
|
||||
constructor(server, chanDB){
|
||||
this.server = server;
|
||||
this.name = chanDB.name;
|
||||
this.tokeCommands = chanDB.tokeCommands;
|
||||
//Keeping these in a map was originally a vestige but it's more preformant than an array or object so :P
|
||||
this.userList = new Map();
|
||||
this.queue = new queue(server, chanDB, this);
|
||||
this.playlistHandler = new playlistHandler(server, chanDB, this);
|
||||
//Define the chat buffer
|
||||
this.chatBuffer = new chatBuffer(server, chanDB, this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handles server-side initialization for new connections to the channel
|
||||
* @param {Mongoose.Document} userDB - User Document Passthrough to save on DB Access
|
||||
* @param {Mongoose.Document} chanDB - Channnel Document Passthrough to save on DB Access
|
||||
* @param {Socket} socket - Requesting Socket
|
||||
*/
|
||||
async handleConnection(userDB, chanDB, socket){
|
||||
//get current user object from the userlist
|
||||
var userObj = this.userList.get(userDB.user);
|
||||
//get channel rank for current user
|
||||
const chanRank = await chanDB.getChannelRankByUserDoc(userDB);
|
||||
|
||||
//If user is already connected
|
||||
if(userObj){
|
||||
//Add this socket on to the userobject
|
||||
userObj.sockets.push(socket.id);
|
||||
//If the user is joining the channel
|
||||
}else{
|
||||
//Grab flair
|
||||
await userDB.populate('flair');
|
||||
//Set user object
|
||||
userObj = new connectedUser(userDB, chanRank, this, socket);
|
||||
}
|
||||
|
||||
//Set user entry in userlist
|
||||
this.userList.set(userDB.user, userObj);
|
||||
|
||||
//if everything looks good, admit the connection to the channel
|
||||
socket.join(socket.chan);
|
||||
|
||||
//Define per-channel event listeners
|
||||
this.queue.defineListeners(socket);
|
||||
this.playlistHandler.defineListeners(socket);
|
||||
|
||||
//Hand off the connection initiation to it's user object
|
||||
await userObj.handleConnection(userDB, chanDB, socket)
|
||||
|
||||
//Send out the userlist
|
||||
this.broadcastUserList(socket.chan);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles server-side initialization for disconnecting from the channel
|
||||
* @param {Socket} socket - Requesting Socket
|
||||
*/
|
||||
handleDisconnect(socket){
|
||||
//If we have more than one active connection
|
||||
if(this.userList.get(socket.user.user).sockets.length > 1){
|
||||
//temporarily store userObj
|
||||
var userObj = this.userList.get(socket.user.user);
|
||||
|
||||
//Filter out disconnecting socket from socket list, and set as current socket list for user
|
||||
userObj.sockets = userObj.sockets.filter((id) => {
|
||||
return id != socket.id;
|
||||
});
|
||||
|
||||
//Update the userlist
|
||||
this.userList.set(socket.user.user, userObj);
|
||||
}else{
|
||||
//If this is the last connection for this user, remove them from the userlist
|
||||
this.userList.delete(socket.user.user);
|
||||
}
|
||||
|
||||
//and send out the filtered list
|
||||
this.broadcastUserList(socket.chan);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts user list to all users
|
||||
*/
|
||||
broadcastUserList(){
|
||||
//Create a userlist object with the tokebot user pre-loaded
|
||||
var userList = [{
|
||||
user: "Tokebot",
|
||||
flair: "classic",
|
||||
highLevel: "∞",
|
||||
}];
|
||||
|
||||
this.userList.forEach((userObj, user) => {
|
||||
userList.push({
|
||||
user: user,
|
||||
flair: userObj.flair,
|
||||
highLevel: userObj.highLevel
|
||||
});
|
||||
});
|
||||
|
||||
this.server.io.in(this.name).emit("userList", userList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts channel emote list to connected users
|
||||
* @param {Mongoose.Document} chanDB - Channnel Document Passthrough to save on DB Access
|
||||
*/
|
||||
async broadcastChanEmotes(chanDB){
|
||||
//if we wherent handed a channel document
|
||||
if(chanDB == null){
|
||||
//Pull it based on channel name
|
||||
chanDB = await channelModel.findOne({name: this.name});
|
||||
}
|
||||
|
||||
//Get emote list from channel document
|
||||
const emoteList = chanDB.getEmotes();
|
||||
|
||||
//Broadcast that sumbitch
|
||||
this.server.io.in(this.name).emit('chanEmotes', emoteList);
|
||||
}
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
354
www/doc/app_channel_channelManager.js.html
Normal file
354
www/doc/app_channel_channelManager.js.html
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: app/channel/channelManager.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: app/channel/channelManager.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//Config
|
||||
const config = require('../../../config.json');
|
||||
|
||||
//Local Imports
|
||||
const channelModel = require('../../schemas/channel/channelSchema');
|
||||
const emoteModel = require('../../schemas/emoteSchema');
|
||||
const {userModel} = require('../../schemas/user/userSchema');
|
||||
const userBanModel = require('../../schemas/user/userBanSchema');
|
||||
const loggerUtils = require('../../utils/loggerUtils');
|
||||
const csrfUtils = require('../../utils/csrfUtils');
|
||||
const activeChannel = require('./activeChannel');
|
||||
const chatHandler = require('./chatHandler');
|
||||
|
||||
/**
|
||||
* Class containing global server-side channel connection management logic
|
||||
*/
|
||||
module.exports = class{
|
||||
/**
|
||||
* Instantiates object containing global server-side channel conection management logic
|
||||
* @param {Server} io - Socket.io server instanced passed down from server.js
|
||||
*/
|
||||
constructor(io){
|
||||
//Set the socket.io server
|
||||
this.io = io;
|
||||
|
||||
//Load
|
||||
this.activeChannels = new Map;
|
||||
|
||||
//Load server components
|
||||
this.chatHandler = new chatHandler(this);
|
||||
//this.mediaYanker = new mediaYanker(this);
|
||||
|
||||
//Handle connections from socket.io
|
||||
io.on("connection", this.handleConnection.bind(this) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles global server-side initialization for new connections to any channel
|
||||
* @param {Socket} socket - Requesting Socket
|
||||
*/
|
||||
async handleConnection(socket){
|
||||
try{
|
||||
//ensure unbanned ip and valid CSRF token
|
||||
if(!(await this.validateSocket(socket))){
|
||||
socket.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
//Prevent logged out connections and authenticate socket
|
||||
if(socket.request.session.user != null){
|
||||
//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 chan ban
|
||||
const ban = await chanDB.checkBanByUserDoc(userDB);
|
||||
if(ban != null){
|
||||
//Toss out banned user's
|
||||
if(ban.expirationDays < 0){
|
||||
socket.emit("kick", {type: "kicked", reason: "You have been permanently banned from this channel!"});
|
||||
}else{
|
||||
socket.emit("kick", {type: "kicked", reason: `You have been temporarily banned from this channel, and will be unbanned in ${ban.getDaysUntilExpiration()} day(s)!`});
|
||||
}
|
||||
socket.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
//Define listeners for inter-channel classes
|
||||
this.defineListeners(socket);
|
||||
this.chatHandler.defineListeners(socket);
|
||||
|
||||
//Hand off the connection to it's given active channel object
|
||||
//Lil' hacky to pass chanDB like that, but why double up on DB calls?
|
||||
activeChan.handleConnection(userDB, chanDB, socket);
|
||||
}else{
|
||||
//Toss out anon's
|
||||
socket.emit("kick", {type: "disconnected", reason: "You must log-in to join this channel!"});
|
||||
socket.disconnect();
|
||||
return;
|
||||
}
|
||||
}catch(err){
|
||||
//Flip a table if something fucks up
|
||||
return loggerUtils.socketCriticalExceptionHandler(socket, err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Global server-side validation logic for new connections to any channel
|
||||
* @param {Socket} socket - Requesting Socket
|
||||
* @returns {Boolean} true on success
|
||||
*/
|
||||
async validateSocket(socket){
|
||||
//If we're proxied use passthrough IP
|
||||
const ip = config.proxied ? socket.handshake.headers['x-forwarded-for'] : socket.handshake.address;
|
||||
|
||||
//Look for ban by IP
|
||||
const ipBanDB = await userBanModel.checkBanByIP(ip);
|
||||
|
||||
//If this ip is randy bobandy
|
||||
if(ipBanDB != null){
|
||||
//Make the number a little prettier despite the lack of precision since we're not doing calculations here :P
|
||||
const expiration = ipBanDB.getDaysUntilExpiration() < 1 ? 0 : ipBanDB.getDaysUntilExpiration();
|
||||
|
||||
//If the ban is permanent
|
||||
if(ipBanDB.permanent){
|
||||
//tell it to fuck off
|
||||
socket.emit("kick", {type: "kicked", reason: `The IP address you are trying to connect from has been permanently banned. Your cleartext IP has been saved to the database. Any associated accounts will be nuked in ${expiration} day(s).`});
|
||||
//Otherwise
|
||||
}else{
|
||||
//tell it to fuck off
|
||||
socket.emit("kick", {type: "kicked", reason: `The IP address you are trying to connect from has been temporarily banned. Your cleartext IP has been saved to the database until the ban expires in ${expiration} day(s).`});
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//Check for Cross-Site Request Forgery
|
||||
if(!csrfUtils.isRequestValid(socket.request)){
|
||||
socket.emit("kick", {type: "disconnected", reason: "Invalid CSRF Token!"});
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Global server-side authorization logic for new connections to any channel
|
||||
* @param {Socket} socket - Requesting Socket
|
||||
* @returns {Mongoose.Document} - Authorized User Document upon success
|
||||
*/
|
||||
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 loggerUtils.exceptionSmith("User not found!", "unauthorized");
|
||||
}
|
||||
|
||||
//Set socket user and channel values
|
||||
socket.user = {
|
||||
id: userDB.id,
|
||||
user: userDB.user,
|
||||
};
|
||||
|
||||
return userDB;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets active channel from a given socket
|
||||
* @param {Socket} socket - Socket to check
|
||||
* @returns {Object} Object containing users active channel name and channel document object
|
||||
*/
|
||||
async getActiveChan(socket){
|
||||
socket.chan = socket.handshake.headers.referer.split('/c/')[1].split('/')[0];
|
||||
const chanDB = (await channelModel.findOne({name: socket.chan}));
|
||||
|
||||
//Check if channel exists
|
||||
if(chanDB == null){
|
||||
throw loggerUtils.exceptionSmith("Channel not found", "validation");
|
||||
}
|
||||
|
||||
//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};
|
||||
}
|
||||
|
||||
/**
|
||||
* Define Global Server-Side socket event listeners
|
||||
* @param {Socket} socket - Socket to check
|
||||
*/
|
||||
defineListeners(socket){
|
||||
//Socket Listeners
|
||||
socket.conn.on("close", (reason) => {this.handleDisconnect(socket, reason)});
|
||||
}
|
||||
|
||||
/**
|
||||
* Global server-side logic for handling disconncted sockets
|
||||
* @param {Socket} socket - Socket to check
|
||||
* @param {String} reason - Reason for disconnection
|
||||
*/
|
||||
handleDisconnect(socket, reason){
|
||||
var activeChan = this.activeChannels.get(socket.chan);
|
||||
activeChan.handleDisconnect(socket, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls user information by socket
|
||||
* @param {Socket} socket - Socket to check
|
||||
* @return returns related user info
|
||||
*/
|
||||
getSocketInfo(socket){
|
||||
const channel = this.activeChannels.get(socket.chan);
|
||||
return channel.userList.get(socket.user.user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls user information by socket
|
||||
* @param {Socket} socket - Socket to check
|
||||
* @return returns related user info
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through connections by a given username, and runs them through a given callback function/method
|
||||
* @param {String} user - Username to crawl connections against
|
||||
* @param {Function} cb - Callback function to run active connections of a given user against
|
||||
*/
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through connections by a given username, and runs them through a given callback function/method
|
||||
* @param {String} user - Username to crawl connections against
|
||||
* @param {Function} cb - Callback function to run active connections of a given user against
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kicks a user from all channels by username
|
||||
* @param {String} user - Username to kick from the server
|
||||
* @param {String} reason - Reason for kick
|
||||
*/
|
||||
kickConnections(user, reason){
|
||||
//crawl through connections and kick user
|
||||
this.crawlConnections(user,(foundUser)=>{foundUser.disconnect(reason)});
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast global emote list
|
||||
*/
|
||||
async broadcastSiteEmotes(){
|
||||
//Get emote list from DB
|
||||
const emoteList = await emoteModel.getEmotes();
|
||||
|
||||
//Broadcast that sumbitch
|
||||
this.io.sockets.emit('siteEmotes', emoteList);
|
||||
}
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
90
www/doc/app_channel_chat.js.html
Normal file
90
www/doc/app_channel_chat.js.html
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: app/channel/chat.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: app/channel/chat.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
/**
|
||||
* Class representing a single chat message
|
||||
*/
|
||||
class chat{
|
||||
/**
|
||||
* Instantiates a chat message object
|
||||
* @param {connectedUser} user - User who sent the message
|
||||
* @param {String} flair - Flair ID String for the flair used to send the message
|
||||
* @param {Number} highLevel - Number representing current high level
|
||||
* @param {String} msg - Contents of the message, with links replaced with numbered file-seperator markers
|
||||
* @param {String} type - Message Type Identifier, used for client-side processing.
|
||||
* @param {Array} links - Array of URLs/Links included in the message.
|
||||
*/
|
||||
constructor(user, flair, highLevel, msg, type, links){
|
||||
this.user = user;
|
||||
this.flair = flair;
|
||||
this.highLevel = highLevel;
|
||||
this.msg = msg;
|
||||
this.type = type;
|
||||
this.links = links;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = chat;</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
187
www/doc/app_channel_chatBuffer.js.html
Normal file
187
www/doc/app_channel_chatBuffer.js.html
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: app/channel/chatBuffer.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: app/channel/chatBuffer.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
const config = require('../../../config.json');
|
||||
const channelModel = require('../../schemas/channel/channelSchema');
|
||||
|
||||
/**
|
||||
* Class representing a stored chat buffer
|
||||
*/
|
||||
class chatBuffer{
|
||||
/**
|
||||
* Instantiates a new chat buffer for a given channel
|
||||
* @param {channelManager} server - Parent Server Object
|
||||
* @param {Mongoose.Document} chanDB - chanDB to rehydrate buffer from
|
||||
* @param {activeChannel} channel - Parent Channel Object
|
||||
*/
|
||||
constructor(server, chanDB, channel){
|
||||
//Grab parent server and chan objects
|
||||
this.server = server;
|
||||
this.channel = channel;
|
||||
|
||||
//If we have no chanDB.chatBuffer
|
||||
if(chanDB == null || chanDB.chatBuffer == null){
|
||||
//Create RAM-based buffer array
|
||||
this.buffer = [];
|
||||
//Otherwise
|
||||
}else{
|
||||
//Pull buffer from DB
|
||||
this.buffer = chanDB.chatBuffer;
|
||||
}
|
||||
|
||||
//Create variables to hold timers for deciding when to write RAM buffer to DB
|
||||
//Goes off 'this.inactivityDelay' seconds after the last chat was sent, assuming it isn't interrupted by new chats
|
||||
this.inactivityTimer = null;
|
||||
this.inactivityDelay = 10;
|
||||
//Goes off 'this.busyDelay' minutes after the first chat message in the current volley of messages. Get's cancelled before being called if this.inactivityTimer goes off.
|
||||
this.busyTimer = null;
|
||||
this.busyDelay = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a given chat to the chat buffer in RAM and sets any appropriate timers for DB transactions
|
||||
* @param {chat} chat - Chat object to commit to buffer
|
||||
*/
|
||||
push(chat){
|
||||
//push chat into RAM buffer
|
||||
this.buffer.push(chat);
|
||||
|
||||
//clear existing inactivity timer
|
||||
clearTimeout(this.inactivityTimer);
|
||||
|
||||
//reset inactivity timer
|
||||
this.inactivityTimer = setTimeout(this.handleInactivity.bind(this), 1000 * this.inactivityDelay);
|
||||
|
||||
//If busy timer is unset
|
||||
if(this.busyTimer == null){
|
||||
this.busyTimer = setTimeout(this.handleBusyRoom.bind(this), 1000 * 60 * this.busyDelay);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the oldest item from the chat buffer
|
||||
*
|
||||
* Was originally created in-case we needed to trigger timing functions
|
||||
*
|
||||
* Left here since it seems like good form anywho, since this would be a private, or at least protected member in another language
|
||||
*/
|
||||
shift(){
|
||||
this.buffer.shift();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after 10 seconds of chat room inactivity
|
||||
*/
|
||||
handleInactivity(){
|
||||
this.saveDB(`${this.inactivityDelay} seconds of inactivity.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after 5 minutes of solid activity
|
||||
*/
|
||||
handleBusyRoom(){
|
||||
this.saveDB(`${this.busyDelay} minutes of activity.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves RAM-Based buffer to Channel Document in DB
|
||||
* @param {String} reason - Reason for DB save, formatted as 'x minutes/seconds of in/activity', used for logging purposes
|
||||
* @param {Mongoose.Document} chanDB - Channel Doc to work with, can be left empty for method to auto-find through channel name.
|
||||
*/
|
||||
async saveDB(reason, chanDB){
|
||||
//clear existing timers
|
||||
clearTimeout(this.inactivityTimer);
|
||||
clearTimeout(this.busyTimer);
|
||||
this.inactivityTimer = null;
|
||||
this.busyTimer = null;
|
||||
|
||||
//if the server is in screamy boi mode
|
||||
if(config.verbose){
|
||||
//This should eventually be replaced by a per-channel logging feature that provides access to chan admins via web front-end
|
||||
console.log(`Saving chat buffer to channel ${this.channel.name} after ${reason}.`);
|
||||
}
|
||||
|
||||
|
||||
//If we wheren't handed a channel
|
||||
if(chanDB == null){
|
||||
//Now that everything is clean, we can take our time with the DB :P
|
||||
chanDB = await channelModel.findOne({name:this.channel.name});
|
||||
}
|
||||
|
||||
//If we couldn't find the channel
|
||||
if(chanDB == null){
|
||||
//FUCK
|
||||
throw loggerUtils.exceptionSmith(`Unable to find channel document ${this.channel.name} while saving chat buffer!`, "chat");
|
||||
}
|
||||
|
||||
//Set chan doc buffer to RAM buffer
|
||||
chanDB.chatBuffer = this.buffer;
|
||||
|
||||
//save chan doc to DB.
|
||||
await chanDB.save();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = chatBuffer;</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
383
www/doc/app_channel_chatHandler.js.html
Normal file
383
www/doc/app_channel_chatHandler.js.html
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: app/channel/chatHandler.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: app/channel/chatHandler.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM imports
|
||||
const validator = require('validator')
|
||||
|
||||
//local imports
|
||||
const commandPreprocessor = require('./commandPreprocessor');
|
||||
const loggerUtils = require('../../utils/loggerUtils');
|
||||
const linkUtils = require('../../utils/linkUtils');
|
||||
const emoteValidator = require('../../validators/emoteValidator');
|
||||
const chat = require('./chat');
|
||||
const {userModel} = require('../../schemas/user/userSchema');
|
||||
|
||||
/**
|
||||
* Class containing global server-side chat relay logic
|
||||
*/
|
||||
module.exports = class{
|
||||
/**
|
||||
* Instantiates a chatHandler object
|
||||
* @param {channelManager} server - Parent Server Object
|
||||
*/
|
||||
constructor(server){
|
||||
//Set server
|
||||
this.server = server;
|
||||
//Initialize command preprocessor
|
||||
this.commandPreprocessor = new commandPreprocessor(server, this)
|
||||
//Max chat buffer size
|
||||
this.chatBufferSize = 50;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines global server-side chat relay event listeners
|
||||
* @param {Socket} socket - Requesting Socket
|
||||
*/
|
||||
defineListeners(socket){
|
||||
socket.on("chatMessage", (data) => {this.handleChat(socket, data)});
|
||||
socket.on("setFlair", (data) => {this.setFlair(socket, data)});
|
||||
socket.on("setHighLevel", (data) => {this.setHighLevel(socket, data)});
|
||||
socket.on("addPersonalEmote", (data) => {this.addPersonalEmote(socket, data)});
|
||||
socket.on("deletePersonalEmote", (data) => {this.deletePersonalEmote(socket, data)});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles incoming chat messages from client connections
|
||||
* @param {Socket} socket - Socket we're receiving the request from
|
||||
* @param {Object} data - Event payload
|
||||
*/
|
||||
handleChat(socket, data){
|
||||
this.commandPreprocessor.preprocess(socket, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles incoming client request to change flair
|
||||
* @param {Socket} socket - Socket we're receiving the request from
|
||||
* @param {Object} data - Event payload
|
||||
*/
|
||||
async setFlair(socket, data){
|
||||
var userDB = await userModel.findOne({user: socket.user.user});
|
||||
|
||||
if(userDB){
|
||||
try{
|
||||
//We can take this data raw since our schema checks it against existing flairs, and mongoose sanatizes queries
|
||||
const flairDB = await userDB.setFlair(data.flair);
|
||||
|
||||
//Crawl through users active connections
|
||||
this.server.crawlConnections(socket.user.user, (conn)=>{
|
||||
//Update flair
|
||||
conn.updateFlair(flairDB.name);
|
||||
});
|
||||
}catch(err){
|
||||
return loggerUtils.socketExceptionHandler(socket, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles incoming client request to change high level
|
||||
* @param {Socket} socket - Socket we're receiving the request from
|
||||
* @param {Object} data - Event payload
|
||||
*/
|
||||
async setHighLevel(socket, data){
|
||||
var userDB = await userModel.findOne({user: socket.user.user});
|
||||
|
||||
if(userDB){
|
||||
try{
|
||||
//Floor input to an integer and set high level
|
||||
userDB.highLevel = Math.floor(data.highLevel);
|
||||
//Save user DB Document
|
||||
await userDB.save();
|
||||
|
||||
//GetConnects across all channels
|
||||
const connections = this.server.getConnections(socket.user.user);
|
||||
|
||||
//For each connection
|
||||
connections.forEach((conn) => {
|
||||
conn.updateHighLevel(userDB.highLevel);
|
||||
});
|
||||
}catch(err){
|
||||
return loggerUtils.socketExceptionHandler(socket, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles incoming client request to add a personal emote
|
||||
* @param {Socket} socket - Socket we're receiving the request from
|
||||
* @param {Object} data - Event payload
|
||||
*/
|
||||
async addPersonalEmote(socket, data){
|
||||
//Sanatize and Validate input
|
||||
const name = emoteValidator.manualName(data.name);
|
||||
const link = emoteValidator.manualLink(data.link);
|
||||
|
||||
//If we received good input
|
||||
if(link && name){
|
||||
//Generate marked link object
|
||||
var emote = await linkUtils.markLink(link);
|
||||
|
||||
//If the link we have is an image or video
|
||||
if(emote.type == 'image' || emote.type == 'video'){
|
||||
//Get user document from DB
|
||||
const userDB = await userModel.findOne({user: socket.user.user})
|
||||
|
||||
//if we have a user in the DB
|
||||
if(userDB != null){
|
||||
//Convert marked link into emote object with 1 ez step for only $19.95
|
||||
emote.name = name;
|
||||
|
||||
//add emote to user document emotes list
|
||||
userDB.emotes.push(emote);
|
||||
|
||||
//Save user doc
|
||||
await userDB.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles incoming client request to delete a personal emote
|
||||
* @param {Socket} socket - Socket we're receiving the request from
|
||||
* @param {Object} data - Event payload
|
||||
*/
|
||||
async deletePersonalEmote(socket, data){
|
||||
//Get user doc from DB based on socket
|
||||
const userDB = await userModel.findOne({user: socket.user.user});
|
||||
|
||||
//if we found a user
|
||||
if(userDB != null){
|
||||
await userDB.deleteEmote(data.name);
|
||||
}
|
||||
}
|
||||
|
||||
//Base chat functions
|
||||
/**
|
||||
* Creates a new chatObject and relays the resulting message to the given channel
|
||||
* @param {String} user - Originating user
|
||||
* @param {String} flair - Flair ID to mark chat with
|
||||
* @param {Number} highLevel - High Level to mark chat with
|
||||
* @param {String} msg - Message Text Content
|
||||
* @param {String} type - Message Type, used for client-side chat post-processing.
|
||||
* @param {String} chan - Channel to broadcast message within
|
||||
* @param {Array} links - Array of URLs/Links to hand to the client-side chat post-processor to inject into the final message.
|
||||
*/
|
||||
relayChat(user, flair, highLevel, msg, type = 'chat', chan, links){
|
||||
this.relayChatObject(chan, new chat(user, flair, highLevel, msg, type, links));
|
||||
}
|
||||
|
||||
/**
|
||||
* Relays an existing chat object to a channel
|
||||
* @param {String} chan - Channel to broadcast message within
|
||||
* @param {chat} chat - Chat Object representing the message to broadcast to the given channel
|
||||
*/
|
||||
relayChatObject(chan, chat){
|
||||
//Send out chat
|
||||
this.server.io.in(chan).emit("chatMessage", chat);
|
||||
|
||||
const channel = this.server.activeChannels.get(chan);
|
||||
|
||||
//If chat buffer length is over mandated size
|
||||
if(channel.chatBuffer.buffer.length >= this.chatBufferSize){
|
||||
//Take out oldest chat
|
||||
channel.chatBuffer.shift();
|
||||
}
|
||||
|
||||
//Add buffer to chat
|
||||
channel.chatBuffer.push(chat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new chatObject and relays the resulting message to the given socket
|
||||
* @param {Socket} socket - Socket we're sending a message to (sounds menacing, huh?)
|
||||
* @param {String} user - Originating user
|
||||
* @param {String} flair - Flair ID to mark chat with
|
||||
* @param {Number} highLevel - High Level to mark chat with
|
||||
* @param {String} msg - Message Text Content
|
||||
* @param {String} type - Message Type, used for client-side chat post-processing.
|
||||
* @param {String} chan - Channel to broadcast message within
|
||||
* @param {Array} links - Array of URLs/Links to hand to the client-side chat post-processor to inject into the final message.
|
||||
*/
|
||||
relayPrivateChat(socket, user, flair, highLevel, msg, type, links){
|
||||
this.relayPrivateChatObject(socket , new chat(user, flair, highLevel, msg, type, links));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles incoming client request to delete a personal emote
|
||||
* @param {Socket} socket - Socket we're receiving the request from
|
||||
* @param {Object} data - Event payload
|
||||
*/
|
||||
relayPrivateChatObject(socket, chat){
|
||||
socket.emit("chatMessage", chat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new chatObject and relays the resulting message to the entire server
|
||||
* @param {String} user - Originating user
|
||||
* @param {String} flair - Flair ID to mark chat with
|
||||
* @param {Number} highLevel - High Level to mark chat with
|
||||
* @param {String} msg - Message Text Content
|
||||
* @param {String} type - Message Type, used for client-side chat post-processing.
|
||||
* @param {Array} links - Array of URLs/Links to hand to the client-side chat post-processor to inject into the final message.
|
||||
*/
|
||||
relayGlobalChat(user, flair, highLevel, msg, type = 'chat', links){
|
||||
this.relayGlobalChatObject(new chat(user, flair, highLevel, msg, type, links));
|
||||
}
|
||||
|
||||
/**
|
||||
* Relays an existing chat object to the entire server
|
||||
* @param {chat} chat - Chat Object representing the message to broadcast throughout the server
|
||||
*/
|
||||
relayGlobalChatObject(chat){
|
||||
this.server.io.emit("chatMessage", chat);
|
||||
}
|
||||
|
||||
//User Chat Functions
|
||||
/**
|
||||
* Relays a chat message from a user to the rest of the channel based on socket
|
||||
* @param {Socket} socket - Socket we're receiving the request from
|
||||
* @param {String} msg - Message Text Content
|
||||
* @param {String} type - Message Type, used for client-side chat post-processing.
|
||||
* @param {Array} links - Array of URLs/Links to hand to the client-side chat post-processor to inject into the final message.
|
||||
*/
|
||||
relayUserChat(socket, msg, type, links){
|
||||
const user = this.server.getSocketInfo(socket);
|
||||
this.relayChat(user.user, user.flair, user.highLevel, msg, type, socket.chan, links);
|
||||
}
|
||||
|
||||
//Toke Chat Functions
|
||||
/**
|
||||
* Broadcasts toke callout to the server
|
||||
* @param {String} msg - Message Text Content
|
||||
* @param {Array} links - Array of URLs/Links to hand to the client-side chat post-processor to inject into the final message.
|
||||
*/
|
||||
relayTokeCallout(msg, links){
|
||||
this.relayGlobalChat("Tokebot", "", '∞', msg, "toke", links);
|
||||
}
|
||||
/**
|
||||
* Broadcasts toke callout to the server
|
||||
* @param {Socket} socket - Socket we're sending the whisper to
|
||||
* @param {String} msg - Message Text Content
|
||||
* @param {Array} links - Array of URLs/Links to hand to the client-side chat post-processor to inject into the final message.
|
||||
*/
|
||||
relayTokeWhisper(socket, msg, links){
|
||||
this.relayPrivateChat(socket, "Tokebot", "", '∞', msg, "tokewhisper", links);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts toke whisper to the server
|
||||
* @param {String} msg - Message Text Content
|
||||
* @param {Array} links - Array of URLs/Links to hand to the client-side chat post-processor to inject into the final message.
|
||||
*/
|
||||
relayGlobalTokeWhisper(msg, links){
|
||||
this.relayGlobalChat("Tokebot", "", '∞', msg, "tokewhisper", links);
|
||||
}
|
||||
|
||||
//Announcement Functions
|
||||
/**
|
||||
* Broadcasts announcement to the server
|
||||
* @param {String} msg - Message Text Content
|
||||
* @param {Array} links - Array of URLs/Links to hand to the client-side chat post-processor to inject into the final message.
|
||||
*/
|
||||
relayServerAnnouncement(msg, links){
|
||||
this.relayGlobalChat("Server", "", '∞', msg, "announcement", links);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts announcement to a given channel
|
||||
* @param {String} msg - Message Text Content
|
||||
* @param {Array} links - Array of URLs/Links to hand to the client-side chat post-processor to inject into the final message.
|
||||
*/
|
||||
relayChannelAnnouncement(chan, msg, links){
|
||||
const activeChan = this.server.activeChannels.get(chan);
|
||||
|
||||
//If channel isn't null
|
||||
if(activeChan != null){
|
||||
this.relayChat("Channel", "", '∞', msg, "announcement", chan, links);
|
||||
}
|
||||
}
|
||||
|
||||
//Misc Functions
|
||||
/**
|
||||
* Clears chat for a given channel, targets specified user or entire channel if none found/specified.
|
||||
* @param {String} user - User chats to clear
|
||||
* @param {String} chan - Channel to broadcast message within
|
||||
*/
|
||||
clearChat(chan, user){
|
||||
const activeChan = this.server.activeChannels.get(chan);
|
||||
|
||||
//If channel isn't null
|
||||
if(activeChan != null){
|
||||
const target = activeChan.userList.get(user);
|
||||
|
||||
//If no user was entered OR the user was found
|
||||
if(user == null || target != null){
|
||||
this.server.io.in(chan).emit("clearChat", {user});
|
||||
}
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
480
www/doc/app_channel_commandPreprocessor.js.html
Normal file
480
www/doc/app_channel_commandPreprocessor.js.html
Normal file
|
|
@ -0,0 +1,480 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: app/channel/commandPreprocessor.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: app/channel/commandPreprocessor.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const validator = require('validator');//No express here, so regular validator it is!
|
||||
|
||||
//Local Imports
|
||||
const tokebot = require('./tokebot');
|
||||
const linkUtils = require('../../utils/linkUtils');
|
||||
const permissionModel = require('../../schemas/permissionSchema');
|
||||
const channelModel = require('../../schemas/channel/channelSchema');
|
||||
|
||||
/**
|
||||
* Class containing global server-side chat/command pre-processing logic
|
||||
*/
|
||||
module.exports = class commandPreprocessor{
|
||||
/**
|
||||
* Instantiates a commandPreprocessor object
|
||||
* @param {channelManager} server - Parent Server Object
|
||||
* @param {chatHandler} chatHandler - Parent Chat Handler Object
|
||||
*/
|
||||
constructor(server, chatHandler){
|
||||
this.server = server;
|
||||
this.chatHandler = chatHandler;
|
||||
this.commandProcessor = new commandProcessor(server, chatHandler);
|
||||
this.tokebot = new tokebot(server, chatHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ingests a command/chat request from Chat Handler and pre-processes and processes it accordingly
|
||||
* @param {Socket} socket - Socket we're receiving the request from
|
||||
* @param {Object} data - Event payload
|
||||
*/
|
||||
async preprocess(socket, data){
|
||||
//Set command object
|
||||
const commandObj = {
|
||||
socket,
|
||||
sendFlag: true,
|
||||
rawData: data,
|
||||
chatType: 'chat'
|
||||
}
|
||||
|
||||
//If we don't pass sanatization/validation turn this car around
|
||||
if(!this.sanatizeCommand(commandObj)){
|
||||
return;
|
||||
}
|
||||
|
||||
//split the command
|
||||
this.splitCommand(commandObj);
|
||||
|
||||
//Process the command
|
||||
await this.processServerCommand(commandObj);
|
||||
|
||||
//If we're going to relay this command as a message, continue on to chat processing
|
||||
if(commandObj.sendFlag){
|
||||
//Prep the message
|
||||
await this.prepMessage(commandObj);
|
||||
|
||||
//Send the chat
|
||||
this.sendChat(commandObj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanatizes and Validates a single user chat message/command
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request
|
||||
* @returns {Boolean} false if Command/Message is too long to send
|
||||
*/
|
||||
sanatizeCommand(commandObj){
|
||||
//Trim and Sanatize for XSS
|
||||
commandObj.command = validator.trim(validator.escape(commandObj.rawData.msg));
|
||||
|
||||
//Return whether or not the shit was long enough
|
||||
return (validator.isLength(commandObj.rawData.msg, {min: 1, max: 255}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits raw chat/command data into seperate arrays, one by word-borders and words surrounded by word-borders
|
||||
* These arrays are used to handle further command/chat processing
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request
|
||||
*/
|
||||
splitCommand(commandObj){
|
||||
//Split string by words
|
||||
commandObj.commandArray = commandObj.command.split(/\b/g);//Split by word-borders
|
||||
commandObj.argumentArray = commandObj.command.match(/\b\w+\b/g);//Match by words surrounded by borders
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the server's Command Processor object to process the chat/command request.
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request
|
||||
*/
|
||||
async processServerCommand(commandObj){
|
||||
//If the raw message starts with '!' (skip commands that start with whitespace so people can send example commands in chat)
|
||||
if(commandObj.rawData.msg[0] == '!'){
|
||||
//if it isn't just an exclimation point, and we have a real command
|
||||
if(commandObj.argumentArray != null){
|
||||
//If the command processor knows what to do with whatever the fuck the user sent us
|
||||
if(this.commandProcessor[commandObj.argumentArray[0].toLowerCase()] != null){
|
||||
//Process the command and use the return value to set the sendflag (true if command valid)
|
||||
commandObj.sendFlag = await this.commandProcessor[commandObj.argumentArray[0].toLowerCase()](commandObj, this);
|
||||
}else{
|
||||
//Process as toke command if we didnt get a match from the standard server-side command processor
|
||||
commandObj.sendFlag = await this.tokebot.tokeProcessor(commandObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through links in message and marks them by link type for later use by client-side post-processing
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request
|
||||
*/
|
||||
async markLinks(commandObj){
|
||||
//Setup the links array
|
||||
commandObj.links = [];
|
||||
|
||||
//For each link sent from the client
|
||||
//this.rawData.links.forEach((link) => {
|
||||
for (const link of commandObj.rawData.links){
|
||||
//Add a marked link object to our links array
|
||||
commandObj.links.push(await linkUtils.markLink(link));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-creates message string from processed Command Array
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request
|
||||
*/
|
||||
async prepMessage(commandObj){
|
||||
//Create message from commandArray
|
||||
commandObj.message = commandObj.commandArray.join('').trimStart();
|
||||
//Validate links and mark them by embed type
|
||||
await this.markLinks(commandObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Relays chat to channel via parent Chat Handler object
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request
|
||||
*/
|
||||
sendChat(commandObj){
|
||||
//FUCKIN' SEND IT!
|
||||
this.chatHandler.relayUserChat(commandObj.socket, commandObj.message, commandObj.chatType, commandObj.links);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing global server-side chat/command processing logic
|
||||
*/
|
||||
class commandProcessor{
|
||||
/**
|
||||
* Instantiates a commandProcessor object
|
||||
* @param {channelManager} server - Parent Server Object
|
||||
* @param {chatHandler} chatHandler - Parent Chat Handler Object
|
||||
*/
|
||||
constructor(server, chatHandler){
|
||||
this.server = server;
|
||||
this.chatHandler = chatHandler;
|
||||
}
|
||||
|
||||
//Command keywords get run through .toLowerCase(), so we should use lowercase method names for command methods
|
||||
/**
|
||||
* Command Processor method to handle the '!whisper' command
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request
|
||||
* @returns {Boolean} True to enable send flag
|
||||
*/
|
||||
whisper(commandObj){
|
||||
//splice out our command
|
||||
commandObj.commandArray.splice(0,2);
|
||||
|
||||
//Mark out the current message as a whisper
|
||||
commandObj.chatType = 'whisper';
|
||||
|
||||
//Make sure to throw the send flag
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Command Processor method to handle the '!spoiler' command
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request
|
||||
* @returns {Boolean} True to enable send flag
|
||||
*/
|
||||
spoiler(commandObj){
|
||||
//splice out our command
|
||||
commandObj.commandArray.splice(0,2);
|
||||
|
||||
//Mark out the current message as a spoiler
|
||||
commandObj.chatType = 'spoiler';
|
||||
|
||||
//Make sure to throw the send flag
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Command Processor method to handle the '!strikethrough' command
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request
|
||||
* @returns {Boolean} True to enable send flag
|
||||
*/
|
||||
strikethrough(commandObj){
|
||||
//splice out our command
|
||||
commandObj.commandArray.splice(0,2);
|
||||
|
||||
//Mark out the current message as a spoiler
|
||||
commandObj.chatType = 'strikethrough';
|
||||
|
||||
//Make sure to throw the send flag
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Command Processor method to handle the '!bold' command
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request
|
||||
* @returns {Boolean} True to enable send flag
|
||||
*/
|
||||
bold(commandObj){
|
||||
//splice out our command
|
||||
commandObj.commandArray.splice(0,2);
|
||||
|
||||
//Mark out the current message as a spoiler
|
||||
commandObj.chatType = 'bold';
|
||||
|
||||
//Make sure to throw the send flag
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Command Processor method to handle the '!italics' command
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request
|
||||
* @returns {Boolean} True to enable send flag
|
||||
*/
|
||||
italics(commandObj){
|
||||
//splice out our command
|
||||
commandObj.commandArray.splice(0,2);
|
||||
|
||||
//Mark out the current message as a spoiler
|
||||
commandObj.chatType = 'italics';
|
||||
|
||||
//Make sure to throw the send flag
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Command Processor method to handle the '!announce' command
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request
|
||||
* @returns {Boolean} True to enable send flag on un-authorized call to shame the user
|
||||
*/
|
||||
async announce(commandObj, preprocessor){
|
||||
//Get the current channel from the database
|
||||
const chanDB = await channelModel.findOne({name: commandObj.socket.chan});
|
||||
|
||||
//Check if the user has permission, and publicly shame them if they don't (lmao)
|
||||
if(chanDB != null && await chanDB.permCheck(commandObj.socket.user, 'announce')){
|
||||
//splice out our command
|
||||
commandObj.commandArray.splice(0,2);
|
||||
|
||||
//Prep the message using pre-processor functions chat-handling
|
||||
await preprocessor.prepMessage(commandObj);
|
||||
|
||||
//send it
|
||||
this.chatHandler.relayChannelAnnouncement(commandObj.socket.chan, commandObj.message, commandObj.links);
|
||||
|
||||
//throw send flag
|
||||
return false;
|
||||
}
|
||||
|
||||
//throw send flag
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Command Processor method to handle the '!serverannounce' command
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request
|
||||
* @returns {Boolean} True to enable send flag on un-authorized call to shame the user
|
||||
*/
|
||||
async serverannounce(commandObj, preprocessor){
|
||||
//Check if the user has permission, and publicly shame them if they don't (lmao)
|
||||
if(await permissionModel.permCheck(commandObj.socket.user, 'announce')){
|
||||
//splice out our command
|
||||
commandObj.commandArray.splice(0,2);
|
||||
|
||||
//Prep the message using pre-processor functions for chat-handling
|
||||
await preprocessor.prepMessage(commandObj);
|
||||
|
||||
//send it
|
||||
this.chatHandler.relayServerAnnouncement(commandObj.message, commandObj.links);
|
||||
|
||||
//disble send flag
|
||||
return false;
|
||||
}
|
||||
|
||||
//throw send flag
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Command Processor method to handle the '!resettoke' command
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request
|
||||
* @returns {Boolean} True to enable send flag on un-authorized call to shame the user
|
||||
*/
|
||||
async resettoke(commandObj, preprocessor){
|
||||
//Check if the user has permission, and publicly shame them if they don't (lmao)
|
||||
if(await permissionModel.permCheck(commandObj.socket.user, 'resetToke')){
|
||||
//Acknowledge command
|
||||
this.chatHandler.relayTokeWhisper(commandObj.socket, 'Toke cooldown reset.');
|
||||
|
||||
//Tell tokebot to reset the toke
|
||||
preprocessor.tokebot.resetToke();
|
||||
|
||||
//disable send flag
|
||||
return false;
|
||||
}
|
||||
|
||||
//throw send flag
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Command Processor method to handle the '!clear' command
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request
|
||||
* @returns {Boolean} True to enable send flag on un-authorized call to shame the user
|
||||
*/
|
||||
async clear(commandObj){
|
||||
//Get the current channel from the database
|
||||
const chanDB = await channelModel.findOne({name: commandObj.socket.chan});
|
||||
|
||||
//Check if the user has permission, and publicly shame them if they don't (lmao)
|
||||
if(await chanDB.permCheck(commandObj.socket.user, 'clearChat')){
|
||||
//Send off the command
|
||||
this.chatHandler.clearChat(commandObj.socket.chan, commandObj.argumentArray[1]);
|
||||
//disable send flag
|
||||
return false;
|
||||
}
|
||||
|
||||
//throw send flag
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Command Processor method to handle the '!kick' command
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request
|
||||
* @returns {Boolean} True to enable send flag on un-authorized call to shame the user
|
||||
*/
|
||||
async kick(commandObj){
|
||||
//Get the current channel from the database
|
||||
const chanDB = await channelModel.findOne({name: commandObj.socket.chan});
|
||||
|
||||
//Check if the user has permission, and publicly shame them if they don't (lmao)
|
||||
if(await chanDB.permCheck(commandObj.socket.user, 'kickUser')){
|
||||
//Get username from argument array
|
||||
const username = commandObj.argumentArray[1];
|
||||
|
||||
//Get channel
|
||||
const channel = this.server.activeChannels.get(commandObj.socket.chan);
|
||||
|
||||
//get initiator and target user objects
|
||||
const initiator = channel.userList.get(commandObj.socket.user.user);
|
||||
const target = channel.userList.get(username);
|
||||
|
||||
//get initiator and target override abilities
|
||||
const override = await permissionModel.overrideCheck(commandObj.socket.user, 'kickUser');
|
||||
const targetOverride = await permissionModel.overrideCheck(target, 'kickUser');
|
||||
|
||||
//If there is no user
|
||||
if(target == null){
|
||||
//silently drop the command
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//If the user is capable of overriding this permission based on site permissions
|
||||
if(override || targetOverride){
|
||||
//If the site rank is equal
|
||||
if(permissionModel.rankToNum(initiator.rank) == permissionModel.rankToNum(target.rank)){
|
||||
//compare chan rank
|
||||
if(permissionModel.rankToNum(initiator.chanRank) <= permissionModel.rankToNum(target.chanRank)){
|
||||
//shame the person running it
|
||||
return true;
|
||||
}
|
||||
//otherwise
|
||||
}else{
|
||||
//compare site rank
|
||||
if(permissionModel.rankToNum(initiator.rank) <= permissionModel.rankToNum(target.rank)){
|
||||
//shame the person running it
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//If the target has a higher chan rank than the initiator
|
||||
if(permissionModel.rankToNum(initiator.chanRank) <= permissionModel.rankToNum(target.chanRank)){
|
||||
//shame the person running it
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Splice out kick
|
||||
commandObj.commandArray.splice(0,4)
|
||||
|
||||
//Get collect reason
|
||||
var reason = commandObj.commandArray.join('');
|
||||
|
||||
//If no reason was given
|
||||
if(reason == ''){
|
||||
//Fill in a generic reason
|
||||
reason = "You have been kicked from the channel!";
|
||||
}
|
||||
|
||||
//Kick the user
|
||||
target.disconnect(reason);
|
||||
|
||||
//throw send flag
|
||||
return false;
|
||||
}
|
||||
|
||||
//throw send flag
|
||||
return true;
|
||||
}
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
341
www/doc/app_channel_connectedUser.js.html
Normal file
341
www/doc/app_channel_connectedUser.js.html
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: app/channel/connectedUser.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: app/channel/connectedUser.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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 config = require('../../../config.json');
|
||||
const channelModel = require('../../schemas/channel/channelSchema');
|
||||
const permissionModel = require('../../schemas/permissionSchema');
|
||||
const flairModel = require('../../schemas/flairSchema');
|
||||
const emoteModel = require('../../schemas/emoteSchema');
|
||||
const { userModel } = require('../../schemas/user/userSchema');
|
||||
|
||||
/**
|
||||
* Class representing a single user connected to a channel
|
||||
*/
|
||||
module.exports = class{
|
||||
/**
|
||||
* Instantiates a connectedUser object
|
||||
* @param {Mongoose.Document} userDB - User document to re-hydrate user from
|
||||
* @param {PemissionModel.chanRank} chanRank - Enum representing user channel rank
|
||||
* @param {String} - Channel the user is connecting to
|
||||
* @param {Socket} socket - Socket associated with the users connection
|
||||
*/
|
||||
constructor(userDB, chanRank, channel, socket){
|
||||
this.id = userDB.id;
|
||||
this.user = userDB.user;
|
||||
this.rank = userDB.rank;
|
||||
this.highLevel = userDB.highLevel;
|
||||
|
||||
//Check to make sure users flair entry from DB is good
|
||||
if(userDB.flair != null){
|
||||
//Use flair from DB
|
||||
this.flair = userDB.flair.name;
|
||||
//Otherwise
|
||||
}else{
|
||||
//Gracefully default to classic
|
||||
this.flair = 'classic';
|
||||
}
|
||||
|
||||
this.chanRank = chanRank;
|
||||
this.channel = channel;
|
||||
this.sockets = [socket.id];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles server-side initialization for new connections from a specific user
|
||||
* @param {Mongoose.Document} userDB - User Document Passthrough to save on DB Access
|
||||
* @param {Mongoose.Document} chanDB - Channnel Document Passthrough to save on DB Access
|
||||
* @param {Socket} socket - Requesting Socket
|
||||
*/
|
||||
async handleConnection(userDB, chanDB, socket){
|
||||
//send metadata to client
|
||||
this.sendClientMetadata();
|
||||
|
||||
//Send out emotes
|
||||
this.sendSiteEmotes();
|
||||
this.sendChanEmotes(chanDB);
|
||||
this.sendPersonalEmotes(userDB);
|
||||
|
||||
//Send out used tokes
|
||||
this.sendUsedTokes(userDB);
|
||||
|
||||
//Send out the currently playing item
|
||||
this.channel.queue.sendMedia(socket);
|
||||
|
||||
//If we're proxied
|
||||
if(config.proxied){
|
||||
//Tattoo hashed IP address from reverse proxy to user account for seven days
|
||||
await userDB.tattooIPRecord(socket.handshake.headers['x-forwarded-for']);
|
||||
}else{
|
||||
//Tattoo hashed IP address to user account for seven days
|
||||
await userDB.tattooIPRecord(socket.handshake.address);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through all known connections for a given user, running them through a supplied callback function
|
||||
* @param {Function} cb - Callback to call against found sockets for a given user
|
||||
*/
|
||||
socketCrawl(cb){
|
||||
//Crawl through user's sockets (lol)
|
||||
this.sockets.forEach((sockid) => {
|
||||
//get socket based on ID
|
||||
const socket = this.channel.server.io.sockets.sockets.get(sockid);
|
||||
//Callback with socket
|
||||
cb(socket);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Emits an event to all known sockets for a given user
|
||||
*
|
||||
* My brain keeps going back to using dynamic per-user namespaces for this
|
||||
* but everytime i look into it I come to the conclusion that it's a bad idea, then I toy with making chans namespaces
|
||||
* and using per-user channels for this, but what of gold or mod-only features? or games?
|
||||
* No matter what it'd probably end up hacky, as namespaces where meant for splitting app logic not user comms (like rooms).
|
||||
* at the end of the day there has to be some penance for decent multi-session handling on-top of a library that doesn't do it.
|
||||
* Having to crawl through these sockets is that. Because the other ways seem more gross somehow.
|
||||
* @param {String} eventName - Event name to emit to client sockets
|
||||
* @param {Object} data - Data to emit to client sockets
|
||||
*/
|
||||
emit(eventName, data){
|
||||
this.socketCrawl((socket)=>{
|
||||
//Ensure our socket is initialized
|
||||
if(socket != null){
|
||||
socket.emit(eventName, data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnects all sockets for a given user
|
||||
* @param {String} reason - Reason for being disconnected
|
||||
* @param {String} type - Disconnection Type
|
||||
*/
|
||||
disconnect(reason, type = "Disconnected"){
|
||||
this.emit("kick",{type, reason});
|
||||
this.socketCrawl((socket)=>{socket.disconnect()});
|
||||
}
|
||||
|
||||
//This is the big first push upon connection
|
||||
//It should only fire once, so things that only need to be sent once can be slapped into here
|
||||
/**
|
||||
* Sends glut of required initial metadata to the client upon a new connection
|
||||
* @param {Mongoose.Document} userDB - User Document Passthrough to save on DB Access
|
||||
* @param {Mongoose.Document} chanDB - Channnel Document Passthrough to save on DB Access
|
||||
*/
|
||||
async sendClientMetadata(userDB, chanDB){
|
||||
//Get flairList from DB and setup flairList array
|
||||
const flairListDB = await flairModel.find({});
|
||||
var flairList = [];
|
||||
|
||||
//if we wherent handed a user document
|
||||
if(userDB == null){
|
||||
//Pull it based on user name
|
||||
userDB = await userModel.findOne({user: this.user});
|
||||
}
|
||||
|
||||
//if we wherent handed a channel document
|
||||
if(chanDB == null){
|
||||
//Pull it based on channel name
|
||||
chanDB = await channelModel.findOne({name: this.channel.name});
|
||||
}
|
||||
|
||||
//If our perm map is un-initiated
|
||||
//can't set this in constructor easily since it's asyncornous
|
||||
//need to wait for it to complete before sending this off, but shouldnt re-do the wait for later connections
|
||||
if(this.permMap == null){
|
||||
//Grab perm map
|
||||
this.permMap = await chanDB.getPermMapByUserDoc(userDB);
|
||||
}
|
||||
|
||||
//Setup our userObj
|
||||
const userObj = {
|
||||
id: this.id,
|
||||
user: this.user,
|
||||
rank: this.rank,
|
||||
chanRank: this.chanRank,
|
||||
highLevel: this.highLevel,
|
||||
permMap: {
|
||||
site: Array.from(this.permMap.site),
|
||||
chan: Array.from(this.permMap.chan),
|
||||
},
|
||||
flair: this.flair,
|
||||
}
|
||||
|
||||
//For each flair listed in the Database
|
||||
flairListDB.forEach((flair)=>{
|
||||
//Check if the user has permission to use the current flair
|
||||
if(permissionModel.rankToNum(flair.rank) <= permissionModel.rankToNum(this.rank)){
|
||||
//If so push a light version of the flair object into our final flair list
|
||||
flairList.push({
|
||||
name: flair.name,
|
||||
displayName: flair.displayName
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//Get schedule as a temporary array
|
||||
const queue = await this.channel.queue.prepQueue(chanDB);
|
||||
|
||||
//Get schedule lock status
|
||||
const queueLock = this.channel.queue.locked;
|
||||
|
||||
//Get chat buffer
|
||||
const chatBuffer = this.channel.chatBuffer.buffer;
|
||||
|
||||
//Send off the metadata to our user's clients
|
||||
this.emit("clientMetadata", {user: userObj, flairList, queue, queueLock, chatBuffer});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send copy of site emotes to the user
|
||||
*/
|
||||
async sendSiteEmotes(){
|
||||
//Get emote list from DB
|
||||
const emoteList = await emoteModel.getEmotes();
|
||||
|
||||
//Send it off to the user
|
||||
this.emit('siteEmotes', emoteList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send copy of channel emotes to the user
|
||||
* @param {Mongoose.Document} chanDB - Channnel Document Passthrough to save on DB Access
|
||||
*/
|
||||
async sendChanEmotes(chanDB){
|
||||
//if we wherent handed a channel document
|
||||
if(chanDB == null){
|
||||
//Pull it based on channel name
|
||||
chanDB = await channelModel.findOne({name: this.channel.name});
|
||||
}
|
||||
|
||||
//Pull emotes from channel
|
||||
const emoteList = chanDB.getEmotes();
|
||||
|
||||
//Send it off to the user
|
||||
this.emit('chanEmotes', emoteList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send copy of channel emotes to the user
|
||||
* @param {Mongoose.Document} userDB - User Document Passthrough to save on DB Access
|
||||
*/
|
||||
async sendPersonalEmotes(userDB){
|
||||
//if we wherent handed a user document
|
||||
if(userDB == null){
|
||||
//Pull it based on user name
|
||||
userDB = await userModel.findOne({user: this.user});
|
||||
}
|
||||
|
||||
//Pull emotes from channel
|
||||
const emoteList = userDB.getEmotes();
|
||||
|
||||
//Send it off to the user
|
||||
this.emit('personalEmotes', emoteList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send copy of channel emotes to the user
|
||||
* @param {Mongoose.Document} userDB - User Document Passthrough to save on DB Access
|
||||
*/
|
||||
async sendUsedTokes(userDB){
|
||||
//if we wherent handed a user document
|
||||
if(userDB == null){
|
||||
//Pull it based on user name
|
||||
userDB = await userModel.findOne({user: this.user});
|
||||
}
|
||||
|
||||
//Create array of used toks from toke map and send it out to the user
|
||||
this.emit('usedTokes',{
|
||||
tokes: Array.from(userDB.tokes.keys())
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set flair for a given user and broadcast update to clients
|
||||
* @param {String} flair - Flair string to update user's flair to
|
||||
*/
|
||||
updateFlair(flair){
|
||||
this.flair = flair;
|
||||
|
||||
this.channel.broadcastUserList();
|
||||
this.sendClientMetadata();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set high level for a given user and broadcast update to clients
|
||||
* @param {Number} highLevel - Number to update user's high-level to
|
||||
*/
|
||||
updateHighLevel(highLevel){
|
||||
this.highLevel = highLevel;
|
||||
|
||||
//TODO: show high-level in userlist
|
||||
this.channel.broadcastUserList();
|
||||
this.sendClientMetadata();
|
||||
}
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
90
www/doc/app_channel_media_media.js.html
Normal file
90
www/doc/app_channel_media_media.js.html
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: app/channel/media/media.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: app/channel/media/media.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
/**
|
||||
* Object representing a piece of media
|
||||
*/
|
||||
module.exports = class{
|
||||
/**
|
||||
* Creates a new media object from scraped information
|
||||
* @param {String} title - Chosen title of media
|
||||
* @param {String} fileName - Original filename/title of media provided by source
|
||||
* @param {String} url - Original URL to file
|
||||
* @param {String} id - Video ID from source (IE: youtube watch code/archive.org file path)
|
||||
* @param {String} type - Original video source
|
||||
* @param {Number} duration - Length of media in seconds
|
||||
* @param {String} rawLink - URL to raw file copy of media, not applicable to all sources
|
||||
*/
|
||||
constructor(title, fileName, url, id, type, duration, rawLink = url){
|
||||
this.title = title;
|
||||
this.fileName = fileName
|
||||
this.url = url;
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.duration = duration;
|
||||
this.rawLink = rawLink;
|
||||
}
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
1187
www/doc/app_channel_media_playlistHandler.js.html
Normal file
1187
www/doc/app_channel_media_playlistHandler.js.html
Normal file
File diff suppressed because it is too large
Load diff
1802
www/doc/app_channel_media_queue.js.html
Normal file
1802
www/doc/app_channel_media_queue.js.html
Normal file
File diff suppressed because it is too large
Load diff
172
www/doc/app_channel_media_queuedMedia.js.html
Normal file
172
www/doc/app_channel_media_queuedMedia.js.html
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: app/channel/media/queuedMedia.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: app/channel/media/queuedMedia.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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 media = require('./media');
|
||||
|
||||
/**
|
||||
* Class extending media which represents a queued piece of media
|
||||
* @extends media
|
||||
*/
|
||||
module.exports = class extends media{
|
||||
/**
|
||||
* Creates a new queued media object
|
||||
* @param {Number} startTime - JS Epoch representing start time
|
||||
* @param {Number} startTimeStamp - Media start time stamp in seconds (relative to duration)
|
||||
* @param {Number} earlyEnd - Media end timestamp in seconds (relative to duration)
|
||||
* @param {String} uuid - Media object's unique identifier
|
||||
*/
|
||||
constructor(title, fileName, url, id, type, duration, rawLink, startTime, startTimeStamp = 0, earlyEnd, uuid){
|
||||
//Call derived constructor
|
||||
super(title, fileName, url, id, type, duration, rawLink);
|
||||
//Set media start time
|
||||
this.startTime = startTime;
|
||||
//Set the media start time stamp
|
||||
this.startTimeStamp = startTimeStamp;
|
||||
//Create empty variable to hold early end if media is stopped early
|
||||
this.earlyEnd = earlyEnd;
|
||||
//Set status for discriminator key
|
||||
this.status = 'queued';
|
||||
|
||||
//If we have a null uuid (can't use default argument because of 'this')
|
||||
if(uuid == null){
|
||||
//Generate id unique to this specific entry of this specific file within this specific channel's queue
|
||||
//That way even if we have six copies of the same video queued, we can still uniquely idenitify each instance
|
||||
this.genUUID();
|
||||
}else{
|
||||
this.uuid = uuid;
|
||||
}
|
||||
}
|
||||
|
||||
//statics
|
||||
/**
|
||||
* Creates a queuedMedia object from a media object
|
||||
* @param {media} media - Media object to queue
|
||||
* @param {Number} startTime - Start time formatted as a JS Epoch
|
||||
* @param {Number} startTimeStamp - Start time stamp in seconds
|
||||
* @returns {queuedMedia} queuedMedia object created from given media object
|
||||
*/
|
||||
static fromMedia(media, startTime, startTimeStamp){
|
||||
//Create and return queuedMedia object from given media object and arguments
|
||||
return new this(
|
||||
media.title,
|
||||
media.fileName,
|
||||
media.url,
|
||||
media.id,
|
||||
media.type,
|
||||
media.duration,
|
||||
media.rawLink,
|
||||
startTime,
|
||||
startTimeStamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts array of media objects into array of queuedMedia objects
|
||||
* @param {Array} mediaList - Array of media objects to queue
|
||||
* @param {Number} start - Start time formatted as JS Epoch
|
||||
* @returns Array of converted queued media objects
|
||||
*/
|
||||
static fromMediaArray(mediaList, start){
|
||||
//Queued Media List
|
||||
const queuedMediaList = [];
|
||||
//Start Time Offset
|
||||
let startOffset = 0;
|
||||
|
||||
for(let media of mediaList){
|
||||
//Convert mediaObj to queuedMedia and push to the back of the list
|
||||
queuedMediaList.push(this.fromMedia(media, start + startOffset, 0));
|
||||
|
||||
//Set start offset to end of the current item
|
||||
startOffset += (media.duration * 1000) + 5;
|
||||
}
|
||||
|
||||
return queuedMediaList;
|
||||
}
|
||||
|
||||
//methods
|
||||
/**
|
||||
* Generates new unique identifier for queued media
|
||||
*/
|
||||
genUUID(){
|
||||
this.uuid = crypto.randomUUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* return the end time of a given queuedMedia object
|
||||
* @param {boolean} fullTime - Overrides early ends
|
||||
* @returns end time of given queuedMedia object
|
||||
*/
|
||||
getEndTime(fullTime = false){
|
||||
//If we have an early ending
|
||||
if(this.earlyEnd == null || fullTime){
|
||||
//Calculate our ending
|
||||
return this.startTime + ((this.duration - this.startTimeStamp) * 1000);
|
||||
}else{
|
||||
//Return our early end
|
||||
return this.startTime + (this.earlyEnd * 1000);
|
||||
}
|
||||
}
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
281
www/doc/app_channel_tokebot.js.html
Normal file
281
www/doc/app_channel_tokebot.js.html
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: app/channel/tokebot.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: app/channel/tokebot.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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 tokeCommandModel = require('../../schemas/tokebot/tokeCommandSchema');
|
||||
const {userModel} = require('../../schemas/user/userSchema');
|
||||
const statSchema = require('../../schemas/statSchema');
|
||||
|
||||
|
||||
/**
|
||||
* Class containing global server-side tokebot logic
|
||||
*/
|
||||
module.exports = class tokebot{
|
||||
/**
|
||||
* Instantiates a tokebot object
|
||||
* @param {channelManager} server - Parent Server Object
|
||||
* @param {chatHandler} chatHandler - Parent Chat Handler Object
|
||||
*/
|
||||
constructor(server, chatHandler){
|
||||
//Set parents
|
||||
this.server = server;
|
||||
this.chatHandler = chatHandler;
|
||||
|
||||
//Set timeouts to null
|
||||
this.tokeTimer = null;
|
||||
this.cooldownTimer = null;
|
||||
|
||||
//Set start times
|
||||
this.tokeTime = 60;
|
||||
this.cooldownTime = 120;
|
||||
|
||||
//Create counter variable
|
||||
this.tokeCounter = 0;
|
||||
this.cooldownCounter = 0;
|
||||
|
||||
//Create tokers list
|
||||
this.tokers = new Map();
|
||||
|
||||
//Load in toke commands from the DB
|
||||
this.refreshCommands();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads toke commands from DB into RAM-based toke command store
|
||||
*/
|
||||
async refreshCommands(){
|
||||
//Pull Command Strings from DB
|
||||
this.tokeCommands = await tokeCommandModel.getCommandStrings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes toke commands from Command Pre-Processor
|
||||
* @param {Object} commandObj - Object representing a single given command/chat request, passed down from the Command Pre-Processor
|
||||
* @returns {Boolean} True if the toke is an invalid toke command (tells Command Pre-Processor to send command as chat)
|
||||
*/
|
||||
tokeProcessor(commandObj){
|
||||
//Check for site-wide toke commands
|
||||
if(this.tokeCommands.indexOf(commandObj.argumentArray[0].toLowerCase()) != -1){
|
||||
//Seems lame to set a bool in an if statement but this would've made a really ugly turinary
|
||||
var foundToke = true;
|
||||
}else if(commandObj.argumentArray[0].toLowerCase() == 'r'){
|
||||
//Find the users active channel
|
||||
const activeChan = this.server.activeChannels.get(commandObj.socket.chan);
|
||||
|
||||
//Combile site-wide and channel tokes into one list
|
||||
const tokeList = this.tokeCommands.concat(activeChan.tokeCommands);
|
||||
|
||||
//Pick a random number between 0 and one less than the number of tokes
|
||||
const foundIndex = Math.round(Math.random() * (tokeList.length - 1));
|
||||
|
||||
//Set override command argument 0 w/ the found toke
|
||||
commandObj.argumentArray[0] = tokeList[foundIndex];
|
||||
|
||||
//throw toke flag
|
||||
var foundToke = true;
|
||||
}else{
|
||||
//Find the users active channel
|
||||
const activeChan = this.server.activeChannels.get(commandObj.socket.chan);
|
||||
|
||||
//Check if they're using a channel-only toke
|
||||
//This should be safe to do without a null check but someone prove me wrong lmao
|
||||
var foundToke = (activeChan.tokeCommands.indexOf(commandObj.argumentArray[0].toLowerCase()) != -1);
|
||||
}
|
||||
|
||||
|
||||
//If we found a toke
|
||||
if(foundToke){
|
||||
//If there is no active toke or cooldown (new toke)
|
||||
if(this.tokeTimer == null && this.cooldownTimer == null){
|
||||
//Call-out toke start
|
||||
this.chatHandler.relayTokeCallout(`A group toke has been started by ${commandObj.socket.user.user} from #${commandObj.socket.chan}! We'll be taking a toke in 60 seconds - join in by posting !${commandObj.argumentArray[0]}`);
|
||||
//Set a full minute on our toke timer
|
||||
this.tokeCounter = this.tokeTime;
|
||||
|
||||
//Add the toking user to the tokers map
|
||||
this.tokers.set(commandObj.socket.user.user, commandObj.argumentArray[0].toLowerCase());
|
||||
|
||||
//kick-off the count-down
|
||||
this.tokeTimer = setTimeout(this.countdown.bind(this), 1000)
|
||||
//If the tokeTimer is popping but the cooldownTimer has fucked off (a toke is in progress)
|
||||
}else if(this.cooldownTimer == null){
|
||||
//look for user in tokers map
|
||||
const foundToker = this.tokers.get(commandObj.socket.user.user);
|
||||
|
||||
//if the user has not yet joined the toke
|
||||
if(foundToker == null){
|
||||
//Call-out toke join
|
||||
this.chatHandler.relayTokeCallout(`${commandObj.socket.user.user} has joined the toke from #${commandObj.socket.chan}! Post !${commandObj.argumentArray[0]} to take part!`);
|
||||
|
||||
//Add the toking user to the tokers map
|
||||
this.tokers.set(commandObj.socket.user.user, commandObj.argumentArray[0].toLowerCase());
|
||||
//If the user is already in the toke
|
||||
}else{
|
||||
//Tell them to fuck off
|
||||
this.chatHandler.relayTokeWhisper(commandObj.socket, "You're already taking part in this toke!");
|
||||
}
|
||||
|
||||
//Otherwise (there isn't a toke timer, but there is a cooldown timer. AKA: we're in cooldown)
|
||||
}else{
|
||||
//if the cooldownTimer exists (we're cooling down the toke)
|
||||
this.chatHandler.relayTokeWhisper(commandObj.socket, `Please wait ${this.cooldownCounter} seconds before starting a new group toke.`);
|
||||
}
|
||||
|
||||
//Toke command found, and there isn't any extra text, don't send as chat (re-create fore.st tokebot behaviour)
|
||||
return (commandObj.command != `!${commandObj.argumentArray[0]}` && commandObj.command != '!r');
|
||||
}else{
|
||||
//No toke found, send it down the line, because shaming the user is funny
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called each second during the toke. Handles decrementing the timer variable, and countdown end logic.
|
||||
*/
|
||||
countdown(){
|
||||
//If we're in the last three seconds
|
||||
if(this.tokeCounter <= 3 && this.tokeCounter > 0){
|
||||
//Callout the last three seconds
|
||||
this.chatHandler.relayTokeCallout(`${this.tokeCounter}...`);
|
||||
//if the toke is over
|
||||
}else if(this.tokeCounter < 0){
|
||||
//if we had multiple tokers
|
||||
if(this.tokers.size > 1){
|
||||
//call out the toke
|
||||
this.chatHandler.relayTokeCallout(`Take a toke ${Array.from(this.tokers.keys()).join(', ')}! ${this.tokers.size} tokers!`);
|
||||
//if we only had one toker
|
||||
}else{
|
||||
//call out the solo toke
|
||||
this.chatHandler.relayTokeCallout(`Take a toke ${Array.from(this.tokers.keys())[0]}.`);
|
||||
}
|
||||
|
||||
//Asynchronously tattoo the toke into the users documents within the database so that tokebot doesn't have to wait or worry about DB transactions
|
||||
userModel.tattooToke(this.tokers);
|
||||
//Do the same for the global stat schema
|
||||
statSchema.tattooToke(this.tokers);
|
||||
|
||||
//Set the toke cooldown
|
||||
this.cooldownCounter = this.cooldownTime;
|
||||
this.cooldownTimer = setTimeout(this.cooldown.bind(this), 1000);
|
||||
|
||||
//Empty out the tokers array
|
||||
this.tokers = new Map;
|
||||
|
||||
//Null out our timer
|
||||
this.tokeTimer = null;
|
||||
|
||||
//return the function before it can continue
|
||||
return;
|
||||
}
|
||||
|
||||
//Decrement toke time
|
||||
this.tokeCounter--;
|
||||
//try again in another second
|
||||
this.tokeTimer = setTimeout(this.countdown.bind(this), 1000)
|
||||
}
|
||||
|
||||
/**
|
||||
* This method seems to be a vestage from a bygone era. We should remove it after documenting shit.
|
||||
* I would now, but I don't want to break shit in a comment-only commit.
|
||||
*/
|
||||
async asyncFinisher(){
|
||||
//Grab a copy of the tokers map before it gets cleared out
|
||||
const tokers = this.tokers;
|
||||
|
||||
//we need to wait for this so we don't send used tokes pre-maturely
|
||||
await userModel.tattooToke(tokers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs every second for 60 seconds after a toke
|
||||
*/
|
||||
cooldown(){
|
||||
//If the cooldown timer isn't over
|
||||
if(this.cooldownCounter > 0){
|
||||
//Decrement toke time
|
||||
this.cooldownCounter--;
|
||||
//try again in another second
|
||||
this.cooldownTimer = setTimeout(this.cooldown.bind(this), 1000);
|
||||
//If the cooldown is over
|
||||
}else{
|
||||
//Null out the cooldown timer
|
||||
this.cooldownTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets toke cooldowns early upon authorized request
|
||||
*/
|
||||
resetToke(){
|
||||
//Set cooldown to 0
|
||||
this.cooldownCounter = 0;
|
||||
|
||||
//Null out the timer
|
||||
this.cooldownTimer = null;
|
||||
}
|
||||
|
||||
}
|
||||
</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
338
www/doc/chat.html
Normal file
338
www/doc/chat.html
Normal file
|
|
@ -0,0 +1,338 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Class: chat</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Class: chat</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
|
||||
<header>
|
||||
|
||||
<h2><span class="attribs"><span class="type-signature"></span></span>chat<span class="signature">(user, flair, highLevel, msg, type, links)</span><span class="type-signature"></span></h2>
|
||||
|
||||
<div class="class-description">Class representing a single chat message</div>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
<article>
|
||||
<div class="container-overview">
|
||||
|
||||
|
||||
|
||||
|
||||
<h2>Constructor</h2>
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="chat"><span class="type-signature"></span>new chat<span class="signature">(user, flair, highLevel, msg, type, links)</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="description">
|
||||
Instantiates a chat message object
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Parameters:</h5>
|
||||
|
||||
|
||||
<table class="params">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
<th>Name</th>
|
||||
|
||||
|
||||
<th>Type</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th class="last">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>user</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">connectedUser</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last">User who sent the message</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>flair</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">String</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last">Flair ID String for the flair used to send the message</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>highLevel</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">Number</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last">Number representing current high level</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>msg</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">String</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last">Contents of the message, with links replaced with numbered file-seperator markers</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>type</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">String</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last">Message Type Identifier, used for client-side processing.</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>links</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">Array</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last">Array of URLs/Links included in the message.</td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="app_channel_chat.js.html">app/channel/chat.js</a>, <a href="app_channel_chat.js.html#line20">line 20</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
838
www/doc/chatBuffer.html
Normal file
838
www/doc/chatBuffer.html
Normal file
|
|
@ -0,0 +1,838 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Class: chatBuffer</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Class: chatBuffer</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
|
||||
<header>
|
||||
|
||||
<h2><span class="attribs"><span class="type-signature"></span></span>chatBuffer<span class="signature">(server, chanDB, channel)</span><span class="type-signature"></span></h2>
|
||||
|
||||
<div class="class-description">Class representing a stored chat buffer</div>
|
||||
|
||||
|
||||
</header>
|
||||
|
||||
<article>
|
||||
<div class="container-overview">
|
||||
|
||||
|
||||
|
||||
|
||||
<h2>Constructor</h2>
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="chatBuffer"><span class="type-signature"></span>new chatBuffer<span class="signature">(server, chanDB, channel)</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="description">
|
||||
Instantiates a new chat buffer for a given channel
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Parameters:</h5>
|
||||
|
||||
|
||||
<table class="params">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
<th>Name</th>
|
||||
|
||||
|
||||
<th>Type</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th class="last">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>server</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">channelManager</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last">Parent Server Object</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>chanDB</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">Mongoose.Document</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last">chanDB to rehydrate buffer from</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>channel</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">activeChannel</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last">Parent Channel Object</td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="app_channel_chatBuffer.js.html">app/channel/chatBuffer.js</a>, <a href="app_channel_chatBuffer.js.html#line22">line 22</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3 class="subsection-title">Methods</h3>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="handleBusyRoom"><span class="type-signature"></span>handleBusyRoom<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="description">
|
||||
Called after 5 minutes of solid activity
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="app_channel_chatBuffer.js.html">app/channel/chatBuffer.js</a>, <a href="app_channel_chatBuffer.js.html#line94">line 94</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="handleInactivity"><span class="type-signature"></span>handleInactivity<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="description">
|
||||
Called after 10 seconds of chat room inactivity
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="app_channel_chatBuffer.js.html">app/channel/chatBuffer.js</a>, <a href="app_channel_chatBuffer.js.html#line87">line 87</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="push"><span class="type-signature"></span>push<span class="signature">(chat)</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="description">
|
||||
Adds a given chat to the chat buffer in RAM and sets any appropriate timers for DB transactions
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Parameters:</h5>
|
||||
|
||||
|
||||
<table class="params">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
<th>Name</th>
|
||||
|
||||
|
||||
<th>Type</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th class="last">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>chat</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type"><a href="chat.html">chat</a></span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last">Chat object to commit to buffer</td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="app_channel_chatBuffer.js.html">app/channel/chatBuffer.js</a>, <a href="app_channel_chatBuffer.js.html#line57">line 57</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="saveDB"><span class="type-signature">(async) </span>saveDB<span class="signature">(reason, chanDB)</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="description">
|
||||
Saves RAM-Based buffer to Channel Document in DB
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h5>Parameters:</h5>
|
||||
|
||||
|
||||
<table class="params">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
<th>Name</th>
|
||||
|
||||
|
||||
<th>Type</th>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<th class="last">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>reason</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">String</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last">Reason for DB save, formatted as 'x minutes/seconds of in/activity', used for logging purposes</td>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
<tr>
|
||||
|
||||
<td class="name"><code>chanDB</code></td>
|
||||
|
||||
|
||||
<td class="type">
|
||||
|
||||
|
||||
<span class="param-type">Mongoose.Document</span>
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<td class="description last">Channel Doc to work with, can be left empty for method to auto-find through channel name.</td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="app_channel_chatBuffer.js.html">app/channel/chatBuffer.js</a>, <a href="app_channel_chatBuffer.js.html#line103">line 103</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="shift"><span class="type-signature"></span>shift<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="description">
|
||||
Removes the oldest item from the chat buffer
|
||||
|
||||
Was originally created in-case we needed to trigger timing functions
|
||||
|
||||
Left here since it seems like good form anywho, since this would be a private, or at least protected member in another language
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-source">Source:</dt>
|
||||
<dd class="tag-source"><ul class="dummy"><li>
|
||||
<a href="app_channel_chatBuffer.js.html">app/channel/chatBuffer.js</a>, <a href="app_channel_chatBuffer.js.html#line80">line 80</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
1840
www/doc/commandProcessor.html
Normal file
1840
www/doc/commandProcessor.html
Normal file
File diff suppressed because it is too large
Load diff
BIN
www/doc/fonts/OpenSans-Bold-webfont.eot
Normal file
BIN
www/doc/fonts/OpenSans-Bold-webfont.eot
Normal file
Binary file not shown.
1830
www/doc/fonts/OpenSans-Bold-webfont.svg
Normal file
1830
www/doc/fonts/OpenSans-Bold-webfont.svg
Normal file
File diff suppressed because it is too large
Load diff
|
After Width: | Height: | Size: 116 KiB |
BIN
www/doc/fonts/OpenSans-Bold-webfont.woff
Normal file
BIN
www/doc/fonts/OpenSans-Bold-webfont.woff
Normal file
Binary file not shown.
BIN
www/doc/fonts/OpenSans-BoldItalic-webfont.eot
Normal file
BIN
www/doc/fonts/OpenSans-BoldItalic-webfont.eot
Normal file
Binary file not shown.
1830
www/doc/fonts/OpenSans-BoldItalic-webfont.svg
Normal file
1830
www/doc/fonts/OpenSans-BoldItalic-webfont.svg
Normal file
File diff suppressed because it is too large
Load diff
|
After Width: | Height: | Size: 118 KiB |
BIN
www/doc/fonts/OpenSans-BoldItalic-webfont.woff
Normal file
BIN
www/doc/fonts/OpenSans-BoldItalic-webfont.woff
Normal file
Binary file not shown.
BIN
www/doc/fonts/OpenSans-Italic-webfont.eot
Normal file
BIN
www/doc/fonts/OpenSans-Italic-webfont.eot
Normal file
Binary file not shown.
1830
www/doc/fonts/OpenSans-Italic-webfont.svg
Normal file
1830
www/doc/fonts/OpenSans-Italic-webfont.svg
Normal file
File diff suppressed because it is too large
Load diff
|
After Width: | Height: | Size: 120 KiB |
BIN
www/doc/fonts/OpenSans-Italic-webfont.woff
Normal file
BIN
www/doc/fonts/OpenSans-Italic-webfont.woff
Normal file
Binary file not shown.
BIN
www/doc/fonts/OpenSans-Light-webfont.eot
Normal file
BIN
www/doc/fonts/OpenSans-Light-webfont.eot
Normal file
Binary file not shown.
1831
www/doc/fonts/OpenSans-Light-webfont.svg
Normal file
1831
www/doc/fonts/OpenSans-Light-webfont.svg
Normal file
File diff suppressed because it is too large
Load diff
|
After Width: | Height: | Size: 114 KiB |
BIN
www/doc/fonts/OpenSans-Light-webfont.woff
Normal file
BIN
www/doc/fonts/OpenSans-Light-webfont.woff
Normal file
Binary file not shown.
BIN
www/doc/fonts/OpenSans-LightItalic-webfont.eot
Normal file
BIN
www/doc/fonts/OpenSans-LightItalic-webfont.eot
Normal file
Binary file not shown.
1835
www/doc/fonts/OpenSans-LightItalic-webfont.svg
Normal file
1835
www/doc/fonts/OpenSans-LightItalic-webfont.svg
Normal file
File diff suppressed because it is too large
Load diff
|
After Width: | Height: | Size: 120 KiB |
BIN
www/doc/fonts/OpenSans-LightItalic-webfont.woff
Normal file
BIN
www/doc/fonts/OpenSans-LightItalic-webfont.woff
Normal file
Binary file not shown.
BIN
www/doc/fonts/OpenSans-Regular-webfont.eot
Normal file
BIN
www/doc/fonts/OpenSans-Regular-webfont.eot
Normal file
Binary file not shown.
1831
www/doc/fonts/OpenSans-Regular-webfont.svg
Normal file
1831
www/doc/fonts/OpenSans-Regular-webfont.svg
Normal file
File diff suppressed because it is too large
Load diff
|
After Width: | Height: | Size: 117 KiB |
BIN
www/doc/fonts/OpenSans-Regular-webfont.woff
Normal file
BIN
www/doc/fonts/OpenSans-Regular-webfont.woff
Normal file
Binary file not shown.
7386
www/doc/global.html
Normal file
7386
www/doc/global.html
Normal file
File diff suppressed because it is too large
Load diff
65
www/doc/index.html
Normal file
65
www/doc/index.html
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Home</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Home</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3> </h3>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
202454
www/doc/module.exports.html
Normal file
202454
www/doc/module.exports.html
Normal file
File diff suppressed because it is too large
Load diff
110
www/doc/schemas_channel_channelBanSchema.js.html
Normal file
110
www/doc/schemas_channel_channelBanSchema.js.html
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/channel/channelBanSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/channel/channelBanSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
|
||||
/**
|
||||
* DB Schema for Documents representing a user ban from a single channel
|
||||
*/
|
||||
const channelBanSchema = new mongoose.Schema({
|
||||
user: {
|
||||
type: mongoose.SchemaTypes.ObjectID,
|
||||
required: true,
|
||||
ref: "user"
|
||||
},
|
||||
banDate: {
|
||||
type: mongoose.SchemaTypes.Date,
|
||||
required: true,
|
||||
default: new Date()
|
||||
},
|
||||
expirationDays: {
|
||||
type: mongoose.SchemaTypes.Number,
|
||||
required: true,
|
||||
default: 14
|
||||
},
|
||||
banAlts: {
|
||||
type: mongoose.SchemaTypes.Boolean,
|
||||
required: true,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
//methods
|
||||
/**
|
||||
* Calculates days until ban expiration
|
||||
* @returns {Number} Days until the given ban expires
|
||||
*/
|
||||
channelBanSchema.methods.getDaysUntilExpiration = function(){
|
||||
//Get ban date
|
||||
const expirationDate = new Date(this.banDate);
|
||||
//Get expiration days and calculate expiration date
|
||||
expirationDate.setDate(expirationDate.getDate() + this.expirationDays);
|
||||
//Calculate and return days until ban expiration
|
||||
return ((expirationDate - new Date()) / (1000 * 60 * 60 * 24)).toFixed(1);
|
||||
}
|
||||
|
||||
module.exports = channelBanSchema;</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
178
www/doc/schemas_channel_channelPermissionSchema.js.html
Normal file
178
www/doc/schemas_channel_channelPermissionSchema.js.html
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/channel/channelPermissionSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/channel/channelPermissionSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
|
||||
/**
|
||||
* Rank Enum, lists all known permission ranks from lowest to highest.
|
||||
*
|
||||
* This originally belonged to the permissionSchema, but this avoids circular dependencies.
|
||||
*/
|
||||
const rankEnum = ["anon", "user", "gold", "bot", "mod", "admin"];
|
||||
|
||||
//Since this is intended to be used as a child schema for multiple parent schemas, we won't export it as a model
|
||||
/**
|
||||
* DB Schema for Sub-Document representing permission structure for a single channel
|
||||
*/
|
||||
const channelPermissionSchema = new mongoose.Schema({
|
||||
manageChannel: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
changeRank: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
changePerms: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
changeSettings: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
kickUser: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
banUser: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
announce: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
clearChat: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
editTokeCommands: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
editEmotes: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
scheduleMedia: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
clearSchedule:{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
scheduleAdmin:{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
editChannelPlaylists:{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
deleteChannel: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
//Only putting the rank enum out, all other logic should be handled by channelSchema methods to avoid circular dependencies
|
||||
//Alternatively if things get to big we can make it it's own util.
|
||||
channelPermissionSchema.statics.rankEnum = rankEnum;
|
||||
|
||||
module.exports = channelPermissionSchema;</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
943
www/doc/schemas_channel_channelSchema.js.html
Normal file
943
www/doc/schemas_channel_channelSchema.js.html
Normal file
|
|
@ -0,0 +1,943 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/channel/channelSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/channel/channelSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
const {validationResult, matchedData} = require('express-validator');
|
||||
|
||||
//Local Imports
|
||||
//Server
|
||||
const server = require('../../server');
|
||||
//DB Models
|
||||
const statModel = require('../statSchema');
|
||||
const {userModel} = require('../user/userSchema');
|
||||
const permissionModel = require('../permissionSchema');
|
||||
const emoteModel = require('../emoteSchema');
|
||||
//DB Schemas
|
||||
const channelPermissionSchema = require('./channelPermissionSchema');
|
||||
const channelBanSchema = require('./channelBanSchema');
|
||||
const queuedMediaSchema = require('./media/queuedMediaSchema');
|
||||
const playlistSchema = require('./media/playlistSchema');
|
||||
const chatSchema = require('./chatSchema');
|
||||
//Utils
|
||||
const { exceptionHandler, errorHandler } = require('../../utils/loggerUtils');
|
||||
|
||||
/**
|
||||
* DB Schema for Documents containing de-hydrated representations of Canopy Stream/Chat Channels
|
||||
*/
|
||||
const channelSchema = new mongoose.Schema({
|
||||
id: {
|
||||
type: mongoose.SchemaTypes.Number,
|
||||
required: true
|
||||
},
|
||||
name: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
//Calculate max length by the validator max length and the size of an escaped character
|
||||
maxLength: 50 * 6,
|
||||
default: 0
|
||||
},
|
||||
description: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
//Calculate max length by the validator max length and the size of an escaped character
|
||||
maxLength: 1000 * 6,
|
||||
default: 0
|
||||
},
|
||||
thumbnail: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
default: "/img/johnny.png"
|
||||
},
|
||||
settings: {
|
||||
hidden: {
|
||||
type: mongoose.SchemaTypes.Boolean,
|
||||
required: true,
|
||||
default: true
|
||||
},
|
||||
streamURL: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
permissions: {
|
||||
type: channelPermissionSchema,
|
||||
default: () => ({})
|
||||
},
|
||||
rankList: [{
|
||||
user: {
|
||||
type: mongoose.SchemaTypes.ObjectID,
|
||||
required: true,
|
||||
ref: "user"
|
||||
},
|
||||
rank: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
enum: permissionModel.rankEnum
|
||||
}
|
||||
}],
|
||||
tokeCommands: [{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true
|
||||
}],
|
||||
//Not re-using the site-wide schema because post/pre save should call different functions
|
||||
emotes: [{
|
||||
name:{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true
|
||||
},
|
||||
link:{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true
|
||||
},
|
||||
type:{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
enum: emoteModel.typeEnum,
|
||||
default: emoteModel.typeEnum[0]
|
||||
}
|
||||
}],
|
||||
media: {
|
||||
nowPlaying: queuedMediaSchema,
|
||||
scheduled: [queuedMediaSchema],
|
||||
//We should consider moving archived media and channel playlists to their own collections/models for preformances sake
|
||||
archived: [queuedMediaSchema],
|
||||
playlists: [playlistSchema],
|
||||
liveRemainder: {
|
||||
type: mongoose.SchemaTypes.UUID,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
//Thankfully we don't have to keep track of alts, ips, or deleted users so this should be a lot easier than site-wide bans :P
|
||||
banList: [channelBanSchema],
|
||||
chatBuffer: [chatSchema]
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Channel pre-save function. Ensures name requirements (for some reason, we should move that to the schema probably), kicks users after rank change, and handles housekeeping after adding tokes/emotes
|
||||
*/
|
||||
channelSchema.pre('save', async function (next){
|
||||
if(this.isModified("name")){
|
||||
if(this.name.match(/^[a-z0-9_\-.]+$/i) == null){
|
||||
throw loggerUtils.exceptionSmith("Username must only contain alpha-numerics and the following symbols: '-_.'", "validation");
|
||||
}
|
||||
}
|
||||
|
||||
//This entire block is just about finding users after rank-change and making sure they get kicked
|
||||
//Getting the affected user would be a million times easier elsewhere
|
||||
//But this ensures it happens every time channel rank gets changed no matter what
|
||||
if(this.isModified('rankList') && this.rankList != null){
|
||||
//Get the rank list before it was modified (gross but works, find a better way if you dont like it :P)
|
||||
var chanDB = await module.exports.findOne({_id: this._id});
|
||||
//Create empty variable for the found rank object
|
||||
var foundRank = null;
|
||||
if(chanDB != null){
|
||||
//If we're removing one
|
||||
if(chanDB.rankList.length > this.rankList.length){
|
||||
//Child/Parent is *WAY* tooo atomic family for my tastes :P
|
||||
var top = chanDB;
|
||||
var bottom = this;
|
||||
}else{
|
||||
//otherwise reverse the loops
|
||||
var top = this;
|
||||
var bottom = chanDB;
|
||||
}
|
||||
|
||||
//Populate the top doc
|
||||
await top.populate('rankList.user');
|
||||
|
||||
|
||||
//For each rank in the dommy-top copy of the rank list
|
||||
top.rankList.forEach((topObj) => {
|
||||
//Create empty variable for the matched rank
|
||||
var matchedRank = null;
|
||||
//For each rank in the subby-bottom copy of the rank list
|
||||
bottom.rankList.forEach((bottomObj) => {
|
||||
//So long as both users exist (we're not working with deleted users)
|
||||
if(topObj.user != null && bottomObj.user != null){
|
||||
//If it's the same user
|
||||
if(topObj.user._id.toString() == bottomObj.user._id.toString()){
|
||||
//matched rank found
|
||||
matchedRank = bottomObj;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//If matched rank is null or isn't the topObject rank
|
||||
if(matchedRank == null || matchedRank.rank != topObj.rank){
|
||||
//Set top object to found rank
|
||||
foundRank = topObj;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
//get relevant active channel
|
||||
const activeChan = server.channelManager.activeChannels.get(this.name);
|
||||
|
||||
//if the channel is online
|
||||
if(activeChan != null){
|
||||
//make sure we're not trying to kick a deleted user
|
||||
if(foundRank.user != null){
|
||||
//Get the relevant user connection
|
||||
const userConn = activeChan.userList.get(foundRank.user.user);
|
||||
//if the user is online
|
||||
if(userConn != null){
|
||||
//kick the user
|
||||
userConn.disconnect("Your channel rank has changed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//if the toke commands where changed
|
||||
if(this.isModified("tokeCommands")){
|
||||
//Get the active Channel object from the application side of the house
|
||||
const activeChannel = server.channelManager.activeChannels.get(this.name);
|
||||
|
||||
//If the channel is active
|
||||
if(activeChannel != null){
|
||||
//Reload the toke command list
|
||||
activeChannel.tokeCommands = this.tokeCommands;
|
||||
}
|
||||
}
|
||||
|
||||
//if emotes where modified
|
||||
if(this.isModified('emotes')){
|
||||
//Get the active Channel object from the application side of the house
|
||||
const activeChannel = server.channelManager.activeChannels.get(this.name);
|
||||
|
||||
//If the channel is active
|
||||
if(activeChannel != null){
|
||||
//Broadcast the emote list
|
||||
activeChannel.broadcastChanEmotes(this);
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
//statics
|
||||
/**
|
||||
* Registers a new channel to the DB
|
||||
* @param {Object} channelObj - Channel Object from Browser to register
|
||||
* @param {Mongoose.Document} ownerObj - DB Docuement representing user
|
||||
*/
|
||||
channelSchema.statics.register = async function(channelObj, ownerObj){
|
||||
const {name, description, thumbnail} = channelObj;
|
||||
|
||||
const chanDB = await this.findOne({ name });
|
||||
|
||||
if(chanDB){
|
||||
throw loggerUtils.exceptionSmith("Channel name already taken!", "validation");
|
||||
}else{
|
||||
const id = await statModel.incrementChannelCount();
|
||||
const rankList = [{
|
||||
user: ownerObj._id,
|
||||
rank: "admin"
|
||||
}];
|
||||
|
||||
const newChannelObj = {
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
thumbnail,
|
||||
rankList,
|
||||
media: {
|
||||
nowPlaying: null,
|
||||
scheduledMedia: [],
|
||||
archived: []
|
||||
}
|
||||
};
|
||||
|
||||
const newChannel = await this.create(newChannelObj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates Network-Friendly Browser-Digestable list of channels
|
||||
* @param {Boolean} includeHidden - Whether or not to include hidden channels within the list
|
||||
* @returns {Array} List of Network-Friendly Browser-Digestable Objects representing channels on the server
|
||||
*/
|
||||
channelSchema.statics.getChannelList = async function(includeHidden = false){
|
||||
const chanDB = await this.find({});
|
||||
var chanGuide = [];
|
||||
|
||||
//crawl through channels
|
||||
chanDB.forEach((channel) => {
|
||||
//For each channel, push an object with only the information we need to the channel guide
|
||||
if(!channel.settings.hidden || includeHidden){
|
||||
chanGuide.push({
|
||||
id: channel.id,
|
||||
name: channel.name,
|
||||
description: channel.description,
|
||||
thumbnail: channel.thumbnail
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
//return the channel guide
|
||||
return chanGuide;
|
||||
}
|
||||
|
||||
//Middleware for rank checks
|
||||
/**
|
||||
* Configurable Express Middleware for Per-Channel Endpoint Authorization
|
||||
*
|
||||
* Man, it would be really nice if express middleware actually supported async functions, you know, as if it where't still 2015 >:(
|
||||
* Also holy shit, sharing a function between two middleware functions is a nightmare
|
||||
* I'd rather just have this check chanField for '/c/' to handle channels in URL, fuck me this was obnoxious to write
|
||||
* @param {String} - Permission to check against
|
||||
* @param {String} - Name of channel to authorize against
|
||||
* @returns {Function} Express middleware function with arguments injected into logic
|
||||
*/
|
||||
channelSchema.statics.reqPermCheck = function(perm, chanField = "chanName"){
|
||||
return (req, res, next)=>{
|
||||
try{
|
||||
//Check validation result
|
||||
const validResult = validationResult(req);
|
||||
|
||||
//if our chan field is set to '/c/', telling us to check the URL
|
||||
if(chanField == '/c/'){
|
||||
//Rip the chan name out of the URL
|
||||
var chanName = (req.originalUrl.split('/c/')[1].replace('/settings',''));
|
||||
}else if(validResult.isEmpty()){
|
||||
//otherwise if our input is valid, use that
|
||||
var chanName = matchedData(req)[chanField];
|
||||
}else{
|
||||
//We didn't get /c/ and we got a bad input, time for shit to hit the fan!
|
||||
res.status(400);
|
||||
return res.send({errors: validResult.array()})
|
||||
}
|
||||
|
||||
//Find the related channel document, and handle it using a then() block
|
||||
this.findOne({name: chanName}).then((chanDB) => {
|
||||
//If we didnt find a channel
|
||||
if(chanDB == null){
|
||||
//FUCK
|
||||
return errorHandler(res, "You cannot check permissions against a non-existant channel!", 'Unauthorized', 401);
|
||||
}
|
||||
|
||||
//Run a perm check against the current user and permission
|
||||
chanDB.permCheck(req.session.user, perm).then((permitted) => {
|
||||
if(permitted){
|
||||
//if we're permitted, go on to fulfill the request
|
||||
next();
|
||||
}else{
|
||||
//If not, prevent the request from going through and tell them why
|
||||
return errorHandler(res, "You do not have a high enough rank to access this resource.", 'Unauthorized', 401);
|
||||
}
|
||||
});
|
||||
});
|
||||
}catch(err){
|
||||
return exceptionHandler(res, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedulable Function for Processing and Deleting Expired Channel-level User Bans
|
||||
*/
|
||||
channelSchema.statics.processExpiredBans = async function(){
|
||||
const chanDB = await this.find({});
|
||||
|
||||
for(let chanIndex in chanDB){
|
||||
//Pull channel from channels by index
|
||||
const channel = chanDB[chanIndex];
|
||||
|
||||
//channel.banList.forEach(async (ban, banIndex) => {
|
||||
for(let banIndex in channel.banList){
|
||||
//Pull ban from channel ban list
|
||||
const ban = channel.banList[banIndex];
|
||||
|
||||
//ignore permanent and non-expired bans
|
||||
if(ban.expirationDays >= 0 && ban.getDaysUntilExpiration() <= 0){
|
||||
//Get the index of the ban
|
||||
channel.banList.splice(banIndex,1);
|
||||
await channel.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//methods
|
||||
/**
|
||||
* Updates settings map for a given channel document
|
||||
* @param {Map} settingsMap - Map of settings updates to apply against channel document
|
||||
* @returns {Map} Map of all channel settings
|
||||
*/
|
||||
channelSchema.methods.updateSettings = async function(settingsMap){
|
||||
settingsMap.forEach((value, key) => {
|
||||
if(this.settings[key] == null){
|
||||
throw loggerUtils.exceptionSmith("Invalid channel setting.", "validation");
|
||||
}
|
||||
|
||||
this.settings[key] = value;
|
||||
})
|
||||
|
||||
await this.save();
|
||||
|
||||
return this.settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crawls through channel rank and runs a callback against the requested user's rank sub-doc
|
||||
* @param {Mongoose.Document} userDB - User DB Document to run the callback against
|
||||
* @param {Function} cb - Callback Function to call against the given users rank sub-doc
|
||||
*/
|
||||
channelSchema.methods.rankCrawl = async function(userDB,cb){
|
||||
//Crawl through channel rank list
|
||||
//TODO: replace this with rank check function shared with setRank
|
||||
this.rankList.forEach(async (rankObj, rankIndex) => {
|
||||
//check against user ID to speed things up
|
||||
if(rankObj.user != null && rankObj.user._id.toString() == userDB._id.toString()){
|
||||
//If we found a match, call back
|
||||
cb(rankObj, rankIndex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets users rank by User Doc
|
||||
* @param {Mongoose.Document} userDB - DB Document of user's channel rank to change
|
||||
* @param {String} rank - Channel rank to set user to
|
||||
* @returns {Array} Channel Rank List
|
||||
*/
|
||||
channelSchema.methods.setRank = async function(userDB,rank){
|
||||
//Create variable to store found ranks
|
||||
var foundRankIndex = null;
|
||||
|
||||
//Crawl through ranks to find matching index
|
||||
this.rankCrawl(userDB,(rankObj, rankIndex)=>{foundRankIndex = rankIndex});
|
||||
|
||||
//If we found an existing rank object
|
||||
if(foundRankIndex != null){
|
||||
if(rank == "user"){
|
||||
this.rankList.splice(foundRankIndex,1);
|
||||
}else{
|
||||
//otherwise, set the users rank
|
||||
this.rankList[foundRankIndex].rank = rank;
|
||||
}
|
||||
}else if(rank != "user"){
|
||||
//if the user rank object doesn't exist, and we're not setting to user
|
||||
//Create rank object based on input
|
||||
const rankObj = {
|
||||
user: userDB._id,
|
||||
rank: rank
|
||||
}
|
||||
|
||||
//Add it to rank list
|
||||
this.rankList.push(rankObj);
|
||||
}
|
||||
|
||||
|
||||
//Save our channel and return rankList
|
||||
await this.save();
|
||||
return this.rankList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates Network-Friendly Browser-Digestable channel rank list
|
||||
* @returns {Array} Network-Friendly Browser-Digestable channel rank list
|
||||
*/
|
||||
channelSchema.methods.getRankList = async function(){
|
||||
//Create an empty array to hold the user list
|
||||
const rankList = new Map()
|
||||
//Create temp rank list to replace the current one in the advant we have busted users
|
||||
let tempRankList = [];
|
||||
//Flag that lets us know we gotta save
|
||||
let reqSave = false;
|
||||
|
||||
//Populate the user objects in our ranklist based off of their DB ID's
|
||||
await this.populate('rankList.user');
|
||||
|
||||
//For each rank object in the rank list
|
||||
for(rankObjIndex in this.rankList){
|
||||
const rankObj = this.rankList[rankObjIndex];
|
||||
//If the use still exists
|
||||
if(rankObj.user != null){
|
||||
//Push current rank object to the temp rank list in the advant that it doesn't get saved
|
||||
tempRankList.push(rankObj);
|
||||
|
||||
//Create a new user object from rank object data
|
||||
const userObj = {
|
||||
id: rankObj.user.id,
|
||||
user: rankObj.user.user,
|
||||
img: rankObj.user.img,
|
||||
rank: rankObj.rank
|
||||
}
|
||||
|
||||
//Add our user object to the list
|
||||
rankList.set(rankObj.user.user, userObj);
|
||||
//Otherwise if it's an invalid rank for a deleted user
|
||||
}else{
|
||||
//Ignore the rank object and throw the save flag to save the temporary rank list
|
||||
reqSave = true;
|
||||
}
|
||||
}
|
||||
|
||||
//if we need to save the temp rank list
|
||||
if(reqSave){
|
||||
//set rank list
|
||||
this.rankList = tempRankList;
|
||||
//save
|
||||
await this.save();
|
||||
}
|
||||
|
||||
//return userList
|
||||
return rankList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets channel rank by user document
|
||||
* @param {Mongoose.Document} userDB - DB Document of User to pull Channel Rank of
|
||||
* @returns {String} Channel rank of requested user
|
||||
*/
|
||||
channelSchema.methods.getChannelRankByUserDoc = async function(userDB = null){
|
||||
var foundRank = null;
|
||||
|
||||
//Check to make sure userDB exists before going forward
|
||||
if(userDB == null){
|
||||
//If so this user is probably not signed in
|
||||
return "anon"
|
||||
}
|
||||
|
||||
//Crawl through ranks to find matching rank
|
||||
this.rankCrawl(userDB,(rankObj)=>{foundRank = rankObj});
|
||||
|
||||
//If we found an existing rank object
|
||||
if(foundRank != null){
|
||||
//return rank
|
||||
return foundRank.rank;
|
||||
}else{
|
||||
//default to "user" for registered users, and "anon" for anonymous
|
||||
if(userDB.rank == "anon"){
|
||||
return "anon";
|
||||
}else{
|
||||
return "user";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets channel rank by username
|
||||
* @param {String} user - Username of user to pull channel rank of
|
||||
* @returns {String} Channel rank of requested user
|
||||
*/
|
||||
channelSchema.methods.getChannelRank = async function(user){
|
||||
const userDB = await userModel.findOne({user: user.user});
|
||||
return await this.getChannelRankByUserDoc(userDB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a permission check against a specific channel permission for a given user by username
|
||||
* @param {String} user - Username of user to check against
|
||||
* @param {String} perm - Name of channel Permission to check against
|
||||
* @returns {Boolean} Whether or not the given user passes the given channel perm check
|
||||
*/
|
||||
channelSchema.methods.permCheck = async function (user, perm){
|
||||
//Set userDB to null if we wheren't passed a real user
|
||||
if(user != null){
|
||||
var userDB = await userModel.findOne({user: user.user});
|
||||
}else{
|
||||
var userDB = null;
|
||||
}
|
||||
|
||||
return await this.permCheckByUserDoc(userDB, perm)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a permission check against a specific channel permission for a given user by DB Document
|
||||
* @param {Mongoose.Document} userDB - DB Document of user to check against
|
||||
* @param {String} perm - Name of channel Permission to check against
|
||||
* @returns {Boolean} Whether or not the given user passes the given channel perm check
|
||||
*/
|
||||
channelSchema.methods.permCheckByUserDoc = async function(userDB, perm){
|
||||
//Get site-wide rank as number, default to anon for anonymous users
|
||||
const rank = userDB ? permissionModel.rankToNum(userDB.rank) : permissionModel.rankToNum("anon");
|
||||
//Get channel rank as number
|
||||
const chanRank = permissionModel.rankToNum(await this.getChannelRankByUserDoc(userDB));
|
||||
//Get channel permission rank requirement as number
|
||||
const permRank = permissionModel.rankToNum(this.permissions[perm]);
|
||||
//Get site-wide rank requirement to override as number
|
||||
const overrideRank = permissionModel.rankToNum((await permissionModel.getPerms()).channelOverrides[perm]);
|
||||
//Get channel perm check result
|
||||
const permCheck = (chanRank >= permRank);
|
||||
//Get site-wide override perm check result
|
||||
const overrideCheck = (rank >= overrideRank);
|
||||
|
||||
return (permCheck || overrideCheck);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates channel-wide permission map for a given user by user doc
|
||||
* @param {Mongoose.Document} userDB - DB Document representing a single user account
|
||||
* @returns {Object} Object containing two maps, one for channel perms, another for site-wide perms
|
||||
*/
|
||||
channelSchema.methods.getPermMapByUserDoc = async function(userDB){
|
||||
//Grap site-wide permissions
|
||||
const sitePerms = await permissionModel.getPerms();
|
||||
const siteMap = sitePerms.getPermMapByUserDoc(userDB);
|
||||
//Pull chan permissions keys
|
||||
let permTree = channelPermissionSchema.tree;
|
||||
let permMap = new Map();
|
||||
|
||||
//For each object in the temporary permissions object
|
||||
for(let perm of Object.keys(permTree)){
|
||||
//Check the current permission
|
||||
permMap.set(perm, await this.permCheckByUserDoc(userDB, perm));
|
||||
}
|
||||
|
||||
//return perm map
|
||||
return {
|
||||
site: siteMap.site,
|
||||
chan: permMap
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specific user has been issued a channel-specific ban by DB doc
|
||||
* @param {Mongoose.Document} userDB - DB Document representing a single user account
|
||||
* @returns {Object} Found ban, if one exists
|
||||
*/
|
||||
channelSchema.methods.checkBanByUserDoc = async function(userDB){
|
||||
var foundBan = null;
|
||||
|
||||
//this needs to be a for loop for async
|
||||
//this.banList.forEach((ban) => {
|
||||
for(banIndex in this.banList){
|
||||
|
||||
if(this.banList[banIndex].user != null){
|
||||
if(this.banList[banIndex].user.toString() == userDB._id.toString()){
|
||||
foundBan = this.banList[banIndex];
|
||||
}
|
||||
|
||||
//If this bans alts are banned
|
||||
if(this.banList[banIndex].banAlts){
|
||||
//Populate the user of the current ban being checked
|
||||
await this.populate(`banList.${banIndex}.user`);
|
||||
|
||||
//If this is an alt of the banned user
|
||||
if(await this.banList[banIndex].user.altCheck(userDB)){
|
||||
foundBan = this.banList[banIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foundBan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates Network-Friendly Browser-Digestable list of channel emotes
|
||||
* @returns {Array} Network-Friendly Browser-Digestable list of channel emotes
|
||||
*/
|
||||
channelSchema.methods.getEmotes = function(){
|
||||
//Create an empty array to hold our emote list
|
||||
const emoteList = [];
|
||||
|
||||
//For each channel emote
|
||||
this.emotes.forEach((emote) => {
|
||||
//Push an object with select information from the emote to the emote list
|
||||
emoteList.push({
|
||||
name: emote.name,
|
||||
link: emote.link,
|
||||
type: emote.type
|
||||
});
|
||||
});
|
||||
|
||||
//return the emote list
|
||||
return emoteList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates Network-Friendly Browser-Digestable list of channel playlists
|
||||
* @returns {Array} Network-Friendly Browser-Digestable list of channel playlists
|
||||
*/
|
||||
channelSchema.methods.getPlaylists = function(){
|
||||
//Create an empty array to hold our emote list
|
||||
const playlists = [];
|
||||
|
||||
//For each channel emote
|
||||
for(let playlist of this.media.playlists){
|
||||
//Push an object with select information from the emote to the emote list
|
||||
playlists.push(playlist.dehydrate());
|
||||
}
|
||||
|
||||
//return the emote list
|
||||
return playlists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crawls through channel playlists, running a given callback function against each one
|
||||
* @param {Function} cb - Callback function to run against channel playlists
|
||||
*/
|
||||
channelSchema.methods.playlistCrawl = function(cb){
|
||||
for(let listIndex in this.media.playlists){
|
||||
//Grab the associated playlist
|
||||
playlist = this.media.playlists[listIndex];
|
||||
|
||||
//Call the callback with the playlist and list index as arguments
|
||||
cb(playlist, listIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds channel playlist by playlist name
|
||||
* @param {String} name - name of given playlist to find
|
||||
* @returns {Mongoose.Document} - Sub-Document representing a single playlist
|
||||
*/
|
||||
channelSchema.methods.getPlaylistByName = function(name){
|
||||
//Create null value to hold our found playlist
|
||||
let foundPlaylist = null;
|
||||
|
||||
//Crawl through active playlists
|
||||
this.playlistCrawl((playlist, listIndex) => {
|
||||
//If we found a match based on name
|
||||
if(playlist.name == name){
|
||||
//Keep it
|
||||
foundPlaylist = playlist;
|
||||
//Pass down the list index
|
||||
foundPlaylist.listIndex = listIndex;
|
||||
}
|
||||
});
|
||||
|
||||
//return the given playlist
|
||||
return foundPlaylist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes channel playlist by playlist name
|
||||
* @param {String} name - name of given playlist to Delete
|
||||
*/
|
||||
channelSchema.methods.deletePlaylistByName = async function(name){
|
||||
//Find the playlist
|
||||
let playlist = this.getPlaylistByName(name);
|
||||
|
||||
//splice out the given playlist
|
||||
this.media.playlists.splice(playlist.listIndex, 1);
|
||||
|
||||
//save the channel document
|
||||
await this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates Network-Friendly Browser-Digestable list of Channel-Wide user bans
|
||||
* @returns {Array} Network-Friendly Browser-Digestable list of Channel-Wide user bans
|
||||
*/
|
||||
channelSchema.methods.getChanBans = async function(){
|
||||
//Create an empty list to hold our found bans
|
||||
var banList = [];
|
||||
//Populate the users in the banList
|
||||
await this.populate('banList.user');
|
||||
|
||||
//Crawl through known bans
|
||||
this.banList.forEach((ban) => {
|
||||
|
||||
var banObj = {
|
||||
banDate: ban.banDate,
|
||||
expirationDays: ban.expirationDays,
|
||||
banAlts: ban.banAlts,
|
||||
}
|
||||
|
||||
//Check if the ban was permanent (expiration set before ban date)
|
||||
if(ban.expirationDays > 0){
|
||||
//if not calculate expiration date
|
||||
var expirationDate = new Date(ban.banDate);
|
||||
expirationDate.setDate(expirationDate.getDate() + ban.expirationDays);
|
||||
|
||||
//Set calculated expiration date
|
||||
banObj.expirationDate = expirationDate;
|
||||
banObj.daysUntilExpiration = ban.getDaysUntilExpiration();
|
||||
}
|
||||
|
||||
//Setup user object (Do this last to keep it at bottom for human-readibility of json :P)
|
||||
banObj.user = {
|
||||
id: ban.user.id,
|
||||
user: ban.user.user,
|
||||
img: ban.user.img,
|
||||
date: ban.user.date
|
||||
}
|
||||
|
||||
banList.push(banObj);
|
||||
});
|
||||
|
||||
return banList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Issues channel-wide ban to user based on user DB document
|
||||
* @param {Mongoose.Document} userDB - DB Document representing a single user account to ban
|
||||
* @param {Number} expirationDays - Days until ban expiration
|
||||
* @param {Boolean} banAlts - Whether or not to ban alts
|
||||
*/
|
||||
channelSchema.methods.banByUserDoc = async function(userDB, expirationDays, banAlts){
|
||||
//Throw a shitfit if the user doesn't exist
|
||||
if(userDB == null){
|
||||
throw loggerUtils.exceptionSmith("Cannot ban non-existant user!", "validation");
|
||||
}
|
||||
|
||||
const foundBan = await this.checkBanByUserDoc(userDB);
|
||||
|
||||
if(foundBan != null){
|
||||
throw loggerUtils.exceptionSmith("User already banned!", "validation");
|
||||
}
|
||||
|
||||
//Create a new ban document based on input
|
||||
const banDoc = {
|
||||
user: userDB._id,
|
||||
expirationDays,
|
||||
banAlts
|
||||
}
|
||||
|
||||
const activeChan = server.channelManager.activeChannels.get(this.name);
|
||||
if(activeChan != null){
|
||||
const userConn = activeChan.userList.get(userDB.user);
|
||||
if(userConn != null){
|
||||
if(expirationDays < 0){
|
||||
userConn.disconnect("You have been permanently banned from this channel!");
|
||||
}else{
|
||||
userConn.disconnect(`You have been banned from this channel for ${expirationDays} day(s)!`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Push the ban to the list
|
||||
this.banList.push(banDoc);
|
||||
|
||||
await this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntatic sugar for banning users by username
|
||||
* @param {String} user - Username of user to ban
|
||||
* @param {Number} expirationDays - Days until ban expiration
|
||||
* @param {Boolean} banAlts - Whether or not to ban alts
|
||||
* @returns {Promise} promise from this.banByUserDoc
|
||||
*/
|
||||
channelSchema.methods.ban = async function(user, expirationDays, banAlts){
|
||||
const userDB = await userModel.find({user});
|
||||
return await this.banByUserDoc(userDB, expirationDays, banAlts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Un-Bans user by DB Document
|
||||
* @param {Mongoose.Document} userDB - DB Document representing a single user account to un-ban
|
||||
* @returns {Mongoose.Document} Saved channel document
|
||||
*/
|
||||
channelSchema.methods.unbanByUserDoc = async function(userDB){
|
||||
//Throw a shitfit if the user doesn't exist
|
||||
if(userDB == null){
|
||||
throw loggerUtils.exceptionSmith("Cannot ban non-existant user!", "validation");
|
||||
}
|
||||
|
||||
const foundBan = await this.checkBanByUserDoc(userDB);
|
||||
|
||||
if(foundBan == null){
|
||||
throw loggerUtils.exceptionSmith("User already unbanned!", "validation");
|
||||
}
|
||||
|
||||
//You know I can't help but feel like an asshole for looking for the index of something I just pulled out of an array using forEach...
|
||||
//Then again this is such an un-used function that the issue of code re-use overshadows performance
|
||||
//I mean how often are we REALLY going to be un-banning users from channels?
|
||||
const banIndex = this.banList.indexOf(foundBan);
|
||||
this.banList.splice(banIndex,1);
|
||||
return await this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntatic sugar for un-banning by username
|
||||
* @param {String} user - Username of user to un-ban
|
||||
* @returns {Mongoose.Document} Saved channel document
|
||||
*/
|
||||
channelSchema.methods.unban = async function(user){
|
||||
const userDB = await userModel.find({user});
|
||||
return await this.unbanByUserDoc(userDB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nukes channel upon channel-admin request
|
||||
* @param {String} confirm - Channel name to confirm deletion of channel
|
||||
*/
|
||||
channelSchema.methods.nuke = async function(confirm){
|
||||
if(confirm == "" || confirm == null){
|
||||
throw loggerUtils.exceptionSmith("Empty Confirmation String!", "validation");
|
||||
}else if(confirm != this.name){
|
||||
throw loggerUtils.exceptionSmith("Bad Confirmation String!", "validation");
|
||||
}
|
||||
|
||||
//Annoyingly there isnt a good way to do this from 'this'
|
||||
var oldChan = await this.deleteOne();
|
||||
|
||||
if(oldChan.deletedCount == 0){
|
||||
throw loggerUtils.exceptionSmith("Server Error: Unable to delete channel! Please report this error to your server administrator, and with timestamp.", "internal");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = mongoose.model("channel", channelSchema);</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
105
www/doc/schemas_channel_chatSchema.js.html
Normal file
105
www/doc/schemas_channel_chatSchema.js.html
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/channel/chatSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/channel/chatSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
|
||||
const linkSchema = new mongoose.Schema({
|
||||
link: mongoose.SchemaTypes.String,
|
||||
type: mongoose.SchemaTypes.String
|
||||
});
|
||||
|
||||
/**
|
||||
* DB Schema for documents representing a single chat message
|
||||
*/
|
||||
const chatSchema = new mongoose.Schema({
|
||||
user: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
},
|
||||
flair: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
},
|
||||
highLevel: {
|
||||
type: mongoose.SchemaTypes.Number,
|
||||
required: true,
|
||||
},
|
||||
msg: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
},
|
||||
links: {
|
||||
type: [linkSchema],
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = chatSchema;</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
105
www/doc/schemas_channel_media_mediaSchema.js.html
Normal file
105
www/doc/schemas_channel_media_mediaSchema.js.html
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/channel/media/mediaSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/channel/media/mediaSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
|
||||
/**
|
||||
* DB Schema representing a single piece of media
|
||||
*/
|
||||
const mediaSchema = new mongoose.Schema({
|
||||
title: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
},
|
||||
fileName: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
},
|
||||
url: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
},
|
||||
id: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
},
|
||||
duration: {
|
||||
type: mongoose.SchemaTypes.Number,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
discriminatorKey: 'status'
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
module.exports = mediaSchema;</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
133
www/doc/schemas_channel_media_playlistMediaSchema.js.html
Normal file
133
www/doc/schemas_channel_media_playlistMediaSchema.js.html
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/channel/media/playlistMediaSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/channel/media/playlistMediaSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
|
||||
//Local Imports
|
||||
const mediaSchema = require('./mediaSchema');
|
||||
const media = require('../../../app/channel/media/media');
|
||||
|
||||
/**
|
||||
* DB Schema for documents represnting a piece of media held in a playlist
|
||||
*/
|
||||
const playlistMediaProperties = new mongoose.Schema({
|
||||
uuid: {
|
||||
type: mongoose.SchemaTypes.UUID,
|
||||
required:true,
|
||||
default: crypto.randomUUID()
|
||||
}
|
||||
},
|
||||
{
|
||||
discriminatorKey: 'status'
|
||||
});
|
||||
|
||||
//Schema Middleware
|
||||
/**
|
||||
* Pre-save function for playlist meda, ensures unique UUID
|
||||
*/
|
||||
playlistMediaProperties.pre('save', async function (next){
|
||||
//If the UUID was modified in anyway
|
||||
if(this.isModified("uuid")){
|
||||
//Throw that shit out and make a new one since it's probably either null or a leftover from some channel queue
|
||||
this.uuid = crypto.randomUUID();
|
||||
}
|
||||
|
||||
//Keep it moving
|
||||
next();
|
||||
});
|
||||
|
||||
//methods
|
||||
/**
|
||||
* Rehydrate to a full phat media object
|
||||
* @returns {media} A full phat media object, re-hydrated from the DB
|
||||
*/
|
||||
playlistMediaProperties.methods.rehydrate = function(){
|
||||
//Return item as a full phat, standard media object
|
||||
return new media(
|
||||
this.title,
|
||||
this.fileName,
|
||||
this.url,
|
||||
this.id,
|
||||
this.type,
|
||||
this.duration
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dehydrate to minified flat network-friendly object
|
||||
* @returns {Object} Network-Friendly Browser-Digestable object representing media from a playlist
|
||||
*/
|
||||
playlistMediaProperties.methods.dehydrate = function(){
|
||||
return {
|
||||
title: this.title,
|
||||
url: this.url,
|
||||
duration: this.duration,
|
||||
uuid: this.uuid.toString()
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = mediaSchema.discriminator('saved', playlistMediaProperties);</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
183
www/doc/schemas_channel_media_playlistSchema.js.html
Normal file
183
www/doc/schemas_channel_media_playlistSchema.js.html
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/channel/media/playlistSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/channel/media/playlistSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
|
||||
//Local Imports
|
||||
const playlistMediaSchema = require('./playlistMediaSchema');
|
||||
|
||||
/**
|
||||
* DB Schema for Documents representing playlists full of media
|
||||
*/
|
||||
const playlistSchema = new mongoose.Schema({
|
||||
name: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
},
|
||||
media: [playlistMediaSchema],
|
||||
defaultTitles:[{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: false,
|
||||
default: []
|
||||
}]
|
||||
});
|
||||
|
||||
//methods
|
||||
/**
|
||||
* Dehydrate to minified flat network-friendly object
|
||||
* @returns {Object} Network-Friendly Browser-Digestable object representing media from a playlist
|
||||
*/
|
||||
playlistSchema.methods.dehydrate = function(){
|
||||
//Create empty array to hold media
|
||||
const mediaArray = [];
|
||||
|
||||
//Fill media array
|
||||
for(let media of this.media){
|
||||
mediaArray.push(media.dehydrate());
|
||||
}
|
||||
|
||||
//return dehydrated playlist
|
||||
return {
|
||||
name: this.name,
|
||||
media: mediaArray,
|
||||
defaultTitles: this.defaultTitles
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add media to the given playlist Document
|
||||
* @param {Array} mediaList - Array of media Objects to add to playlist
|
||||
*/
|
||||
playlistSchema.methods.addMedia = function(mediaList){
|
||||
//For every piece of media in the list
|
||||
for(let media of mediaList){
|
||||
//Set media status schema discriminator
|
||||
media.status = 'saved';
|
||||
|
||||
//Add the media to the playlist
|
||||
this.media.push(media);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Media from a playlist by UUID
|
||||
* @param {String} uuid - UUID of media to pull
|
||||
* @returns {media} media with matching UUID
|
||||
*/
|
||||
playlistSchema.methods.findMediaByUUID = function(uuid){
|
||||
//For every piece of media in the current playlist
|
||||
for(let media of this.media){
|
||||
//If we found our match
|
||||
if(media.uuid.toString() == uuid){
|
||||
//return it
|
||||
return media;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes media from a given playlist
|
||||
* @param {String} uuid - UUID of media to delete
|
||||
*/
|
||||
playlistSchema.methods.deleteMedia = function(uuid){
|
||||
//Create new array to hold list of media to be kept
|
||||
const keptMedia = [];
|
||||
|
||||
//For every piece of media in the current playlist
|
||||
for(let media of this.media){
|
||||
//It isn't the media to be deleted
|
||||
if(media.uuid.toString() != uuid){
|
||||
//Add it to the list to be kept
|
||||
keptMedia.push(media);
|
||||
}
|
||||
}
|
||||
|
||||
//Set playlist media from keptMedia
|
||||
this.media = keptMedia;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick title based on default title's list and media's given title
|
||||
* @param {String} title - Title to use if there are no default titles.
|
||||
* @returns {String} Chosen title based on result of function
|
||||
*/
|
||||
playlistSchema.methods.pickDefaultTitle = function(title){
|
||||
//If we don't have default titles in this playlist
|
||||
if(this.defaultTitles.length <= 0){
|
||||
//If we wheren't handed an original title
|
||||
if(title == null || title == ''){
|
||||
return 'Unnamed Media'
|
||||
}else{
|
||||
return title;
|
||||
}
|
||||
}else{
|
||||
//Grab a random default title and return it
|
||||
return this.defaultTitles[Math.floor(Math.random() * this.defaultTitles.length)];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = playlistSchema;</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
122
www/doc/schemas_channel_media_queuedMediaSchema.js.html
Normal file
122
www/doc/schemas_channel_media_queuedMediaSchema.js.html
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/channel/media/queuedMediaSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/channel/media/queuedMediaSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
|
||||
//Local Imports
|
||||
const mediaSchema = require('./mediaSchema');
|
||||
const queuedMedia = require('../../../app/channel/media/queuedMedia');
|
||||
|
||||
/**
|
||||
* DB Schema for documents representing a queued media object
|
||||
*/
|
||||
const queuedProperties = new mongoose.Schema({
|
||||
startTime: {
|
||||
type: mongoose.SchemaTypes.Number,
|
||||
required: true,
|
||||
},
|
||||
startTimeStamp: {
|
||||
type: mongoose.SchemaTypes.Number,
|
||||
required: false,
|
||||
},
|
||||
earlyEnd: {
|
||||
type: mongoose.SchemaTypes.Number,
|
||||
required: false,
|
||||
},
|
||||
uuid: {
|
||||
type: mongoose.SchemaTypes.UUID,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
discriminatorKey: 'status'
|
||||
});
|
||||
|
||||
//Methods
|
||||
/**
|
||||
* Rehydrate to a full phat queued media object
|
||||
* @returns {queuedMedia} A full phat queued media object, re-hydrated from the DB
|
||||
*/
|
||||
queuedProperties.methods.rehydrate = function(){
|
||||
return new queuedMedia(
|
||||
this.title,
|
||||
this.fileName,
|
||||
this.url,
|
||||
this.id,
|
||||
this.type,
|
||||
this.duration,
|
||||
//We don't save raw links that are stored seperate from the standard URL as they tend to expire.
|
||||
undefined,
|
||||
this.startTime,
|
||||
this.startTimeStamp,
|
||||
this.earlyEnd,
|
||||
this.uuid.toString()
|
||||
);
|
||||
}
|
||||
|
||||
//Export schema under the 'queued' discriminator of mediaSchema
|
||||
module.exports = mediaSchema.discriminator('queued', queuedProperties);</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
173
www/doc/schemas_emoteSchema.js.html
Normal file
173
www/doc/schemas_emoteSchema.js.html
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/emoteSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/emoteSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
|
||||
//Local Imports
|
||||
const defaultEmote = require("../../defaultEmotes.json");
|
||||
const server = require('../server');
|
||||
|
||||
/**
|
||||
* "Enum" for emote type property
|
||||
*/
|
||||
const typeEnum = ["image", "video"];
|
||||
|
||||
/**
|
||||
* DB Schema for documents represnting site-wide emotes
|
||||
*/
|
||||
const emoteSchema = new mongoose.Schema({
|
||||
name:{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
maxLength: 14,
|
||||
},
|
||||
link:{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true
|
||||
},
|
||||
type:{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
enum: typeEnum,
|
||||
default: typeEnum[0]
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Post-Save function, ensures all new emotes are broadcastes to actively connected clients
|
||||
*/
|
||||
emoteSchema.post('save', async function (next){
|
||||
//broadcast updated emotes
|
||||
server.channelManager.broadcastSiteEmotes();
|
||||
});
|
||||
|
||||
/**
|
||||
* Post-Delete function, ensures all deleted emotes are removed from actively connected clients
|
||||
*/
|
||||
emoteSchema.post('deleteOne', {document: true}, async function (next){
|
||||
//broadcast updated emotes
|
||||
server.channelManager.broadcastSiteEmotes();
|
||||
});
|
||||
|
||||
//statics
|
||||
/**
|
||||
* Loads un-loaded emotes from defaultEmotes.json
|
||||
*/
|
||||
emoteSchema.statics.loadDefaults = async function(){
|
||||
//Make sure registerEmote function is happy
|
||||
const _this = this;
|
||||
|
||||
//Ensure default comes first (.bind(this) doesn't seem to work here...)
|
||||
await registerEmote(defaultEmote.default);
|
||||
//For each entry in the defaultEmote.json file
|
||||
defaultEmote.array.forEach(registerEmote);
|
||||
|
||||
async function registerEmote(emote){
|
||||
try{
|
||||
//Look for emote matching the one from our file
|
||||
const foundEmote = await _this.findOne({name: emote.name});
|
||||
|
||||
//if the emote doesn't exist
|
||||
if(!foundEmote){
|
||||
const emoteDB = await _this.create(emote);
|
||||
console.log(`Loading default emote [${emote.name}] into DB from defaultEmote.json`);
|
||||
}
|
||||
|
||||
}catch(err){
|
||||
if(emote != null){
|
||||
console.log(`Error loading emote [${emote.name}]:`);
|
||||
}else{
|
||||
console.log("Error, null emote:");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a network-friendly browser-digestable list of emotes
|
||||
* @returns {Object} - network-friendly browser-digestable list of emotes
|
||||
*/
|
||||
emoteSchema.statics.getEmotes = async function(){
|
||||
//Create an empty array to hold our emote list
|
||||
const emoteList = [];
|
||||
//Pull emotes from database
|
||||
const emoteDB = await this.find({});
|
||||
|
||||
emoteDB.forEach((emote) => {
|
||||
emoteList.push({
|
||||
name: emote.name,
|
||||
link: emote.link,
|
||||
type: emote.type
|
||||
});
|
||||
});
|
||||
|
||||
return emoteList;
|
||||
}
|
||||
|
||||
emoteSchema.statics.typeEnum = typeEnum;
|
||||
|
||||
module.exports = mongoose.model("emote", emoteSchema);</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
127
www/doc/schemas_flairSchema.js.html
Normal file
127
www/doc/schemas_flairSchema.js.html
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/flairSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/flairSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
|
||||
//Local Imports
|
||||
const permissionModel = require("./permissionSchema");
|
||||
const defaultFlair = require("../../defaultFlair.json");
|
||||
|
||||
/**
|
||||
* DB Schema for documents representing chat flair
|
||||
*/
|
||||
const flairSchema = new mongoose.Schema({
|
||||
name:{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true
|
||||
},
|
||||
displayName:{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true
|
||||
},
|
||||
rank: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: permissionModel.rankEnum,
|
||||
default: "user",
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Function which runs on server startup to load un-loaded flairs from defaultFlair.json into the DB
|
||||
*/
|
||||
flairSchema.statics.loadDefaults = async function(){
|
||||
//Make sure registerFlair function is happy
|
||||
const _this = this;
|
||||
|
||||
//Ensure default comes first (.bind(this) doesn't seem to work here...)
|
||||
await registerFlair(defaultFlair.default);
|
||||
//For each entry in the defaultFlair.json file
|
||||
defaultFlair.array.forEach(registerFlair);
|
||||
|
||||
async function registerFlair(flair){
|
||||
try{
|
||||
//Look for flair matching the one from our file
|
||||
const foundFlair = await _this.findOne({name: flair.name});
|
||||
|
||||
//if the flair doesn't exist
|
||||
if(!foundFlair){
|
||||
const flairDB = await _this.create(flair);
|
||||
console.log(`Loading default flair '${flair.name} into DB from defaultFlair.json`);
|
||||
}
|
||||
|
||||
}catch(err){
|
||||
if(flair != null){
|
||||
console.log(`Error loading flair '${flair.name}':`);
|
||||
}else{
|
||||
console.log("Error, null flair:");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = mongoose.model("flair", flairSchema);</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
365
www/doc/schemas_permissionSchema.js.html
Normal file
365
www/doc/schemas_permissionSchema.js.html
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/permissionSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/permissionSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
|
||||
//Local Imports
|
||||
const userModel = require('./user/userSchema');
|
||||
const channelPermissionSchema = require('./channel/channelPermissionSchema');
|
||||
const {errorHandler} = require('../utils/loggerUtils');
|
||||
|
||||
//This originally belonged to the permissionSchema, but this avoids circular dependencies.
|
||||
//We could update all references but quite honestly I that would be uglier, this should have a copy too...
|
||||
const rankEnum = channelPermissionSchema.statics.rankEnum;
|
||||
|
||||
/**
|
||||
* DB Schema for the singular site-wide permission document
|
||||
*/
|
||||
const permissionSchema = new mongoose.Schema({
|
||||
adminPanel: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
changeRank: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
changePerms: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
announce: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
resetToke: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
editTokeCommands: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
banUser: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
nukeUser: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
genPasswordReset: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
registerChannel: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
editEmotes: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
enum: rankEnum,
|
||||
default: "admin",
|
||||
required: true
|
||||
},
|
||||
channelOverrides: {
|
||||
type: channelPermissionSchema,
|
||||
default: () => ({})
|
||||
},
|
||||
});
|
||||
|
||||
//Statics
|
||||
permissionSchema.statics.rankEnum = rankEnum;
|
||||
|
||||
/**
|
||||
* Returns the server's singular permission document from the DB
|
||||
* @returns {Mongoose.Document} - The server's singular permission document
|
||||
*/
|
||||
permissionSchema.statics.getPerms = async function(){
|
||||
//Not sure why 'this' didn't work from here when calling this, I'm assuming it's because I'm doing it from middleware
|
||||
//which is probably binding shit to this function, either way this works :P
|
||||
|
||||
//Get the first document we find
|
||||
var perms = await module.exports.findOne({});
|
||||
|
||||
if(perms){
|
||||
//If we found something then the permissions document exist and this is it,
|
||||
//So long as no one else has fucked with the database it should be the only one. (is this forshadowing for a future bug?)
|
||||
return perms;
|
||||
}else{
|
||||
//Otherwise this is the first launch of the install, say hello
|
||||
console.log("First launch detected! Initializing permissions document in Database!");
|
||||
|
||||
//create and save the permissions document
|
||||
perms = await module.exports.create({});
|
||||
await perms.save();
|
||||
|
||||
//live up to the name of the function
|
||||
return perms;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts rank name to number
|
||||
* @param {String} rank - rank to check
|
||||
* @returns {Number} Rank level
|
||||
*/
|
||||
permissionSchema.statics.rankToNum = function(rank){
|
||||
return rankEnum.indexOf(rank);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check users rank against a given permission by username
|
||||
* @param {String} user - Username of the user to check against a perm
|
||||
* @param {String} perm - Permission to check user against
|
||||
* @returns {Boolean} Whether or not the user is authorized for the permission in question
|
||||
*/
|
||||
permissionSchema.statics.permCheck = async function(user, perm){
|
||||
//Check if the user is null
|
||||
if(user != null){
|
||||
//This specific call is why we export the userModel the way we do
|
||||
//Someone will yell at me for circular dependencies but the fucking interpreter isn't :P
|
||||
const userDB = await userModel.userModel.findOne({user: user.user});
|
||||
return await this.permCheckByUserDoc(userDB, perm);
|
||||
}else{
|
||||
return await this.permCheckByUserDoc(null, perm);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntatic sugar for perms.CheckByUserDoc so we don't have to get the document ourselves
|
||||
* @param {Mongoose.Document} user - User document to check perms against
|
||||
* @param {String} perm - Permission to check user against
|
||||
* @returns {Boolean} Whether or not the user is authorized for the permission in question
|
||||
*/
|
||||
permissionSchema.statics.permCheckByUserDoc = async function(user, perm){
|
||||
//Get permission list
|
||||
const perms = await this.getPerms();
|
||||
//Call the perm check method
|
||||
return perms.permCheckByUserDoc(user, perm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check users rank by a given permission by username
|
||||
* @param {String} user - Username of the user to check against a perm
|
||||
* @param {String} perm - Permission to check user against
|
||||
* @returns {Boolean} Whether or not the user is authorized for the permission in question
|
||||
*/
|
||||
permissionSchema.statics.overrideCheck = async function(user, perm){
|
||||
//Check if the user is null
|
||||
if(user != null){
|
||||
//This specific call is why we export the userModel the way we do
|
||||
//Someone will yell at me for circular dependencies but the fucking interpreter isn't :P
|
||||
const userDB = await userModel.userModel.findOne({user: user.user});
|
||||
return await this.overrideCheckByUserDoc(userDB, perm);
|
||||
}else{
|
||||
return await this.overrideCheckByUserDoc(null, perm);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Syntatic sugar for perms.overrideCheckByUSerDoc so we don't have to seperately get the perm doc
|
||||
* Checks channel perm override against a given user by username
|
||||
* @param {String} user - Username of the user to check against a perm
|
||||
* @param {String} perm - Permission to check user against
|
||||
* @returns {Boolean} Whether or not the user is authorized for the permission in question
|
||||
*/
|
||||
permissionSchema.statics.overrideCheckByUserDoc = async function(user, perm){
|
||||
//Get permission list
|
||||
const perms = await this.getPerms();
|
||||
//Call the perm check method
|
||||
return perms.overrideCheckByUserDoc(user, perm);
|
||||
}
|
||||
|
||||
//Middleware for rank checks
|
||||
/**
|
||||
* Configurable express middleware which checks user's request against a given permission
|
||||
* @param {String} perm - Permission to check
|
||||
* @returns {Function} Express middlewhere function with given permission injected into it
|
||||
*/
|
||||
permissionSchema.statics.reqPermCheck = function(perm){
|
||||
return (req, res, next)=>{
|
||||
permissionSchema.statics.permCheck(req.session.user, perm).then((access) => {
|
||||
if(access){
|
||||
next();
|
||||
}else{
|
||||
return errorHandler(res, "You do not have a high enough rank to access this resource.", 'Unauthorized', 401);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//methods
|
||||
//these are good to have even for single-doc collections since we can loop through them without finding them in the database each time
|
||||
/**
|
||||
* Checks permission against a single user by document
|
||||
* @param {Mongoose.Document} userDB - User doc to rank check against
|
||||
* @param {String} perm - Permission to check user doc against
|
||||
* @returns {Boolean} True if authorized
|
||||
*/
|
||||
permissionSchema.methods.permCheckByUserDoc = function(userDB, perm){
|
||||
//Set user to anon rank if no rank was found for the given user
|
||||
if(userDB == null || userDB.rank == null){
|
||||
userDB ={
|
||||
rank: "anon"
|
||||
};
|
||||
}
|
||||
|
||||
//Check if this permission exists
|
||||
if(this[perm] != null){
|
||||
//if so get required rank as a number
|
||||
requiredRank = this.model().rankToNum(this[perm])
|
||||
//if so get user rank as a number
|
||||
userRank = userDB ? this.model().rankToNum(userDB.rank) : 0;
|
||||
//return whether or not the user is equal to or higher than the required rank for this permission
|
||||
return (userRank >= requiredRank);
|
||||
}else{
|
||||
//if not scream and shout
|
||||
throw loggerUtils.exceptionSmith(`Permission check '${perm}' not found!`, "Validation");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks channel override permission against a single user by document
|
||||
* @param {Mongoose.Document} userDB - User doc to rank check against
|
||||
* @param {String} perm - Channel Override Permission to check user doc against
|
||||
* @returns {Boolean} True if authorized
|
||||
*/
|
||||
permissionSchema.methods.overrideCheckByUserDoc = function(userDB, perm){
|
||||
//Set user to anon rank if no rank was found for the given user
|
||||
if(userDB == null || userDB.rank == null){
|
||||
userDB ={
|
||||
rank: "anon"
|
||||
};
|
||||
}
|
||||
|
||||
//Check if this permission exists
|
||||
if(this.channelOverrides[perm] != null){
|
||||
//if so get required rank as a number
|
||||
requiredRank = this.model().rankToNum(this.channelOverrides[perm])
|
||||
//if so get user rank as a number
|
||||
userRank = userDB ? this.model().rankToNum(userDB.rank) : 0;
|
||||
//return whether or not the user is equal to or higher than the required rank for this permission
|
||||
return (userRank >= requiredRank);
|
||||
}else{
|
||||
//if not scream and shout
|
||||
throw loggerUtils.exceptionSmith(`Permission check '${perm}' not found!`, "validation");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns entire permission map marked with booleans
|
||||
* @param {Mongoose.Document} userDB - User Doc to generate perm map against
|
||||
* @returns {Map} Permission map containing booleans for each permission's authorization for a given user doc
|
||||
*/
|
||||
permissionSchema.methods.getPermMapByUserDoc = function(userDB){
|
||||
//Pull permissions keys
|
||||
let permTree = this.schema.tree;
|
||||
let overrideTree = channelPermissionSchema.tree;
|
||||
let permMap = new Map();
|
||||
let overrideMap = new Map();
|
||||
|
||||
//For each object in the temporary permissions object
|
||||
for(let perm of Object.keys(permTree)){
|
||||
//Check the current permission
|
||||
permMap.set(perm, this.permCheckByUserDoc(userDB, perm));
|
||||
}
|
||||
|
||||
//For each object in the temporary permissions object
|
||||
for(let perm of Object.keys(overrideTree)){
|
||||
//Check the current permission
|
||||
overrideMap.set(perm, this.overrideCheckByUserDoc(userDB, perm));
|
||||
}
|
||||
|
||||
//return the auto-generated schema
|
||||
return {
|
||||
site: permMap,
|
||||
channelOverrides: overrideMap
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = mongoose.model("permissions", permissionSchema);</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
249
www/doc/schemas_statSchema.js.html
Normal file
249
www/doc/schemas_statSchema.js.html
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/statSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/statSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
|
||||
//Local Imports
|
||||
const config = require('./../../config.json');
|
||||
|
||||
/**
|
||||
* DB Schema for single document for keeping track of server stats
|
||||
*/
|
||||
const statSchema = new mongoose.Schema({
|
||||
//This does NOT handle deleted accounts/channels. Use userModel.estimatedDocumentCount() for number of active users.
|
||||
userCount: {
|
||||
type: mongoose.SchemaTypes.Number,
|
||||
required: true,
|
||||
default: 0
|
||||
},
|
||||
channelCount: {
|
||||
type: mongoose.SchemaTypes.Number,
|
||||
required: true,
|
||||
default: 0
|
||||
},
|
||||
launchCount: {
|
||||
type: mongoose.SchemaTypes.Number,
|
||||
required: true,
|
||||
default: 0
|
||||
},
|
||||
firstLaunch: {
|
||||
type: mongoose.SchemaTypes.Date,
|
||||
required: true,
|
||||
default: new Date()
|
||||
},
|
||||
tokes: [{
|
||||
toke: {
|
||||
type: mongoose.SchemaTypes.Map,
|
||||
required: true,
|
||||
default: new Map()
|
||||
},
|
||||
date: {
|
||||
type: mongoose.SchemaTypes.Date,
|
||||
required: true,
|
||||
default: new Date()
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
//statics
|
||||
/**
|
||||
* Get's servers sole stat document from the DB
|
||||
* @returns {Mongoose.Document} Server's sole statistics document
|
||||
*/
|
||||
statSchema.statics.getStats = async function(){
|
||||
//Get the first document we find
|
||||
var stats = await this.findOne({});
|
||||
|
||||
if(stats){
|
||||
//If we found something then the statistics document exist and this is it,
|
||||
//So long as no one else has fucked with the database it should be the only one. (is this forshadowing for a future bug?)
|
||||
return stats;
|
||||
}else{
|
||||
//Otherwise this is the first launch of the install, say hello
|
||||
console.log("First launch detected! Initializing statistics document in Database!");
|
||||
|
||||
//create and save the statistics document
|
||||
stats = await this.create({});
|
||||
await stats.save();
|
||||
|
||||
//live up to the name of the function
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments Lunach count upon server launch and prints out amount of launches since server initialization
|
||||
*/
|
||||
statSchema.statics.incrementLaunchCount = async function(){
|
||||
//get our statistics document
|
||||
const stats = await this.getStats();
|
||||
|
||||
//increment counter and save
|
||||
stats.launchCount++;
|
||||
stats.save();
|
||||
|
||||
//print bootup message to console.
|
||||
console.log(`${config.instanceName}(Powered by Canopy) initialized. This server has booted ${stats.launchCount} time${stats.launchCount == 1 ? '' : 's'}.`)
|
||||
console.log(`First booted on ${stats.firstLaunch}.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments user count upon new user registration
|
||||
* @returns {Number} Number of users before count was incremented
|
||||
*/
|
||||
statSchema.statics.incrementUserCount = async function(){
|
||||
//get our statistics document
|
||||
const stats = await this.getStats();
|
||||
//temporarily keep old count so we can return it for the users ID
|
||||
const oldCount = stats.userCount;
|
||||
|
||||
//increment counter and save
|
||||
stats.userCount++;
|
||||
stats.save();
|
||||
|
||||
//return the count from beggining of function for user ID
|
||||
return oldCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments channel count upon new channel registration
|
||||
* @returns {Number} Number of channels before count was incremented
|
||||
*/
|
||||
statSchema.statics.incrementChannelCount = async function(){
|
||||
//get our statistics document
|
||||
const stats = await this.getStats();
|
||||
//temporarily keep old count so we can return it for the channel ID
|
||||
const oldCount = stats.channelCount;
|
||||
|
||||
//increment counter and save
|
||||
stats.channelCount++;
|
||||
stats.save();
|
||||
|
||||
//return the count from beggining of function for channel ID
|
||||
return oldCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tattoo's toke to the server statistics document
|
||||
* @param {Map} toke - Tokemap handed down from Tokebot
|
||||
*/
|
||||
statSchema.statics.tattooToke = async function(toke){
|
||||
//Get the statistics document
|
||||
const stats = await this.getStats();
|
||||
|
||||
//Add the toke to the stat document
|
||||
stats.tokes.push({toke});
|
||||
|
||||
//Save the stat document
|
||||
await stats.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets toke count from statistics document
|
||||
* @returns {Number} Number of tokes across the entire site
|
||||
*/
|
||||
statSchema.statics.getTokeCount = async function(){
|
||||
//get stats doc
|
||||
const stats = await this.getStats();
|
||||
|
||||
//return toke count
|
||||
return stats.tokes.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets toke counts for each individual callout in a map
|
||||
* @returns {Map} Map of toke counts for each individual callout registered to the server
|
||||
*/
|
||||
statSchema.statics.getTokeCommandCounts = async function(){
|
||||
//get stats doc
|
||||
const stats = await this.getStats()
|
||||
//Create empty map to hold toke command counts
|
||||
const count = new Map();
|
||||
|
||||
//for each toke
|
||||
stats.tokes.forEach((toke) => {
|
||||
//For each toke command called in the current toke
|
||||
toke.toke.forEach((command) => {
|
||||
//Get the current count for the current command
|
||||
var curCount = count.get(command);
|
||||
|
||||
//if the current count is null
|
||||
if(curCount == null){
|
||||
//Set it to one
|
||||
count.set(command, 1);
|
||||
}else{
|
||||
//Set it to ++curCount
|
||||
count.set(command, ++curCount);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//return the toke command count
|
||||
return count;
|
||||
}
|
||||
|
||||
module.exports = mongoose.model("statistics", statSchema);</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
169
www/doc/schemas_tokebot_tokeCommandSchema.js.html
Normal file
169
www/doc/schemas_tokebot_tokeCommandSchema.js.html
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/tokebot/tokeCommandSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/tokebot/tokeCommandSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
|
||||
//Local Imports
|
||||
const defaultTokes = require("../../../defaultTokes.json");
|
||||
const server = require('../../server');
|
||||
|
||||
/**
|
||||
* Mongoose Schema representing a toke command
|
||||
*/
|
||||
const tokeCommandSchema = new mongoose.Schema({
|
||||
command:{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Pre-Save middleware, ensures tokebot receives all new toke commands
|
||||
*/
|
||||
tokeCommandSchema.pre('save', async function (next){
|
||||
//if the command was changed
|
||||
if(this.isModified("command")){
|
||||
//Get server tokebot object
|
||||
const tokebot = server.channelManager.chatHandler.commandPreprocessor.tokebot;
|
||||
|
||||
//Pop the command on to the end
|
||||
tokebot.tokeCommands.push(this.command);
|
||||
}
|
||||
|
||||
|
||||
//All is good, continue on saving.
|
||||
next();
|
||||
});
|
||||
|
||||
/**
|
||||
* Pre-Delete middleware, ensures tokebot removes all old toke commands
|
||||
*/
|
||||
tokeCommandSchema.pre('deleteOne', {document: true}, async function (next){
|
||||
//Get server tokebot object (isn't this a fun dot crawler? Why hasn't anyone asked me to stop writing software yet?)
|
||||
const tokebot = server.channelManager.chatHandler.commandPreprocessor.tokebot;
|
||||
|
||||
//Get the index of the command within tokeCommand and splice it out
|
||||
tokebot.tokeCommands.splice(tokebot.tokeCommands.indexOf(this.command),1);
|
||||
|
||||
//All is good, continue on deleting.
|
||||
next();
|
||||
});
|
||||
|
||||
/**
|
||||
* Pulls command strings from DB and reports back
|
||||
* @returns {Array} Array of toke commands pulled from the DB
|
||||
*/
|
||||
tokeCommandSchema.statics.getCommandStrings = async function(){
|
||||
//Get all toke commands in the DB
|
||||
const tokeDB = await this.find({});
|
||||
//Create an empty array to hold the toke commands
|
||||
var tokeArray = [];
|
||||
|
||||
//for all toke commands found in the database
|
||||
tokeDB.forEach((toke)=>{
|
||||
//Push the command string into the tokeArray
|
||||
tokeArray.push(toke.command);
|
||||
})
|
||||
|
||||
//return the toke command strings from the database
|
||||
return tokeArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads default tokes into the DB from flat file upon launch
|
||||
*/
|
||||
tokeCommandSchema.statics.loadDefaults = async function(){
|
||||
//Make sure registerToke function is happy
|
||||
const _this = this;
|
||||
|
||||
//Ensure default comes first (.bind(this) doesn't seem to work here...)
|
||||
await registerToke(defaultTokes.default);
|
||||
//For each entry in the defaultTokes.json file
|
||||
defaultTokes.array.forEach(registerToke);
|
||||
|
||||
async function registerToke(toke){
|
||||
try{
|
||||
//Look for toke matching the one from our file
|
||||
const foundToke = await _this.findOne({command: toke});
|
||||
|
||||
//if the toke doesn't exist
|
||||
if(!foundToke){
|
||||
const tokeDB = await _this.create({command: toke});
|
||||
console.log(`Loading default toke command '!${toke}' into DB from defaultTokes.json`);
|
||||
}
|
||||
|
||||
}catch(err){
|
||||
if(toke != null){
|
||||
console.log(`Error loading toke command: '!${toke}'`);
|
||||
}else{
|
||||
console.log("Error, null toke!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = mongoose.model("tokeCommand", tokeCommandSchema);</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
231
www/doc/schemas_user_emailChangeSchema.js.html
Normal file
231
www/doc/schemas_user_emailChangeSchema.js.html
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/user/emailChangeSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/user/emailChangeSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//You could make an argument for making this part of the userModel
|
||||
//However, this is so rarely used the preformance benefits aren't worth the extra clutter
|
||||
|
||||
//Config
|
||||
const config = require('../../../config.json');
|
||||
|
||||
//Node Imports
|
||||
const crypto = require("node:crypto");
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
|
||||
//Local Imports
|
||||
const hashUtil = require('../../utils/hashUtils');
|
||||
const mailUtils = require('../../utils/mailUtils');
|
||||
|
||||
/**
|
||||
* Email change token retention time
|
||||
*/
|
||||
const daysToExpire = 7;
|
||||
|
||||
/**
|
||||
* DB Schema for Document representing a single email change request
|
||||
*/
|
||||
const emailChangeSchema = new mongoose.Schema({
|
||||
user: {
|
||||
type: mongoose.SchemaTypes.ObjectID,
|
||||
ref: "user",
|
||||
required: true
|
||||
},
|
||||
newEmail: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true
|
||||
},
|
||||
token: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
//Use a cryptographically secure algorythm to create a random hex string from 16 bytes as our change/cancel token
|
||||
default: ()=>{return crypto.randomBytes(16).toString('hex')}
|
||||
},
|
||||
ipHash: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true
|
||||
},
|
||||
date: {
|
||||
type: mongoose.SchemaTypes.Date,
|
||||
required: true,
|
||||
default: new Date()
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Pre-Save function, ensures IP's are hashed and previous requests are deleted upon request creation
|
||||
*/
|
||||
emailChangeSchema.pre('save', async function (next){
|
||||
//If we're saving an ip
|
||||
if(this.isModified('ipHash')){
|
||||
//Hash that sunnuvabitch
|
||||
this.ipHash = hashUtil.hashIP(this.ipHash);
|
||||
}
|
||||
|
||||
if(this.isModified('user')){
|
||||
//Delete previous requests for the given user
|
||||
const requests = await this.model().deleteMany({user: this.user._id});
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
/**
|
||||
* Schedulable function for processing expired email change requests
|
||||
*/
|
||||
emailChangeSchema.statics.processExpiredRequests = async function(){
|
||||
//Pull all requests from the DB
|
||||
//Tested finding request by date, but mongoose kept throwing casting errors.
|
||||
//This seems to be an intermittent issue online. Maybe it will work in a future version?
|
||||
const requestDB = await this.find({});
|
||||
|
||||
//Fire em all off at once without waiting for the last one to complete since we don't fuckin' need to
|
||||
for(let requestIndex in requestDB){
|
||||
//Pull request from requestDB by index
|
||||
const request = requestDB[requestIndex];
|
||||
|
||||
//If the request hasn't been processed and it's been expired
|
||||
if(request.getDaysUntilExpiration() <= 0){
|
||||
//Delete the request
|
||||
await this.deleteOne({_id: request._id});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes email change token, changing email address on a given user
|
||||
*/
|
||||
emailChangeSchema.methods.consume = async function(){
|
||||
//Populate the user reference
|
||||
await this.populate('user');
|
||||
|
||||
const oldMail = this.user.email;
|
||||
|
||||
//Set the new email
|
||||
this.user.email = this.newEmail;
|
||||
|
||||
//Save the user
|
||||
await this.user.save();
|
||||
|
||||
//Delete the request token now that it has been consumed
|
||||
await this.deleteOne();
|
||||
|
||||
//If we had a previous email address
|
||||
if(oldMail != null && oldMail != ''){
|
||||
//Notify it of the change
|
||||
await mailUtils.mailem(
|
||||
oldMail,
|
||||
`Email Change Notification - ${this.user.user}`,
|
||||
`<h1>Email Change Notification</h1>
|
||||
<p>The ${config.instanceName} account '${this.user.user}' is no longer associated with this email address.<br>
|
||||
<sup>If you received this email without request, you should <strong>immediately</strong> change your password and contact the server adminsitrator! -Tokebot</sup>`,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
//Notify the new inbox of the change
|
||||
await mailUtils.mailem(
|
||||
this.newEmail,
|
||||
`Email Change Notification - ${this.user.user}`,
|
||||
`<h1>Email Change Notification</h1>
|
||||
<p>The ${config.instanceName} account '${this.user.user}' is now associated with this email address.<br>
|
||||
<sup>If you received this email without request, you should <strong>immediately</strong> check who's been inside your inbox! -Tokebot</sup>`,
|
||||
true
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates email change URL from a given token
|
||||
* @returns {String} Email change URL generated from token
|
||||
*/
|
||||
emailChangeSchema.methods.getChangeURL = function(){
|
||||
//Check for default port based on protocol
|
||||
if((config.protocol == 'http' && config.port == 80) || (config.protocol == 'https' && config.port == 443 || config.proxied)){
|
||||
//Return path
|
||||
return `${config.protocol}://${config.domain}/emailChange?token=${this.token}`;
|
||||
}else{
|
||||
//Return path
|
||||
return `${config.protocol}://${config.domain}:${config.port}/emailChange?token=${this.token}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates days until token expiration
|
||||
* @returns {Number} Days until token expiration
|
||||
*/
|
||||
emailChangeSchema.methods.getDaysUntilExpiration = function(){
|
||||
//Get request date
|
||||
const expirationDate = new Date(this.date);
|
||||
//Get expiration days and calculate expiration date
|
||||
expirationDate.setDate(expirationDate.getDate() + daysToExpire);
|
||||
//Calculate and return days until request expiration
|
||||
return ((expirationDate - new Date()) / (1000 * 60 * 60 * 24)).toFixed(1);
|
||||
}
|
||||
|
||||
module.exports = mongoose.model("emailChange", emailChangeSchema);
|
||||
</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
207
www/doc/schemas_user_passwordResetSchema.js.html
Normal file
207
www/doc/schemas_user_passwordResetSchema.js.html
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/user/passwordResetSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/user/passwordResetSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//You could make an argument for making this part of the userModel
|
||||
//However, this is so rarely used the preformance benefits aren't worth the extra clutter
|
||||
|
||||
//Config
|
||||
const config = require('../../../config.json');
|
||||
|
||||
//Node Imports
|
||||
const crypto = require("node:crypto");
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
|
||||
//Local Imports
|
||||
const hashUtil = require('../../utils/hashUtils.js');
|
||||
const loggerUtils = require('../../utils/loggerUtils.js')
|
||||
|
||||
/**
|
||||
* Password reset token retention time
|
||||
*/
|
||||
const daysToExpire = 7;
|
||||
|
||||
/**
|
||||
* DB Schema for documents containing a single expiring password reset token
|
||||
*/
|
||||
const passwordResetSchema = new mongoose.Schema({
|
||||
user: {
|
||||
type: mongoose.SchemaTypes.ObjectID,
|
||||
ref: "user",
|
||||
required: true
|
||||
},
|
||||
token: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
//Use a cryptographically secure algorythm to create a random hex string from 16 bytes as our reset token
|
||||
default: ()=>{return crypto.randomBytes(16).toString('hex')}
|
||||
},
|
||||
ipHash: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true
|
||||
},
|
||||
date: {
|
||||
type: mongoose.SchemaTypes.Date,
|
||||
required: true,
|
||||
default: new Date()
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Pre-save function, ensures IP's are hashed before saving
|
||||
*/
|
||||
passwordResetSchema.pre('save', async function (next){
|
||||
//If we're saving an ip
|
||||
if(this.isModified('ipHash')){
|
||||
//Hash that sunnuvabitch
|
||||
this.ipHash = hashUtil.hashIP(this.ipHash);
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
/**
|
||||
* Schedulable function for processing expired reset requests
|
||||
*/
|
||||
passwordResetSchema.statics.processExpiredRequests = async function(){
|
||||
//Pull all requests from the DB
|
||||
//Tested finding request by date, but mongoose kept throwing casting errors.
|
||||
//This seems to be an intermittent issue online. Maybe it will work in a future version?
|
||||
const requestDB = await this.find({});
|
||||
|
||||
//Fire em all off at once without waiting for the last one to complete since we don't fuckin' need to
|
||||
for(let requestIndex in requestDB){
|
||||
//pull request from requestDB by index
|
||||
const request = requestDB[requestIndex];
|
||||
|
||||
//If the request hasn't been processed and it's been expired
|
||||
if(request.getDaysUntilExpiration() <= 0){
|
||||
//Delete the request
|
||||
await this.deleteOne({_id: request._id});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//methods
|
||||
/**
|
||||
* Resets password and consumes token
|
||||
* @param {String} pass - New password to set
|
||||
* @param {String} confirmPass - Confirmation String to ensure new pass is correct
|
||||
*/
|
||||
passwordResetSchema.methods.consume = async function(pass, confirmPass){
|
||||
//Check confirmation pass
|
||||
if(pass != confirmPass){
|
||||
throw loggerUtils.exceptionSmith("Confirmation password does not match!", "validation");
|
||||
}
|
||||
|
||||
//Populate the user reference
|
||||
await this.populate('user');
|
||||
|
||||
//Set the users password
|
||||
this.user.pass = pass;
|
||||
|
||||
//Save the user
|
||||
await this.user.save();
|
||||
|
||||
//Kill all authed sessions for security purposes
|
||||
await this.user.killAllSessions("Your password has been reset.");
|
||||
|
||||
//Delete the request token now that it has been consumed
|
||||
await this.deleteOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates password reset URL off of the token object
|
||||
* @returns {String} Reset URL
|
||||
*/
|
||||
passwordResetSchema.methods.getResetURL = function(){
|
||||
//Check for default port based on protocol
|
||||
if((config.protocol == 'http' && config.port == 80) || (config.protocol == 'https' && config.port == 443) || config.proxied){
|
||||
//Return path
|
||||
return `${config.protocol}://${config.domain}/passwordReset?token=${this.token}`;
|
||||
}else{
|
||||
//Return path
|
||||
return `${config.protocol}://${config.domain}:${config.port}/passwordReset?token=${this.token}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of days until token expiration
|
||||
* @returns {Number} Number of days until token expiration
|
||||
*/
|
||||
passwordResetSchema.methods.getDaysUntilExpiration = function(){
|
||||
//Get request date
|
||||
const expirationDate = new Date(this.date);
|
||||
//Get expiration days and calculate expiration date
|
||||
expirationDate.setDate(expirationDate.getDate() + daysToExpire);
|
||||
//Calculate and return days until request expiration
|
||||
return ((expirationDate - new Date()) / (1000 * 60 * 60 * 24)).toFixed(1);
|
||||
}
|
||||
|
||||
module.exports = mongoose.model("passwordReset", passwordResetSchema);
|
||||
</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
530
www/doc/schemas_user_userBanSchema.js.html
Normal file
530
www/doc/schemas_user_userBanSchema.js.html
Normal file
|
|
@ -0,0 +1,530 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/user/userBanSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/user/userBanSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
|
||||
//Local Imports
|
||||
const hashUtil = require('../../utils/hashUtils.js');
|
||||
const {userModel} = require('./userSchema.js');
|
||||
const loggerUtils = require('../../utils/loggerUtils.js');
|
||||
|
||||
/**
|
||||
* DB Schema for Documents representing a single user's ban
|
||||
*/
|
||||
const userBanSchema = new mongoose.Schema({
|
||||
user: {
|
||||
type: mongoose.SchemaTypes.ObjectID,
|
||||
ref: "user"
|
||||
},
|
||||
ips: {
|
||||
plaintext: {
|
||||
type: [mongoose.SchemaTypes.String],
|
||||
required: false
|
||||
},
|
||||
hashed: {
|
||||
type: [mongoose.SchemaTypes.String],
|
||||
required: false
|
||||
}
|
||||
},
|
||||
alts: [{
|
||||
type: mongoose.SchemaTypes.ObjectID,
|
||||
ref: "user"
|
||||
}],
|
||||
deletedNames: {
|
||||
type: [mongoose.SchemaTypes.String],
|
||||
required: false
|
||||
},
|
||||
banDate: {
|
||||
|
||||
type: mongoose.SchemaTypes.Date,
|
||||
required: true,
|
||||
default: new Date()
|
||||
},
|
||||
expirationDays: {
|
||||
type: mongoose.SchemaTypes.Number,
|
||||
required: true,
|
||||
default: 30
|
||||
},
|
||||
//If true, then expiration date deletes associated accounts instead of deleting the ban record
|
||||
permanent: {
|
||||
type: mongoose.SchemaTypes.Boolean,
|
||||
required: true,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Checks ban by IP
|
||||
* @param {String} ip - IP Address check for bans
|
||||
* @returns {Mongoose.Document} Found ban Document if one exists.
|
||||
*/
|
||||
userBanSchema.statics.checkBanByIP = async function(ip){
|
||||
//Get hash of ip
|
||||
const ipHash = hashUtil.hashIP(ip);
|
||||
//Get all bans
|
||||
const banDB = await this.find({});
|
||||
//Create null variable to hold any found ban
|
||||
let foundBan = null;
|
||||
|
||||
//For every ban
|
||||
for(ban of banDB){
|
||||
//Create empty list to hold unmatched hashes in the advent that we match one
|
||||
let tempHashes = [];
|
||||
//Create flag to throw to save tempHashes in the advent that we have matches we dont want to save as hashes
|
||||
let saveBan = false;
|
||||
|
||||
|
||||
//For every plaintext IP in the ban
|
||||
for(ipIndex in ban.ips.plaintext){
|
||||
//Get the current ip
|
||||
const curIP = ban.ips.plaintext[ipIndex];
|
||||
|
||||
//Check the current IP against the given ip
|
||||
if(ip == curIP){
|
||||
//If it matches we found the ban
|
||||
foundBan = ban;
|
||||
}
|
||||
}
|
||||
|
||||
//For every hashed IP in the ban
|
||||
for(ipIndex in ban.ips.hashed){
|
||||
//Get the current ip hash
|
||||
const curHash = ban.ips.hashed[ipIndex];
|
||||
|
||||
//Check the current hash against the given hash
|
||||
if(ipHash == curHash){
|
||||
//If it matches we found the ban
|
||||
foundBan = ban;
|
||||
|
||||
//Push the match to plaintext IPs so we know who the fucker is
|
||||
ban.ips.plaintext.push(ip);
|
||||
|
||||
//Throw the save ban flag to save the ban
|
||||
saveBan = true;
|
||||
//Otherwise
|
||||
}else{
|
||||
//Keep the hash since it hasn't been matched yet
|
||||
tempHashes.push(curHash);
|
||||
}
|
||||
}
|
||||
|
||||
//If we matched a hashed ip and we need to save it as plaintext
|
||||
if(saveBan){
|
||||
//Keep unmatched hashes
|
||||
ban.ips.hashed = tempHashes;
|
||||
|
||||
//Save the current ban
|
||||
await ban.save();
|
||||
}
|
||||
}
|
||||
|
||||
return foundBan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for bans by user DB doc
|
||||
* @param {Mongoose.Document} userDB - User Doc to check
|
||||
* @returns {Mongoose.Document} Found ban document for given user doc
|
||||
*/
|
||||
userBanSchema.statics.checkBanByUserDoc = async function(userDB){
|
||||
const banDB = await this.find({});
|
||||
var foundBan = null;
|
||||
|
||||
banDB.forEach((ban) => {
|
||||
if(ban.user != null){
|
||||
//if we found a match
|
||||
if(ban.user.toString() == userDB._id.toString()){
|
||||
//Set found ban
|
||||
foundBan = ban;
|
||||
}
|
||||
|
||||
//For each banned alt
|
||||
for(altIndex in ban.alts){
|
||||
//get current alt
|
||||
const alt = ban.alts[altIndex];
|
||||
|
||||
//if the alt matches our user
|
||||
if(alt._id.toString() == userDB._id.toString()){
|
||||
//Set found ban
|
||||
foundBan = ban;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return foundBan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for ban by username
|
||||
* @param {String} user - User to check for bans
|
||||
* @returns {Mongoose.Document} Found User Ban DB Document
|
||||
*/
|
||||
userBanSchema.statics.checkBan = async function(user){
|
||||
const userDB = await userModel.findOne({user: user.user});
|
||||
return this.checkBanByUserDoc(userDB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks through processed bans by user
|
||||
* @param {String} user - user to check against for bans
|
||||
* @returns {Mongoose.Document} Spent User Ban Document
|
||||
*/
|
||||
userBanSchema.statics.checkProcessedBans = async function(user){
|
||||
//Pull banlist and create empty variable to hold any found ban
|
||||
const banDB = await this.find({});
|
||||
var foundBan = null;
|
||||
|
||||
//For each ban in list
|
||||
banDB.forEach((ban)=>{
|
||||
//For each deleted account associated with the ban
|
||||
ban.deletedNames.forEach((name)=>{
|
||||
//If the banned name equals the name we're checking against
|
||||
if(name == user){
|
||||
//We've found our ban
|
||||
foundBan = ban;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
//Return any found associated ban
|
||||
return foundBan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bans a given user by their user Document
|
||||
* @param {Mongoose.Document} userDB - DB Doc of the user to ban
|
||||
* @param {Boolean} permanent - Whether or not it's permanant
|
||||
* @param {Number} expirationDays - Days to expire
|
||||
* @param {Boolean} ipBan - Whether or not we're banning by IP
|
||||
* @returns {Mongoose.Document} A freshly created User Ban DB Document :)
|
||||
*/
|
||||
userBanSchema.statics.banByUserDoc = async function(userDB, permanent, expirationDays, ipBan = false){
|
||||
//Prevent missing users
|
||||
if(userDB == null){
|
||||
throw loggerUtils.exceptionSmith("User not found", "validation");
|
||||
}
|
||||
|
||||
//Ensure the user isn't already banned
|
||||
if(await this.checkBanByUserDoc(userDB) != null){
|
||||
throw loggerUtils.exceptionSmith("User already banned", "validation");
|
||||
}
|
||||
|
||||
//Verify time to expire/delete depending on action
|
||||
if(expirationDays < 0){
|
||||
throw loggerUtils.exceptionSmith("Expiration Days must be a positive integer!", "validation");
|
||||
}else if(expirationDays < 30 && permanent){
|
||||
throw loggerUtils.exceptionSmith("Permanent bans must be given at least 30 days before automatic account deletion!", "validation");
|
||||
}else if(expirationDays > 185){
|
||||
throw loggerUtils.exceptionSmith("Expiration/Deletion date cannot be longer than half a year out from the original ban date.", "validation");
|
||||
}
|
||||
|
||||
await banSessions(userDB);
|
||||
|
||||
//Add the ban to the database
|
||||
const banDB = await this.create({user: userDB._id, permanent, expirationDays});
|
||||
|
||||
//If we're banning the users IP
|
||||
if(ipBan){
|
||||
//Scrape IP's from current user into the ban record
|
||||
await scrapeUserIPs(userDB);
|
||||
|
||||
//Populate the users alts
|
||||
await userDB.populate('alts');
|
||||
|
||||
//For each of the users alts
|
||||
for(altIndex in userDB.alts){
|
||||
//Add the current alt to the ban record
|
||||
banDB.alts.push(userDB.alts[altIndex]._id);
|
||||
|
||||
//Scrape out the IPs from the current alt into the ban record
|
||||
await scrapeUserIPs(userDB.alts[altIndex]);
|
||||
|
||||
//Kill all of alts sessions
|
||||
await banSessions(userDB.alts[altIndex]);
|
||||
}
|
||||
|
||||
//Save commited IP information to the ban record
|
||||
await banDB.save();
|
||||
|
||||
async function scrapeUserIPs(curRecord){
|
||||
//For each hashed ip on record for this user
|
||||
for(hashIndex in curRecord.recentIPs){
|
||||
//Look for any occurance of the current hash
|
||||
const foundHash = banDB.ips.hashed.indexOf(curRecord.recentIPs[hashIndex].ipHash);
|
||||
|
||||
//If its not listed in the ban record
|
||||
if(foundHash == -1){
|
||||
//Add it to the list of hashed IPs for this ban
|
||||
banDB.ips.hashed.push(curRecord.recentIPs[hashIndex].ipHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//return the ban record
|
||||
return banDB;
|
||||
|
||||
async function banSessions(user){
|
||||
//Log the user out
|
||||
if(permanent){
|
||||
await user.killAllSessions(`Your account has been permanently banned, and will be nuked from the database in ${expirationDays} day(s).`);
|
||||
}else{
|
||||
await user.killAllSessions(`Your account has been temporarily banned, and will be reinstated in: ${expirationDays} day(s).`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bans user by username
|
||||
* @param {String} user - Username of user to ban
|
||||
* @param {Boolean} permanent - Whether or not it's permanant
|
||||
* @param {Number} expirationDays - Days to expire
|
||||
* @param {Boolean} ipBan - Whether or not we're banning by IP
|
||||
* @returns {Mongoose.Document} A freshly created User Ban DB Document :)
|
||||
*/
|
||||
userBanSchema.statics.ban = async function(user, permanent, expirationDays, ipBan){
|
||||
const userDB = await userModel.findOne({user: user.user});
|
||||
return this.banByUserDoc(userDB, permanent, expirationDays, ipBan);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbans users by user doc
|
||||
* @param {Mongoose.Document} userDB - User DB Document to unban
|
||||
* @returns {Mongoose.Document} Old, deleted ban document
|
||||
*/
|
||||
userBanSchema.statics.unbanByUserDoc = async function(userDB){
|
||||
|
||||
//Prevent missing users
|
||||
if(userDB == null){
|
||||
throw loggerUtils.exceptionSmith("User not found", "validation");
|
||||
}
|
||||
|
||||
const banDB = await this.checkBanByUserDoc(userDB);
|
||||
|
||||
if(!banDB){
|
||||
throw loggerUtils.exceptionSmith("User already un-banned", "validation");
|
||||
}
|
||||
|
||||
//Use _id in-case mongoose wants to be a cunt
|
||||
var oldBan = await this.deleteOne({_id: banDB._id});
|
||||
return oldBan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unban deleted user
|
||||
* Can't bring back accounts, but will re-allow re-use of old usernames, and new accounts/connections from banned IP's
|
||||
* @param {String} user - Username of deleted account to unban
|
||||
* @returns {Mongoose.Document} Old, deleted ban document
|
||||
*/
|
||||
userBanSchema.statics.unbanDeleted = async function(user){
|
||||
const banDB = await this.checkProcessedBans(user);
|
||||
|
||||
if(!banDB){
|
||||
throw loggerUtils.exceptionSmith("User already un-banned", "validation");
|
||||
}
|
||||
|
||||
const oldBan = await this.deleteOne({_id: banDB._id});
|
||||
return oldBan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbans user by username
|
||||
* @param {String} user - Username of user to unban
|
||||
* @returns Old, deleted ban document
|
||||
*/
|
||||
userBanSchema.statics.unban = async function(user){
|
||||
//Find user in DB
|
||||
const userDB = await userModel.findOne({user: user.user});
|
||||
|
||||
//If user was deleted
|
||||
if(userDB == null){
|
||||
//unban deleted user
|
||||
return await this.unbanDeleted(user.user);
|
||||
}else{
|
||||
//unban by user doc
|
||||
return await this.unbanByUserDoc(userDB);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates Network-Friendly Browser-Digestable list of bans for the admin panel
|
||||
* @returns {Object} Network-Friendly Browser-Digestable list of bans for the admin panel
|
||||
*/
|
||||
userBanSchema.statics.getBans = async function(){
|
||||
//Get the ban, populating users and alts
|
||||
const banDB = await this.find({}).populate('user').populate('alts');
|
||||
//Create an empty array to hold ban records
|
||||
var bans = [];
|
||||
|
||||
banDB.forEach((ban) => {
|
||||
//Create array to hold alts
|
||||
var alts = [];
|
||||
//Calculate expiration date
|
||||
var expirationDate = new Date(ban.banDate);
|
||||
expirationDate.setDate(expirationDate.getDate() + ban.expirationDays);
|
||||
|
||||
//Make sure we're not about to read the properties of a null object
|
||||
if(ban.user != null){
|
||||
var userObj = ban.user.getProfile();
|
||||
}
|
||||
|
||||
|
||||
//For each alt in the ban
|
||||
for(alt of ban.alts){
|
||||
//Get the profile and push it to the alt list
|
||||
alts.push(alt.getProfile());
|
||||
}
|
||||
|
||||
//Create ban object
|
||||
const banObj = {
|
||||
banDate: ban.banDate,
|
||||
expirationDays: ban.expirationDays,
|
||||
expirationDate: expirationDate,
|
||||
daysUntilExpiration: ban.getDaysUntilExpiration(),
|
||||
user: userObj,
|
||||
ips: ban.ips,
|
||||
alts,
|
||||
deletedNames: ban.deletedNames,
|
||||
permanent: ban.permanent
|
||||
}
|
||||
|
||||
//Add it to the array
|
||||
bans.push(banObj);
|
||||
});
|
||||
|
||||
//Return the array
|
||||
return bans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scheduable function for processing expired user bans
|
||||
*/
|
||||
userBanSchema.statics.processExpiredBans = async function(){
|
||||
//Channel ban expirations may vary so there's no way to search for expired bans
|
||||
const banDB = await this.find({});
|
||||
|
||||
//Firem all off all at once seperately without waiting for one another
|
||||
for(let banIndex in banDB){
|
||||
//Pull ban from banlist by index
|
||||
const ban = banDB[banIndex];
|
||||
|
||||
//This ban was already processed, and it's user has been deleted. There is no more to be done...
|
||||
if(ban.user == null){
|
||||
return;
|
||||
}
|
||||
|
||||
//If the ban hasn't been processed and it's got 0 or less days to go
|
||||
if(ban.getDaysUntilExpiration() <= 0){
|
||||
//If the ban is permanent
|
||||
if(ban.permanent){
|
||||
//Populate the user and alt fields
|
||||
await ban.populate('user');
|
||||
await ban.populate('alts');
|
||||
//Add the name to our deleted names list
|
||||
ban.deletedNames.push(ban.user.user);
|
||||
//Hey hey hey, goodbye!
|
||||
await userModel.deleteOne({_id: ban.user._id});
|
||||
//Empty out the reference
|
||||
ban.user = null;
|
||||
|
||||
//For every alt
|
||||
for(alt of ban.alts){
|
||||
//Add the alts name to the deleted names list
|
||||
ban.deletedNames.push(alt.user);
|
||||
//Motherfuckin' Kablewie!
|
||||
await userModel.deleteOne({_id: alt._id});
|
||||
}
|
||||
|
||||
//Clear out the alts array
|
||||
ban.alts = [];
|
||||
|
||||
//Save the ban
|
||||
await ban.save();
|
||||
}else{
|
||||
//Otherwise, delete the ban and let our user back in :P
|
||||
await this.deleteOne({_id: ban._id});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//methods
|
||||
/**
|
||||
* Calculates days until ban expiration
|
||||
* @returns {Number} Days until ban expiration
|
||||
*/
|
||||
userBanSchema.methods.getDaysUntilExpiration = function(){
|
||||
//Get ban date
|
||||
const expirationDate = new Date(this.banDate);
|
||||
//Get expiration days and calculate expiration date
|
||||
expirationDate.setDate(expirationDate.getDate() + this.expirationDays);
|
||||
//Calculate and return days until ban expiration
|
||||
return ((expirationDate - new Date()) / (1000 * 60 * 60 * 24)).toFixed(1);
|
||||
}
|
||||
|
||||
module.exports = mongoose.model("userBan", userBanSchema);</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
897
www/doc/schemas_user_userSchema.js.html
Normal file
897
www/doc/schemas_user_userSchema.js.html
Normal file
|
|
@ -0,0 +1,897 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: schemas/user/userSchema.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: schemas/user/userSchema.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const {mongoose} = require('mongoose');
|
||||
|
||||
//local imports
|
||||
//server
|
||||
const server = require('../../server');
|
||||
//DB Models
|
||||
const statModel = require('../statSchema');
|
||||
const flairModel = require('../flairSchema');
|
||||
const permissionModel = require('../permissionSchema');
|
||||
const emoteModel = require('../emoteSchema');
|
||||
const emailChangeModel = require('./emailChangeSchema');
|
||||
const playlistSchema = require('../channel/media/playlistSchema');
|
||||
//Utils
|
||||
const hashUtil = require('../../utils/hashUtils');
|
||||
const mailUtil = require('../../utils/mailUtils');
|
||||
const loggerUtils = require('../../utils/loggerUtils')
|
||||
|
||||
|
||||
/**
|
||||
* Mongoose Schema for a document representing a single canopy user
|
||||
*/
|
||||
const userSchema = new mongoose.Schema({
|
||||
id: {
|
||||
type: mongoose.SchemaTypes.Number,
|
||||
required: true
|
||||
},
|
||||
user: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
maxLength: 22,
|
||||
required: true,
|
||||
},
|
||||
pass: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true
|
||||
},
|
||||
email: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
optional: true,
|
||||
default: ""
|
||||
},
|
||||
date: {
|
||||
type: mongoose.SchemaTypes.Date,
|
||||
required: true,
|
||||
default: new Date()
|
||||
},
|
||||
rank: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
enum: permissionModel.rankEnum,
|
||||
default: "user"
|
||||
},
|
||||
tokes: {
|
||||
type: mongoose.SchemaTypes.Map,
|
||||
required: true,
|
||||
default: new Map()
|
||||
},
|
||||
img: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
default: "/img/johnny.png"
|
||||
},
|
||||
bio: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
//Calculate max length by the validator max length and the size of an escaped character
|
||||
maxLength: 1000 * 6,
|
||||
default: "Bio not set!"
|
||||
},
|
||||
pronouns:{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
optional: true,
|
||||
//Calculate max length by the validator max length and the size of an escaped character
|
||||
maxLength: 15 * 6,
|
||||
default: ""
|
||||
},
|
||||
signature: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
//Calculate max length by the validator max length and the size of an escaped character
|
||||
maxLength: 25 * 6,
|
||||
default: "Signature not set!"
|
||||
},
|
||||
highLevel: {
|
||||
type: mongoose.SchemaTypes.Number,
|
||||
required: true,
|
||||
min: 0,
|
||||
max: 10,
|
||||
default: 0
|
||||
},
|
||||
flair: {
|
||||
type: mongoose.SchemaTypes.ObjectID,
|
||||
default: null,
|
||||
ref: "flair"
|
||||
},
|
||||
//Not re-using the site-wide schema because post/pre save should call different functions
|
||||
emotes: [{
|
||||
name:{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true
|
||||
},
|
||||
link:{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true
|
||||
},
|
||||
type:{
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true,
|
||||
enum: emoteModel.typeEnum,
|
||||
default: emoteModel.typeEnum[0]
|
||||
}
|
||||
}],
|
||||
recentIPs: [{
|
||||
ipHash: {
|
||||
type: mongoose.SchemaTypes.String,
|
||||
required: true
|
||||
},
|
||||
firstLog: {
|
||||
type: mongoose.SchemaTypes.Date,
|
||||
required: true,
|
||||
default: new Date()
|
||||
},
|
||||
lastLog: {
|
||||
type: mongoose.SchemaTypes.Date,
|
||||
required: true,
|
||||
default: new Date()
|
||||
}
|
||||
}],
|
||||
alts:[{
|
||||
type: mongoose.SchemaTypes.ObjectID,
|
||||
ref: "user"
|
||||
}],
|
||||
playlists: [playlistSchema]
|
||||
});
|
||||
|
||||
//This is one of those places where you really DON'T want to use an arrow function over an anonymous one!
|
||||
/**
|
||||
* Pre-Save function for user document, handles password hashing, flair updates, emote refreshes, and kills sessions upon rank change
|
||||
*/
|
||||
userSchema.pre('save', async function (next){
|
||||
|
||||
//If the password was changed
|
||||
if(this.isModified("pass")){
|
||||
//Hash that sunnovabitch, no questions asked.
|
||||
this.pass = hashUtil.hashPassword(this.pass);
|
||||
}
|
||||
|
||||
//If the flair was changed
|
||||
if(this.isModified("flair")){
|
||||
//Get flair properties
|
||||
await this.populate('flair');
|
||||
|
||||
if(permissionModel.rankToNum(this.rank) < permissionModel.rankToNum(this.flair.rank)){
|
||||
throw loggerUtils.exceptionSmith(`User '${this.user}' does not have a high enough rank for flair '${this.flair.displayName}'!`, "unauthorized");
|
||||
}
|
||||
}
|
||||
|
||||
//Ensure we don't have empty flair
|
||||
if(this.flair == null){
|
||||
const flairDB = await flairModel.findOne({});
|
||||
this.flair = flairDB._id;
|
||||
}
|
||||
|
||||
//If rank was changed
|
||||
if(this.isModified("rank")){
|
||||
//force a full log-out
|
||||
await this.killAllSessions("Your site-wide rank has changed. Sign-in required.");
|
||||
}
|
||||
|
||||
//if emotes where modified
|
||||
if(this.isModified('emotes')){
|
||||
//Get the active Channel object from the application side of the house
|
||||
server.channelManager.crawlConnections(this.user, (conn)=>{
|
||||
//Send out emotes to each one
|
||||
conn.sendPersonalEmotes(this);
|
||||
});
|
||||
}
|
||||
|
||||
//All is good, continue on saving.
|
||||
next();
|
||||
});
|
||||
|
||||
/**
|
||||
* Pre-Delete function for user accounts, drops connections and rips it self out from alt accounts
|
||||
*/
|
||||
userSchema.post('deleteOne', {document: true}, async function (){
|
||||
//Kill any active sessions
|
||||
await this.killAllSessions("If you're seeing this, your account has been deleted. So long, and thanks for all the fish! <3");
|
||||
|
||||
//Populate alts
|
||||
await this.populate('alts');
|
||||
|
||||
//iterate through alts
|
||||
for(alt in this.alts){
|
||||
//Find the index of the alt entry for this user inside of the alt users array of alt users
|
||||
const altIndex = this.alts[alt].alts.indexOf(this._id);
|
||||
|
||||
//splice the entry for this user out of the alt users array of alt users
|
||||
this.alts[alt].alts.splice(altIndex,1);
|
||||
|
||||
//Save the alt user
|
||||
await this.alts[alt].save();
|
||||
}
|
||||
});
|
||||
|
||||
//statics
|
||||
/**
|
||||
* Registers a new user account with given information
|
||||
* @param {Object} userObj - Object representing user to register, generated by the client
|
||||
* @param {String} ip - IP Address of connection registering the account
|
||||
*/
|
||||
userSchema.statics.register = async function(userObj, ip){
|
||||
//Pull values from user object
|
||||
const {user, pass, passConfirm, email} = userObj;
|
||||
|
||||
//Check password confirmation matches
|
||||
if(pass == passConfirm){
|
||||
//Look for a user (case insensitive)
|
||||
var userDB = await this.findOne({user: new RegExp(user, 'i')});
|
||||
|
||||
//If the user is found or someones trying to impersonate tokeboi
|
||||
if(userDB || user.toLowerCase() == "tokebot"){
|
||||
throw loggerUtils.exceptionSmith("User name/email already taken!", "validation");
|
||||
}else{
|
||||
//Increment the user count, pulling the id to tattoo to the user
|
||||
const id = await statModel.incrementUserCount();
|
||||
|
||||
//Create user document in the database
|
||||
const newUser = await this.create({id, user, pass});
|
||||
|
||||
//Tattoo the hashed IP used to register to the new user
|
||||
await newUser.tattooIPRecord(ip);
|
||||
|
||||
//if we submitted an email
|
||||
if(email != null){
|
||||
const requestDB = await emailChangeModel.create({user: newUser._id, newEmail: email, ipHash: ip});
|
||||
|
||||
await mailUtil.sendAddressVerification(requestDB, newUser, email)
|
||||
}
|
||||
}
|
||||
}else{
|
||||
throw loggerUtils.exceptionSmith("Confirmation password doesn't match!", "validation");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates a username and password pair
|
||||
* @param {String} user - Username of account to login as
|
||||
* @param {String} pass - Password to authenticat account
|
||||
* @param {String} failLine - Line to paste into custom error upon login failure
|
||||
* @returns {Mongoose.Document} - User DB Document upon success
|
||||
*/
|
||||
userSchema.statics.authenticate = async function(user, pass, failLine = "Bad Username or Password."){
|
||||
//check for missing pass
|
||||
if(!user || !pass){
|
||||
throw loggerUtils.exceptionSmith("Missing user/pass.", "validation");
|
||||
}
|
||||
|
||||
//get the user if it exists
|
||||
const userDB = await this.findOne({ user: new RegExp(user, 'i')});
|
||||
|
||||
//if not scream and shout
|
||||
if(!userDB){
|
||||
badLogin();
|
||||
}
|
||||
|
||||
//Check our password is correct
|
||||
if(userDB.checkPass(pass)){
|
||||
return userDB;
|
||||
}else{
|
||||
//if not scream and shout
|
||||
badLogin();
|
||||
}
|
||||
|
||||
//standardize bad login response so it's unknown which is bad for security reasons.
|
||||
function badLogin(){
|
||||
throw loggerUtils.exceptionSmith(failLine, "unauthorized");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets profile by username
|
||||
* @param {String} user - name of user to find
|
||||
* @param {Boolean} includeEmail - Whether or not to include email in the final result
|
||||
* @returns {Object} A network-friendly browser-digestable Object representing a single user profile
|
||||
*/
|
||||
userSchema.statics.findProfile = async function(user, includeEmail = false){
|
||||
//Catch null users
|
||||
if(user == null || user.user == null){
|
||||
return null;
|
||||
//If someone's looking for tokebot
|
||||
}else if(user.user.toLowerCase() == "tokebot"){
|
||||
//fake a profile hashtable for tokebot
|
||||
const profile = {
|
||||
id: -420,
|
||||
user: "Tokebot",
|
||||
date: (await statModel.getStats()).firstLaunch,
|
||||
tokes: await statModel.getTokeCommandCounts(),
|
||||
tokeCount: await statModel.getTokeCount(),
|
||||
img: "/img/johnny.png",
|
||||
signature: "!TOKE",
|
||||
bio: "!TOKE OR DIE!"
|
||||
};
|
||||
|
||||
//return the faked profile
|
||||
return profile;
|
||||
}else{
|
||||
//find user
|
||||
const userDB = await this.findOne({user: user.user});
|
||||
|
||||
//If we don't find a user just return a null profile
|
||||
if(userDB == null){
|
||||
return null
|
||||
}
|
||||
|
||||
//return the profile
|
||||
return userDB.getProfile(includeEmail);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tattoos a single toke callout to all the users within it
|
||||
* @param {Map} tokemap - Map containing list of users and the toke command they used to join the toke
|
||||
*/
|
||||
userSchema.statics.tattooToke = function(tokemap){
|
||||
//For each toke, asynchronously:
|
||||
tokemap.forEach(async (toke, user) => {
|
||||
//get user
|
||||
const userDB = await this.findOne({user});
|
||||
|
||||
//Check that the user exists (might come in handy for future treez.one integration?)
|
||||
if(userDB != null){
|
||||
var tokeCount = userDB.tokes.get(toke);
|
||||
|
||||
//if this is the first time using this toke command
|
||||
if(tokeCount == null){
|
||||
//set toke count to one
|
||||
tokeCount = 1;
|
||||
//otherwise
|
||||
}else{
|
||||
//increment tokecount
|
||||
tokeCount++;
|
||||
}
|
||||
|
||||
//Set the toke count for the specific command
|
||||
userDB.tokes.set(toke, tokeCount);
|
||||
|
||||
//Save the user doc to the database
|
||||
await userDB.save();
|
||||
|
||||
//Would rather do this inside of tokebot but then our boi would have to wait for DB or pass a nasty-looking callback function
|
||||
//Crawl through active connections
|
||||
server.channelManager.crawlConnections(userDB.user, (conn)=>{
|
||||
//Update used toke list
|
||||
conn.sendUsedTokes(userDB);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires a list of the entire userbase
|
||||
* @param {Boolean} fullList - Determines whether or not we're listing rank and email
|
||||
* @returns {Array} Array of objects containing user metadata
|
||||
*/
|
||||
userSchema.statics.getUserList = async function(fullList = false){
|
||||
var userList = [];
|
||||
//Get all of our users
|
||||
const users = await this.find({});
|
||||
|
||||
//Return empty if we don't find nuthin'
|
||||
if(users == null){
|
||||
return [];
|
||||
}
|
||||
|
||||
//For each user
|
||||
users.forEach((user)=>{
|
||||
//create a user object with limited properties (safe for public consumption)
|
||||
var userObj = {
|
||||
id: user.id,
|
||||
user: user.user,
|
||||
img: user.img,
|
||||
date: user.date
|
||||
}
|
||||
|
||||
//Put together a spicier version for admins when told so (permission checks should happen before this is called)
|
||||
if(fullList){
|
||||
userObj.rank = user.rank,
|
||||
userObj.email = user.email
|
||||
}
|
||||
|
||||
//Add user object to list
|
||||
userList.push(userObj);
|
||||
});
|
||||
|
||||
//return the userlist
|
||||
return userList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process and Deletes Aged IP Records
|
||||
*/
|
||||
userSchema.statics.processAgedIPRecords = async function(){
|
||||
//Pull full userlist
|
||||
const users = await this.find({});
|
||||
|
||||
//Not a fan of iterating through the DB but there doesn't seem to be a way to search for a doc based on the properties of it's subdoc
|
||||
for(let userIndex in users){
|
||||
//Pull user record from users by index
|
||||
const userDB = users[userIndex];
|
||||
//For every recent ip within the user
|
||||
for(let recordIndex in userDB.recentIPs){
|
||||
//Pull record from recent IPs by index
|
||||
const record = userDB.recentIPs[recordIndex];
|
||||
//Check how long it's been since we've last seen the IP
|
||||
const daysSinceLastUse = ((new Date() - record.lastLog) / (1000 * 60 * 60 * 24)).toFixed(1);
|
||||
|
||||
//If it's been more than a week
|
||||
if(daysSinceLastUse >= 7){
|
||||
//Splice out the IP record
|
||||
userDB.recentIPs.splice(recordIndex, 1);
|
||||
//No reason to wait on this since we're done with this user
|
||||
await userDB.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//methods
|
||||
/**
|
||||
* Checks password against a user document
|
||||
* @param {String} pass - Password to authenticate
|
||||
* @returns {Boolean} True if authenticated
|
||||
*/
|
||||
userSchema.methods.checkPass = function(pass){
|
||||
return hashUtil.comparePassword(pass, this.pass)
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists authenticated sessions for a given user document
|
||||
* @returns {Promise} Promise containing an array of active user sessions for a given user
|
||||
*/
|
||||
userSchema.methods.getAuthenticatedSessions = async function(){
|
||||
var returnArr = [];
|
||||
|
||||
//retrieve active sessions (they really need to implement this shit async already)
|
||||
return new Promise((resolve) => {
|
||||
server.store.all((err, sessions) => {
|
||||
//You guys ever hear of a 'not my' problem? Fucking y33tskies lmao, better use a try/catch
|
||||
if(err){
|
||||
throw err;
|
||||
|
||||
}
|
||||
|
||||
//crawl through active sessions
|
||||
sessions.forEach((session) => {
|
||||
//Skip un-authed sessions
|
||||
if(session.user != null){
|
||||
//if a session matches the current user
|
||||
if(session.user.id == this.id){
|
||||
//we return it
|
||||
returnArr.push(session);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
resolve(returnArr);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a network-friendly browser-digestable profile object for a sepcific user document
|
||||
* @param {Boolean} includeEmail - Whether or not to include the email address in the complete profile object
|
||||
* @returns {Object} Network-Friendly Browser-Digestable object containing profile metadata
|
||||
*/
|
||||
userSchema.methods.getProfile = function(includeEmail = false){
|
||||
//Create profile hashtable
|
||||
const profile = {
|
||||
id: this.id,
|
||||
user: this.user,
|
||||
date: this.date,
|
||||
tokes: this.tokes,
|
||||
tokeCount: this.getTokeCount(),
|
||||
img: this.img,
|
||||
signature: this.signature,
|
||||
pronouns: this.pronouns,
|
||||
bio: this.bio
|
||||
};
|
||||
|
||||
//Include the email if we need to
|
||||
if(includeEmail){
|
||||
profile.email = this.email;
|
||||
}
|
||||
|
||||
//return profile hashtable
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets list of profiles of alt accounts related to the given user document
|
||||
* @returns {Array} List of alternate profiles
|
||||
*/
|
||||
userSchema.methods.getAltProfiles = async function(){
|
||||
//Create an empty list to hold alt profiles
|
||||
const profileList = [];
|
||||
|
||||
//populate the users alt list
|
||||
await this.populate('alts');
|
||||
|
||||
//For every alt for the current user
|
||||
for(let alt of this.alts){
|
||||
//get the alts profile and push it to the profile list
|
||||
profileList.push(alt.getProfile());
|
||||
}
|
||||
|
||||
//return our generated profile list
|
||||
return profileList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets flair for current user documetn
|
||||
* @param {String} flair - flair to set
|
||||
* @returns {Mongoose.Document} returns found flair document from DB
|
||||
*/
|
||||
userSchema.methods.setFlair = async function(flair){
|
||||
//Find flair by name
|
||||
const flairDB = await flairModel.findOne({name: flair});
|
||||
//Set the users flair ref to the found flairs _id
|
||||
this.flair = flairDB._id;
|
||||
//Save the user
|
||||
await this.save();
|
||||
//return the found flair
|
||||
return flairDB;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets number of tokes for a given user document
|
||||
* @returns {Number} Number of tokes recorded for the given user document
|
||||
*/
|
||||
userSchema.methods.getTokeCount = function(){
|
||||
//Set tokeCount to 0
|
||||
var tokeCount = 0;
|
||||
|
||||
//For each toke command the user has used
|
||||
this.tokes.forEach((commandCount) => {
|
||||
//Add the count for that specific command to the total
|
||||
tokeCount += commandCount;
|
||||
});
|
||||
|
||||
//Return the amount of tokes recorded
|
||||
return tokeCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets number of emotes for a given user document
|
||||
* @returns {Array} Array of objects representing emotes for the given user document
|
||||
*/
|
||||
userSchema.methods.getEmotes = function(){
|
||||
//Create an empty array to hold our emote list
|
||||
const emoteList = [];
|
||||
|
||||
//For each channel emote
|
||||
this.emotes.forEach((emote) => {
|
||||
//Push an object with select information from the emote to the emote list
|
||||
emoteList.push({
|
||||
name: emote.name,
|
||||
link: emote.link,
|
||||
type: emote.type
|
||||
});
|
||||
});
|
||||
|
||||
//return the emote list
|
||||
return emoteList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an emote from the user Document
|
||||
* @param {String} name - Name of emote to delete
|
||||
*/
|
||||
userSchema.methods.deleteEmote = async function(name){
|
||||
//Get index by emote name
|
||||
const emoteIndex = this.emotes.findIndex(checkName);
|
||||
|
||||
//Splice out found emote
|
||||
this.emotes.splice(emoteIndex, 1);
|
||||
|
||||
//Save the user doc
|
||||
await this.save();
|
||||
|
||||
function checkName(emote){
|
||||
//return emotes
|
||||
return emote.name == name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets list of user playlists
|
||||
* @returns {Array} Array of objects represnting a playlist containing media objects
|
||||
*/
|
||||
userSchema.methods.getPlaylists = function(){
|
||||
//Create an empty array to hold our emote list
|
||||
const playlists = [];
|
||||
|
||||
//For each channel emote
|
||||
for(let playlist of this.playlists){
|
||||
//Push an object with select information from the emote to the emote list
|
||||
playlists.push(playlist.dehydrate());
|
||||
}
|
||||
|
||||
//return the emote list
|
||||
return playlists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through user playlists and calls a given callback function against them
|
||||
* @param {Function} cb - Callback function to call against user playlists
|
||||
*/
|
||||
userSchema.methods.playlistCrawl = function(cb){
|
||||
for(let listIndex in this.playlists){
|
||||
//Grab the associated playlist
|
||||
playlist = this.playlists[listIndex];
|
||||
|
||||
//Call the callback with the playlist and list index as arguments
|
||||
cb(playlist, listIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns playlist by name
|
||||
* @param {String} name - Playlist to get by name
|
||||
* @returns {Object} Object representing the requested playlist
|
||||
*/
|
||||
userSchema.methods.getPlaylistByName = function(name){
|
||||
//Create null value to hold our found playlist
|
||||
let foundPlaylist = null;
|
||||
|
||||
//Crawl through active playlists
|
||||
this.playlistCrawl((playlist, listIndex) => {
|
||||
//If we found a match based on name
|
||||
if(playlist.name == name){
|
||||
//Keep it
|
||||
foundPlaylist = playlist;
|
||||
//Pass down the list index
|
||||
foundPlaylist.listIndex = listIndex;
|
||||
}
|
||||
});
|
||||
|
||||
//return the given playlist
|
||||
return foundPlaylist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a playlist from the user document by name
|
||||
* @param {String} name - Name of playlist to delete
|
||||
*/
|
||||
userSchema.methods.deletePlaylistByName = async function(name){
|
||||
//Find the playlist
|
||||
const playlist = this.getPlaylistByName(name);
|
||||
|
||||
//splice out the given playlist
|
||||
this.playlists.splice(playlist.listIndex, 1);
|
||||
|
||||
//save the channel document
|
||||
await this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Salts, Hashes and Tattoo's IP address to user document in a privacy respecting manner
|
||||
* @param {String} ip - Plaintext IP Address to Salt, Hash and Tattoo
|
||||
*/
|
||||
userSchema.methods.tattooIPRecord = async function(ip){
|
||||
//Hash the users ip
|
||||
const ipHash = hashUtil.hashIP(ip);
|
||||
|
||||
//Look for a pre-existing entry for this ipHash
|
||||
const foundIndex = this.recentIPs.findIndex(checkHash);
|
||||
|
||||
//If there is no entry
|
||||
if(foundIndex == -1){
|
||||
//Pull the entire userlist
|
||||
//TODO: update query to only pull users with recentIPs, so we aren't looping through inactive users
|
||||
const users = await this.model().find({});
|
||||
|
||||
//create record object
|
||||
const record = {
|
||||
ipHash: ipHash,
|
||||
firstLog: new Date(),
|
||||
lastLog: new Date()
|
||||
};
|
||||
|
||||
//We should really start using for loops and stop acting like its 2008
|
||||
//Though to be quite honest this bit would be particularly brutal without them
|
||||
//For every user in the userlist
|
||||
for(let curUser of users){
|
||||
//Ensure we're not checking the user against itself
|
||||
if(curUser._id != this._id){
|
||||
//For every IP record in the current user
|
||||
for(let curRecord of curUser.recentIPs){
|
||||
//If it matches the current ipHash
|
||||
if(curRecord.ipHash == ipHash){
|
||||
//Check if we've already marked the user as an alt
|
||||
const foundAlt = this.alts.indexOf(curUser._id);
|
||||
|
||||
//If these accounts aren't already marked as alts
|
||||
if(foundAlt == -1){
|
||||
//Add found user to this users alt list
|
||||
this.alts.push(curUser._id);
|
||||
|
||||
//add this user to found users alt list
|
||||
curUser.alts.push(this._id);
|
||||
|
||||
//Save changes to the found user, this user will save at the end of the function
|
||||
await curUser.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Pop it into place
|
||||
this.recentIPs.push(record);
|
||||
|
||||
//Save the user doc
|
||||
await this.save();
|
||||
//Otherwise, if we already have a record for this IP
|
||||
}else{
|
||||
//Update the last logged date for the found record
|
||||
this.recentIPs[foundIndex].lastLog = new Date();
|
||||
|
||||
//Save the user doc
|
||||
await this.save();
|
||||
}
|
||||
|
||||
//Look for matching ip record
|
||||
function checkHash(ipRecord){
|
||||
//return matching records
|
||||
return ipRecord.ipHash == ipHash;
|
||||
}
|
||||
}
|
||||
|
||||
//note: if you gotta call this from a request authenticated by it's user, make sure to kill that session first!
|
||||
/**
|
||||
* Kills all sessions for a given user
|
||||
* @param {String} reason - Reason to kill user sessions
|
||||
*/
|
||||
userSchema.methods.killAllSessions = async function(reason = "A full log-out from all devices was requested for your account."){
|
||||
//get authenticated sessions
|
||||
var sessions = await this.getAuthenticatedSessions();
|
||||
|
||||
//crawl through and kill all sessions
|
||||
sessions.forEach((session) => {
|
||||
server.store.destroy(session.seshid);
|
||||
});
|
||||
|
||||
//Tell the application side of the house to kick the user out as well
|
||||
server.channelManager.kickConnections(this.user, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes password for a given user document
|
||||
* @param {Object} passChange - passChange object handed down from Browser
|
||||
*/
|
||||
userSchema.methods.changePassword = async function(passChange){
|
||||
if(this.checkPass(passChange.oldPass)){
|
||||
if(passChange.newPass == passChange.confirmPass){
|
||||
//Note: We don't have to worry about hashing here because the schema is written to do it auto-magically
|
||||
this.pass = passChange.newPass;
|
||||
|
||||
//Save our password
|
||||
await this.save();
|
||||
|
||||
//Kill all authed sessions for security purposes
|
||||
await this.killAllSessions("Your password has been reset.");
|
||||
}else{
|
||||
//confirmation pass doesn't match
|
||||
throw loggerUtils.exceptionSmith("Mismatched confirmation password!", "validation");
|
||||
}
|
||||
}else{
|
||||
//Old password wrong
|
||||
throw loggerUtils.exceptionSmith("Incorrect Password!", "validation");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks another user document against this one to see if they're alts
|
||||
* @param {Mongoose.Document} userDB - User document to check for alts against
|
||||
* @returns {Boolean} True if alt
|
||||
*/
|
||||
userSchema.methods.altCheck = async function(userDB){
|
||||
//Found alt flag
|
||||
let foundAlt = false;
|
||||
|
||||
for(alt in this.alts){
|
||||
//If we found a match
|
||||
if(this.alts[alt]._id.toString() == userDB._id.toString()){
|
||||
foundAlt = true;
|
||||
}
|
||||
}
|
||||
|
||||
//return the results
|
||||
return foundAlt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nukes user account from the face of the planet upon said user's request
|
||||
* @param {String} pass - Password to authenticate against before deleting
|
||||
*/
|
||||
userSchema.methods.nuke = async function(pass){
|
||||
//Check we have a confirmation password
|
||||
if(pass == "" || pass == null){
|
||||
//scream and shout
|
||||
throw loggerUtils.exceptionSmith("No confirmation password!", "validation");
|
||||
}
|
||||
|
||||
//Check that the password is correct
|
||||
if(this.checkPass(pass)){
|
||||
//delete the user
|
||||
var oldUser = await this.deleteOne();
|
||||
}else{
|
||||
//complain about a bad pass
|
||||
throw loggerUtils.exceptionSmith("Bad pass.", "unauthorized");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.userModel = mongoose.model("user", userSchema);</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
25
www/doc/scripts/linenumber.js
Normal file
25
www/doc/scripts/linenumber.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/*global document */
|
||||
(() => {
|
||||
const source = document.getElementsByClassName('prettyprint source linenums');
|
||||
let i = 0;
|
||||
let lineNumber = 0;
|
||||
let lineId;
|
||||
let lines;
|
||||
let totalLines;
|
||||
let anchorHash;
|
||||
|
||||
if (source && source[0]) {
|
||||
anchorHash = document.location.hash.substring(1);
|
||||
lines = source[0].getElementsByTagName('li');
|
||||
totalLines = lines.length;
|
||||
|
||||
for (; i < totalLines; i++) {
|
||||
lineNumber++;
|
||||
lineId = `line${lineNumber}`;
|
||||
lines[i].id = lineId;
|
||||
if (lineId === anchorHash) {
|
||||
lines[i].className += ' selected';
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
202
www/doc/scripts/prettify/Apache-License-2.0.txt
Normal file
202
www/doc/scripts/prettify/Apache-License-2.0.txt
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
2
www/doc/scripts/prettify/lang-css.js
Normal file
2
www/doc/scripts/prettify/lang-css.js
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n"]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com",
|
||||
/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]);
|
||||
28
www/doc/scripts/prettify/prettify.js
Normal file
28
www/doc/scripts/prettify/prettify.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
|
||||
(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a=
|
||||
[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c<i;++c){var j=f[c];if(/\\[bdsw]/i.test(j))a.push(j);else{var j=m(j),d;c+2<i&&"-"===f[c+1]?(d=m(f[c+2]),c+=2):d=j;b.push([j,d]);d<65||j>122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;c<b.length;++c)i=b[c],i[0]<=j[1]+1?j[1]=Math.max(j[1],i[1]):f.push(j=i);b=["["];o&&b.push("^");b.push.apply(b,a);for(c=0;c<
|
||||
f.length;++c)i=f[c],b.push(e(i[0])),i[1]>i[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c<b;++c){var j=f[c];j==="("?++i:"\\"===j.charAt(0)&&(j=+j.substring(1))&&j<=i&&(d[j]=-1)}for(c=1;c<d.length;++c)-1===d[c]&&(d[c]=++t);for(i=c=0;c<b;++c)j=f[c],j==="("?(++i,d[i]===void 0&&(f[c]="(?:")):"\\"===j.charAt(0)&&
|
||||
(j=+j.substring(1))&&j<=i&&(f[c]="\\"+d[i]);for(i=c=0;c<b;++c)"^"===f[c]&&"^"!==f[c+1]&&(f[c]="");if(a.ignoreCase&&s)for(c=0;c<b;++c)j=f[c],a=j.charAt(0),j.length>=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p<d;++p){var g=a[p];if(g.ignoreCase)l=!0;else if(/[a-z]/i.test(g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){s=!0;l=!1;break}}for(var r=
|
||||
{b:8,t:9,n:10,v:11,f:12,r:13},n=[],p=0,d=a.length;p<d;++p){g=a[p];if(g.global||g.multiline)throw Error(""+g);n.push("(?:"+y(g)+")")}return RegExp(n.join("|"),l?"gi":"g")}function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.className))break;for(var g=a.firstChild;g;g=g.nextSibling)m(g);g=a.nodeName;if("BR"===g||"LI"===g)h[s]="\n",t[s<<1]=y++,t[s++<<1|1]=a;break;case 3:case 4:g=a.nodeValue,g.length&&(g=p?g.replace(/\r\n?/g,"\n"):g.replace(/[\t\n\r ]+/g," "),h[s]=g,t[s<<1]=y,y+=g.length,
|
||||
t[s++<<1|1]=a)}}var e=/(?:^|\s)nocode(?:\s|$)/,h=[],y=0,t=[],s=0,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=document.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);m(a);return{a:h.join("").replace(/\n$/,""),c:t}}function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(y)||[],r={},n=0,z=g.length;n<z;++n){var f=g[n],b=r[f],o=void 0,c;if(typeof b===
|
||||
"string")c=!1;else{var i=h[f.charAt(0)];if(i)o=f.match(i[1]),b=i[0];else{for(c=0;c<t;++c)if(i=m[c],o=f.match(i[1])){b=i[0];break}o||(b="pln")}if((c=b.length>=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m),
|
||||
l=[],p={},d=0,g=e.length;d<g;++d){var r=e[d],n=r[3];if(n)for(var k=n.length;--k>=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
|
||||
q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/,
|
||||
q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g,
|
||||
"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a),
|
||||
a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e}
|
||||
for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g<d.length;++g)e(d[g]);m===(m|0)&&d[0].setAttribute("value",
|
||||
m);var r=s.createElement("OL");r.className="linenums";for(var n=Math.max(0,m-1|0)||0,g=0,z=d.length;g<z;++g)l=d[g],l.className="L"+(g+n)%10,l.firstChild||l.appendChild(s.createTextNode("\xa0")),r.appendChild(l);a.appendChild(r)}function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-markup":"default-code";return A[a]}function E(a){var m=
|
||||
a.g;try{var e=M(a.h),h=e.a;a.a=h;a.c=e.c;a.d=0;C(m,h)(a);var k=/\bMSIE\b/.test(navigator.userAgent),m=/\n/g,t=a.a,s=t.length,e=0,l=a.c,p=l.length,h=0,d=a.e,g=d.length,a=0;d[g]=s;var r,n;for(n=r=0;n<g;)d[n]!==d[n+2]?(d[r++]=d[n++],d[r++]=d[n++]):n+=2;g=r;for(n=r=0;n<g;){for(var z=d[n],f=d[n+1],b=n+2;b+2<=g&&d[b+1]===f;)b+=2;d[r++]=z;d[r++]=f;n=b}for(d.length=r;h<p;){var o=l[h+2]||s,c=d[a+2]||s,b=Math.min(o,c),i=l[h+1],j;if(i.nodeType!==1&&(j=t.substring(e,b))){k&&(j=j.replace(m,"\r"));i.nodeValue=
|
||||
j;var u=i.ownerDocument,v=u.createElement("SPAN");v.className=d[a+1];var x=i.parentNode;x.replaceChild(v,i);v.appendChild(i);e<o&&(l[h+1]=i=u.createTextNode(t.substring(b,o)),x.insertBefore(i,v.nextSibling))}e=b;e>=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
|
||||
"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],
|
||||
H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
|
||||
J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+
|
||||
I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),
|
||||
["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",
|
||||
/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),
|
||||
["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes",
|
||||
hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p<h.length&&l.now()<e;p++){var n=h[p],k=n.className;if(k.indexOf("prettyprint")>=0){var k=k.match(g),f,b;if(b=
|
||||
!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p<h.length?setTimeout(m,
|
||||
250):a&&a()}for(var e=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],h=[],k=0;k<e.length;++k)for(var t=0,s=e[k].length;t<s;++t)h.push(e[k][t]);var e=q,l=Date;l.now||(l={now:function(){return+new Date}});var p=0,d,g=/\blang(?:uage)?-([\w.]+)(?!\S)/;m()};window.PR={createSimpleLexer:x,registerLangHandler:k,sourceDecorator:u,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",
|
||||
PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ"}})();
|
||||
358
www/doc/styles/jsdoc-default.css
Normal file
358
www/doc/styles/jsdoc-default.css
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
src: url('../fonts/OpenSans-Regular-webfont.eot');
|
||||
src:
|
||||
local('Open Sans'),
|
||||
local('OpenSans'),
|
||||
url('../fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('../fonts/OpenSans-Regular-webfont.woff') format('woff'),
|
||||
url('../fonts/OpenSans-Regular-webfont.svg#open_sansregular') format('svg');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans Light';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
src: url('../fonts/OpenSans-Light-webfont.eot');
|
||||
src:
|
||||
local('Open Sans Light'),
|
||||
local('OpenSans Light'),
|
||||
url('../fonts/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('../fonts/OpenSans-Light-webfont.woff') format('woff'),
|
||||
url('../fonts/OpenSans-Light-webfont.svg#open_sanslight') format('svg');
|
||||
}
|
||||
|
||||
html
|
||||
{
|
||||
overflow: auto;
|
||||
background-color: #fff;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
line-height: 1.5;
|
||||
color: #4d4e53;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
a, a:visited, a:active {
|
||||
color: #0095dd;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
header
|
||||
{
|
||||
display: block;
|
||||
padding: 0px 4px;
|
||||
}
|
||||
|
||||
tt, code, kbd, samp {
|
||||
font-family: Consolas, Monaco, 'Andale Mono', monospace;
|
||||
}
|
||||
|
||||
.class-description {
|
||||
font-size: 130%;
|
||||
line-height: 140%;
|
||||
margin-bottom: 1em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.class-description:empty {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#main {
|
||||
float: left;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
article dl {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
article img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
section
|
||||
{
|
||||
display: block;
|
||||
background-color: #fff;
|
||||
padding: 12px 24px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
.variation {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.signature-attributes {
|
||||
font-size: 60%;
|
||||
color: #aaa;
|
||||
font-style: italic;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
nav
|
||||
{
|
||||
display: block;
|
||||
float: right;
|
||||
margin-top: 28px;
|
||||
width: 30%;
|
||||
box-sizing: border-box;
|
||||
border-left: 1px solid #ccc;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif;
|
||||
font-size: 100%;
|
||||
line-height: 17px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
nav ul a, nav ul a:visited, nav ul a:active {
|
||||
font-family: Consolas, Monaco, 'Andale Mono', monospace;
|
||||
line-height: 18px;
|
||||
color: #4D4E53;
|
||||
}
|
||||
|
||||
nav h3 {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
nav li {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: block;
|
||||
padding: 6px;
|
||||
margin-top: 12px;
|
||||
font-style: italic;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
font-weight: 200;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1
|
||||
{
|
||||
font-family: 'Open Sans Light', sans-serif;
|
||||
font-size: 48px;
|
||||
letter-spacing: -2px;
|
||||
margin: 12px 24px 20px;
|
||||
}
|
||||
|
||||
h2, h3.subsection-title
|
||||
{
|
||||
font-size: 30px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -1px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
h3
|
||||
{
|
||||
font-size: 24px;
|
||||
letter-spacing: -0.5px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
h4
|
||||
{
|
||||
font-size: 18px;
|
||||
letter-spacing: -0.33px;
|
||||
margin-bottom: 12px;
|
||||
color: #4d4e53;
|
||||
}
|
||||
|
||||
h5, .container-overview .subsection-title
|
||||
{
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
letter-spacing: -0.01em;
|
||||
margin: 8px 0 3px 0;
|
||||
}
|
||||
|
||||
h6
|
||||
{
|
||||
font-size: 100%;
|
||||
letter-spacing: -0.01em;
|
||||
margin: 6px 0 3px 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
table
|
||||
{
|
||||
border-spacing: 0;
|
||||
border: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
td, th
|
||||
{
|
||||
border: 1px solid #ddd;
|
||||
margin: 0px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
padding: 4px 6px;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
thead tr
|
||||
{
|
||||
background-color: #ddd;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
th { border-right: 1px solid #aaa; }
|
||||
tr > th:last-child { border-right: 1px solid #ddd; }
|
||||
|
||||
.ancestors, .attribs { color: #999; }
|
||||
.ancestors a, .attribs a
|
||||
{
|
||||
color: #999 !important;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.clear
|
||||
{
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.important
|
||||
{
|
||||
font-weight: bold;
|
||||
color: #950B02;
|
||||
}
|
||||
|
||||
.yes-def {
|
||||
text-indent: -1000px;
|
||||
}
|
||||
|
||||
.type-signature {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.name, .signature {
|
||||
font-family: Consolas, Monaco, 'Andale Mono', monospace;
|
||||
}
|
||||
|
||||
.details { margin-top: 14px; border-left: 2px solid #DDD; }
|
||||
.details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; }
|
||||
.details dd { margin-left: 70px; }
|
||||
.details ul { margin: 0; }
|
||||
.details ul { list-style-type: none; }
|
||||
.details li { margin-left: 30px; padding-top: 6px; }
|
||||
.details pre.prettyprint { margin: 0 }
|
||||
.details .object-value { padding-top: 0; }
|
||||
|
||||
.description {
|
||||
margin-bottom: 1em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.code-caption
|
||||
{
|
||||
font-style: italic;
|
||||
font-size: 107%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.source
|
||||
{
|
||||
border: 1px solid #ddd;
|
||||
width: 80%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.prettyprint.source {
|
||||
width: inherit;
|
||||
}
|
||||
|
||||
.source code
|
||||
{
|
||||
font-size: 100%;
|
||||
line-height: 18px;
|
||||
display: block;
|
||||
padding: 4px 12px;
|
||||
margin: 0;
|
||||
background-color: #fff;
|
||||
color: #4D4E53;
|
||||
}
|
||||
|
||||
.prettyprint code span.line
|
||||
{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.prettyprint.linenums
|
||||
{
|
||||
padding-left: 70px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.prettyprint.linenums ol
|
||||
{
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.prettyprint.linenums li
|
||||
{
|
||||
border-left: 3px #ddd solid;
|
||||
}
|
||||
|
||||
.prettyprint.linenums li.selected,
|
||||
.prettyprint.linenums li.selected *
|
||||
{
|
||||
background-color: lightyellow;
|
||||
}
|
||||
|
||||
.prettyprint.linenums li *
|
||||
{
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.params .name, .props .name, .name code {
|
||||
color: #4D4E53;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', monospace;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
.params td.description > p:first-child,
|
||||
.props td.description > p:first-child
|
||||
{
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.params td.description > p:last-child,
|
||||
.props td.description > p:last-child
|
||||
{
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
color: #454545;
|
||||
}
|
||||
111
www/doc/styles/prettify-jsdoc.css
Normal file
111
www/doc/styles/prettify-jsdoc.css
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/* JSDoc prettify.js theme */
|
||||
|
||||
/* plain text */
|
||||
.pln {
|
||||
color: #000000;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* string content */
|
||||
.str {
|
||||
color: #006400;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a keyword */
|
||||
.kwd {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a comment */
|
||||
.com {
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* a type name */
|
||||
.typ {
|
||||
color: #000000;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a literal value */
|
||||
.lit {
|
||||
color: #006400;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* punctuation */
|
||||
.pun {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* lisp open bracket */
|
||||
.opn {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* lisp close bracket */
|
||||
.clo {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a markup tag name */
|
||||
.tag {
|
||||
color: #006400;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a markup attribute name */
|
||||
.atn {
|
||||
color: #006400;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a markup attribute value */
|
||||
.atv {
|
||||
color: #006400;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a declaration */
|
||||
.dec {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a variable name */
|
||||
.var {
|
||||
color: #000000;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* a function name */
|
||||
.fun {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* Specify class=linenums on a pre to get line numbering */
|
||||
ol.linenums {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
132
www/doc/styles/prettify-tomorrow.css
Normal file
132
www/doc/styles/prettify-tomorrow.css
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/* Tomorrow Theme */
|
||||
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
|
||||
/* Pretty printing styles. Used with prettify.js. */
|
||||
/* SPAN elements with the classes below are added by prettyprint. */
|
||||
/* plain text */
|
||||
.pln {
|
||||
color: #4d4d4c; }
|
||||
|
||||
@media screen {
|
||||
/* string content */
|
||||
.str {
|
||||
color: #718c00; }
|
||||
|
||||
/* a keyword */
|
||||
.kwd {
|
||||
color: #8959a8; }
|
||||
|
||||
/* a comment */
|
||||
.com {
|
||||
color: #8e908c; }
|
||||
|
||||
/* a type name */
|
||||
.typ {
|
||||
color: #4271ae; }
|
||||
|
||||
/* a literal value */
|
||||
.lit {
|
||||
color: #f5871f; }
|
||||
|
||||
/* punctuation */
|
||||
.pun {
|
||||
color: #4d4d4c; }
|
||||
|
||||
/* lisp open bracket */
|
||||
.opn {
|
||||
color: #4d4d4c; }
|
||||
|
||||
/* lisp close bracket */
|
||||
.clo {
|
||||
color: #4d4d4c; }
|
||||
|
||||
/* a markup tag name */
|
||||
.tag {
|
||||
color: #c82829; }
|
||||
|
||||
/* a markup attribute name */
|
||||
.atn {
|
||||
color: #f5871f; }
|
||||
|
||||
/* a markup attribute value */
|
||||
.atv {
|
||||
color: #3e999f; }
|
||||
|
||||
/* a declaration */
|
||||
.dec {
|
||||
color: #f5871f; }
|
||||
|
||||
/* a variable name */
|
||||
.var {
|
||||
color: #c82829; }
|
||||
|
||||
/* a function name */
|
||||
.fun {
|
||||
color: #4271ae; } }
|
||||
/* Use higher contrast and text-weight for printable form. */
|
||||
@media print, projection {
|
||||
.str {
|
||||
color: #060; }
|
||||
|
||||
.kwd {
|
||||
color: #006;
|
||||
font-weight: bold; }
|
||||
|
||||
.com {
|
||||
color: #600;
|
||||
font-style: italic; }
|
||||
|
||||
.typ {
|
||||
color: #404;
|
||||
font-weight: bold; }
|
||||
|
||||
.lit {
|
||||
color: #044; }
|
||||
|
||||
.pun, .opn, .clo {
|
||||
color: #440; }
|
||||
|
||||
.tag {
|
||||
color: #006;
|
||||
font-weight: bold; }
|
||||
|
||||
.atn {
|
||||
color: #404; }
|
||||
|
||||
.atv {
|
||||
color: #060; } }
|
||||
/* Style */
|
||||
/*
|
||||
pre.prettyprint {
|
||||
background: white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px; }
|
||||
*/
|
||||
|
||||
/* Specify class=linenums on a pre to get line numbering */
|
||||
ol.linenums {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0; }
|
||||
|
||||
/* IE indents via margin-left */
|
||||
li.L0,
|
||||
li.L1,
|
||||
li.L2,
|
||||
li.L3,
|
||||
li.L4,
|
||||
li.L5,
|
||||
li.L6,
|
||||
li.L7,
|
||||
li.L8,
|
||||
li.L9 {
|
||||
/* */ }
|
||||
|
||||
/* Alternate shading for lines */
|
||||
li.L1,
|
||||
li.L3,
|
||||
li.L5,
|
||||
li.L7,
|
||||
li.L9 {
|
||||
/* */ }
|
||||
127
www/doc/utils_altchaUtils.js.html
Normal file
127
www/doc/utils_altchaUtils.js.html
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: utils/altchaUtils.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: utils/altchaUtils.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//Config
|
||||
const config = require('../../config.json');
|
||||
|
||||
//NPM imports
|
||||
const { createChallenge, verifySolution } = require('altcha-lib');
|
||||
|
||||
/**
|
||||
* Create empty array to hold cache of spent payloads to protect against replay attacks
|
||||
*/
|
||||
const spent = [];
|
||||
/**
|
||||
* Captcha lifetime in minutes
|
||||
*/
|
||||
const lifetime = 2;
|
||||
|
||||
/**
|
||||
* Generates captcha challenges to send down to the browser
|
||||
* @param {Number} difficulty - Challange Difficulty (x100K internally)
|
||||
* @param {String} uniqueSecret - Secret to salt the challange hash with
|
||||
* @returns {String} Altcha Challenge hash
|
||||
*/
|
||||
module.exports.genCaptcha = async function(difficulty = 2, uniqueSecret = ''){
|
||||
//Set altcha expiration date
|
||||
const expiration = new Date();
|
||||
|
||||
//Add four minutes
|
||||
expiration.setMinutes(expiration.getMinutes() + lifetime);
|
||||
|
||||
//Generate Altcha Challenge
|
||||
return await createChallenge({
|
||||
hmacKey: [config.altchaSecret, uniqueSecret].join(''),
|
||||
maxNumber: 100000 * difficulty,
|
||||
expires: expiration
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies completed altcha challenges handed over from the user
|
||||
* @param {String} payload - Completed Altcha Payload
|
||||
* @param {String} uniqueSecret - Server-side Unique Secret to verify payload came from server-generated challenge
|
||||
* @returns {boolean} True if payload is a valid and unique altcha challenge which originated from this server
|
||||
*/
|
||||
module.exports.verify = async function(payload, uniqueSecret = ''){
|
||||
//If this payload is already spent
|
||||
if(spent.indexOf(payload) != -1){
|
||||
//Fuck off and die
|
||||
return false;
|
||||
}
|
||||
|
||||
//Get length before pushing payload to get index of next item
|
||||
const payloadIndex = spent.length;
|
||||
|
||||
//Add payload to cache of spent payloades
|
||||
spent.push(payload);
|
||||
|
||||
//Set timeout to splice out the used payload after its expired so we're not filling RAM with expired payloads that aren't going to resolve true anyways
|
||||
setTimeout(() => {spent.splice(payloadIndex,1);}, lifetime * 60 * 1000);
|
||||
|
||||
//Return verification results
|
||||
return await verifySolution(payload, [config.altchaSecret, uniqueSecret].join(''));
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
117
www/doc/utils_configCheck.js.html
Normal file
117
www/doc/utils_configCheck.js.html
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: utils/configCheck.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: utils/configCheck.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//Config
|
||||
const config = require('../../config.json');
|
||||
|
||||
//Local
|
||||
const loggerUtil = require('./loggerUtils');
|
||||
|
||||
//NPM Imports
|
||||
const validator = require('validator');//We need validators for express-less code too!
|
||||
|
||||
/**
|
||||
* Basic security check which runs on startup.
|
||||
* Warns server admin against unsafe config options.
|
||||
*/
|
||||
module.exports.securityCheck = function(){
|
||||
//Check Protocol
|
||||
if(config.protocol.toLowerCase() != 'https'){
|
||||
//If it's insecure then warn the admin
|
||||
loggerUtil.consoleWarn("Starting in HTTP mode. This server should be used for development purposes only!");
|
||||
}
|
||||
|
||||
//Check mail protocol
|
||||
if(!config.mail.secure){
|
||||
//If it's insecure then warn the admin
|
||||
loggerUtil.consoleWarn("Mail transport security disabled! This server should be used for development purposes only!");
|
||||
}
|
||||
|
||||
//check session secret
|
||||
if(!validator.isStrongPassword(config.sessionSecret) || config.sessionSecret == "CHANGE_ME"){
|
||||
loggerUtil.consoleWarn("Insecure Session Secret! Change Session Secret!");
|
||||
}
|
||||
|
||||
//check altcha secret
|
||||
if(!validator.isStrongPassword(config.altchaSecret) || config.altchaSecret == "CHANGE_ME"){
|
||||
loggerUtil.consoleWarn("Insecure Altcha Secret! Change Altcha Secret!");
|
||||
}
|
||||
|
||||
//check ipHash secret
|
||||
if(!validator.isStrongPassword(config.ipSecret) || config.ipSecret == "CHANGE_ME"){
|
||||
loggerUtil.consoleWarn("Insecure IP Hashing Secret! Change IP Hashing Secret!");
|
||||
}
|
||||
|
||||
//check DB pass
|
||||
if(!validator.isStrongPassword(config.db.pass) || config.db.pass == "CHANGE_ME" || config.db.pass == config.db.user){
|
||||
loggerUtil.consoleWarn("Insecure Database Password! Change Database password!");
|
||||
}
|
||||
|
||||
//check email pass
|
||||
if(!validator.isStrongPassword(config.mail.pass) || config.mail.pass == "CHANGE_ME"){
|
||||
loggerUtil.consoleWarn("Insecure Email Password! Change Email password!");
|
||||
}
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
112
www/doc/utils_hashUtils.js.html
Normal file
112
www/doc/utils_hashUtils.js.html
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: utils/hashUtils.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: utils/hashUtils.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//Config
|
||||
const config = require('../../config.json');
|
||||
|
||||
//Node Imports
|
||||
const crypto = require('node:crypto');
|
||||
|
||||
//NPM Imports
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
/**
|
||||
* Sitewide function for hashing passwords
|
||||
* @param {String} pass - Password to hash
|
||||
* @returns {String} Hashed/Salted password
|
||||
*/
|
||||
module.exports.hashPassword = function(pass){
|
||||
const salt = bcrypt.genSaltSync();
|
||||
return bcrypt.hashSync(pass, salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sitewide password for authenticating/comparing passwords agianst hashes
|
||||
* @param {String} pass - Plaintext Password
|
||||
* @param {String} hash - Salty Hash
|
||||
* @returns {Boolean} True if authentication success
|
||||
*/
|
||||
module.exports.comparePassword = function(pass, hash){
|
||||
return bcrypt.compareSync(pass, hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Site-wide IP hashing/salting function
|
||||
*
|
||||
* Provides a basic level of privacy by only logging salted hashes of IP's
|
||||
* @param {String} ip - IP to hash
|
||||
* @returns {String} Hashed/Salted IP Adress
|
||||
*/
|
||||
module.exports.hashIP = function(ip){
|
||||
//Create hash object
|
||||
const hashObj = crypto.createHash('md5');
|
||||
|
||||
//add IP and salt to the hash
|
||||
hashObj.update(`${ip}${config.ipSecret}`);
|
||||
|
||||
//return the IP hash as a string
|
||||
return hashObj.digest('hex');
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
155
www/doc/utils_linkUtils.js.html
Normal file
155
www/doc/utils_linkUtils.js.html
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: utils/linkUtils.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: utils/linkUtils.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const validator = require('validator');//No express here, so regular validator it is!
|
||||
|
||||
//Create link cache
|
||||
/**
|
||||
* Basic RAM-Based cache of links, so we don't have to re-pull things after we get them
|
||||
*/
|
||||
module.exports.cache = new Map();
|
||||
|
||||
/**
|
||||
* Validates links and returns a marked link object that can be returned to the client to format/embed accordingly
|
||||
* @param {String} link - URL to Validate
|
||||
* @returns {Object} Marked link object
|
||||
*/
|
||||
module.exports.markLink = async function(link){
|
||||
//Check link cache for the requested link
|
||||
const cachedLink = module.exports.cache.get(link);
|
||||
|
||||
//If we have a cached result
|
||||
if(cachedLink){
|
||||
//return the cached link
|
||||
return cachedLink;
|
||||
}
|
||||
|
||||
//Set max file size to 4MB
|
||||
const maxSize = 4000000;
|
||||
//Assume links are guilty until proven innocent
|
||||
var type = "malformedLink"
|
||||
|
||||
//Make sure we have an actual, factual URL
|
||||
if(validator.isURL(link)){
|
||||
//The URL is valid, so this is at least a dead link
|
||||
type = 'deadLink';
|
||||
|
||||
//Don't try this at home, we're what you call "Experts"
|
||||
//TODO: Handle this shit simultaneously and send the chat before its done, then send updated types for each link as they're pulled individually
|
||||
try{
|
||||
//Pull content type
|
||||
var response = await fetch(link,{
|
||||
method: "HEAD",
|
||||
});
|
||||
|
||||
//If we made it this far then the link is, at the very least, not dead.
|
||||
type = 'link'
|
||||
|
||||
//Get file type from header
|
||||
const fileType = response.headers.get('content-type');
|
||||
const fileSize = response.headers.get('content-length');
|
||||
|
||||
//If they're reporting file types
|
||||
if(fileType != null){
|
||||
//If we have an image
|
||||
if(fileType.match('image/')){
|
||||
//If the file size is unreported OR it's smaller than 4MB (not all servers report this and images that big are pretty rare)
|
||||
if(fileSize == null || fileSize <= maxSize){
|
||||
//Mark link as an image
|
||||
type = 'image';
|
||||
}
|
||||
//If it's a video
|
||||
}else if(fileType.match('video/mp4' || 'video/webm')){
|
||||
//If the server is reporting file-size and it's reporting under 4MB (Reject unreported sizes to be on the safe side is video is huge)
|
||||
if(fileSize != null && fileSize <= maxSize){
|
||||
//mark link as a video
|
||||
type = 'video';
|
||||
}
|
||||
}
|
||||
}
|
||||
//Probably bad form but if something happens in here I'm blaming whoever hosted the link
|
||||
//maybe don't host a fucked up server and I wouldn't handle with an empty catch
|
||||
}catch{};
|
||||
}
|
||||
|
||||
//Create the link object from processed information
|
||||
const linkObj = {
|
||||
link,
|
||||
type
|
||||
}
|
||||
|
||||
//Cache the result
|
||||
module.exports.cache.set(link, linkObj);
|
||||
|
||||
//Set timer to remove cache entry in five minutes
|
||||
setTimeout(()=>{
|
||||
module.exports.cache.delete(link);
|
||||
}, 300000)
|
||||
|
||||
//return the link
|
||||
return linkObj;
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
216
www/doc/utils_loggerUtils.js.html
Normal file
216
www/doc/utils_loggerUtils.js.html
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: utils/loggerUtils.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: utils/loggerUtils.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//Config
|
||||
const config = require('../../config.json');
|
||||
|
||||
/**
|
||||
* Creates and returns a custom exception, tagged as a 'custom' exception, using the 'custom' boolean property.
|
||||
* This is used to denote that this error was generated on purpose, with a human readable message, that can be securely sent to the client.
|
||||
* Unexpected exceptions should only be logged internally, however, as they may contain sensitive data.
|
||||
*
|
||||
* @param {String} msg - Error message to send the client
|
||||
* @param {String} type - Error type to send back to the client
|
||||
* @returns {Error} The exception to smith
|
||||
*/
|
||||
module.exports.exceptionSmith = function(msg, type){
|
||||
//Create the new error with the given message
|
||||
const exception = new Error(msg);
|
||||
|
||||
//Set the error type
|
||||
exception.type = type;
|
||||
|
||||
//Mark the error as a custom error
|
||||
exception.custom = true;
|
||||
|
||||
//Return the error
|
||||
return exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main error handling function
|
||||
* @param {Express.Response} res - Response being sent out to the client who caused the issue
|
||||
* @param {String} msg - Error message to send the client
|
||||
* @param {String} type - Error type to send back to the client
|
||||
* @param {Number} status - HTTP(s) Status Code to send back to the client
|
||||
* @returns {Express.Response} If we have a usable Express Response object, return it back after it's been cashed
|
||||
*/
|
||||
module.exports.errorHandler = function(res, msg, type = "Generic", status = 400){
|
||||
//Some controllers do things after sending headers, for those, we should remain silent
|
||||
if(!res.headersSent){
|
||||
res.status(status);
|
||||
return res.send({errors: [{type, msg, date: new Date()}]});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles local exceptions which where not directly created by user interaction
|
||||
* @param {Error} err - Exception to handle
|
||||
*/
|
||||
module.exports.localExceptionHandler = function(err){
|
||||
//If we're being verbose
|
||||
if(config.verbose){
|
||||
//Log the error
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles exceptions which where directly the fault of user action >:(
|
||||
* @param {Express.Response} res - Express Response object to bitch at
|
||||
* @param {Error} err - Error created by the jerk in question
|
||||
*/
|
||||
module.exports.exceptionHandler = function(res, err){
|
||||
//If this is a self-made problem
|
||||
if(err.custom){
|
||||
module.exports.errorHandler(res, err.message, err.type);
|
||||
}else{
|
||||
//Locally handle the exception
|
||||
module.exports.localExceptionHandler(err);
|
||||
|
||||
//if not yell at the browser for fucking up, and tell it what it did wrong.
|
||||
module.exports.errorHandler(res, "An unexpected server crash was just prevented. You should probably report this to an admin.", "Caught Exception");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic error-handling for socket.io so we don't just silently swallow errors.
|
||||
* @param {Socket} socket - Socket error originated from
|
||||
* @param {String} msg - Error message to send the client
|
||||
* @param {String} type - Error type to send back to the client
|
||||
* @returns {Boolean} - Passthrough from socket.emit
|
||||
*/
|
||||
module.exports.socketErrorHandler = function(socket, msg, type = "Generic"){
|
||||
return socket.emit("error", {errors: [{type, msg, date: new Date()}]});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates error messages for simple errors generated by socket.io interaction
|
||||
* @param {Socket} socket - Socket error originated from
|
||||
* @param {Error} err - Error created by the jerk in question
|
||||
* @returns {Boolean} - Passthrough from socket.emit
|
||||
*/
|
||||
module.exports.socketExceptionHandler = function(socket, err){
|
||||
//If this is a self made problem
|
||||
if(err.custom){
|
||||
//yell at the browser for fucking up, and tell it what it did wrong.
|
||||
return module.exports.socketErrorHandler(socket, err.message, err.type);
|
||||
}else{
|
||||
//Locally handle the exception
|
||||
module.exports.localExceptionHandler(err);
|
||||
|
||||
//if not yell at the browser for fucking up
|
||||
return module.exports.socketErrorHandler(socket, "An unexpected server crash was just prevented. You should probably report this to an admin.", "Server");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates error messages and drops connection for critical errors caused by socket.io interaction
|
||||
* @param {Socket} socket - Socket error originated from
|
||||
* @param {Error} err - Error created by the jerk in question
|
||||
* @returns {Boolean} - Passthrough from socket.disconnect
|
||||
*/
|
||||
module.exports.socketCriticalExceptionHandler = function(socket, err){
|
||||
//If this is a self made problem
|
||||
if(err.custom){
|
||||
//yell at the browser for fucking up, and tell it what it did wrong.
|
||||
socket.emit("kick", {type: "Disconnected", reason: `Server Error: ${err.message}`});
|
||||
}else{
|
||||
//Locally handle the exception
|
||||
module.exports.localExceptionHandler(err);
|
||||
|
||||
//yell at the browser for fucking up
|
||||
socket.emit("kick", {type: "Disconnected", reason: "An unexpected server crash was just prevented. You should probably report this to an admin."});
|
||||
}
|
||||
return socket.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints warning text to server console
|
||||
* @param {String} string - String to print to console
|
||||
*/
|
||||
module.exports.consoleWarn = function(string){
|
||||
console.warn('\x1b[31m\x1b[4m%s\x1b[0m',string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic error-handling middleware to ensure we're not dumping stack traces to the client, as that would be insecure
|
||||
* @param {Error} err - Error to handle
|
||||
* @param {Express.Request} req - Express Request
|
||||
* @param {Express.Response} res - Express Response
|
||||
* @param {Function} next - Next function in the Express middleware chain (Not that it's getting called XP)
|
||||
*/
|
||||
module.exports.errorMiddleware = function(err, req, res, next){
|
||||
//Set generic error
|
||||
var reason = "Server Error";
|
||||
|
||||
//If it's un-authorized
|
||||
if(err.status == 403){
|
||||
reason = "Unauthorized"
|
||||
}
|
||||
|
||||
module.exports.errorHandler(res, err.message, reason, err.status);
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
149
www/doc/utils_mailUtils.js.html
Normal file
149
www/doc/utils_mailUtils.js.html
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: utils/mailUtils.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: utils/mailUtils.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//Config
|
||||
const config = require('../../config.json');
|
||||
|
||||
//NPM imports
|
||||
const nodeMailer = require("nodemailer");
|
||||
|
||||
//Setup mail transport
|
||||
/**
|
||||
* nodemailer transport object, generated from options specific in our config file
|
||||
*/
|
||||
const transporter = nodeMailer.createTransport({
|
||||
host: config.mail.host,
|
||||
port: config.mail.port,
|
||||
secure: config.mail.secure,
|
||||
auth: {
|
||||
user: config.mail.address,
|
||||
pass: config.mail.pass
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Sends an email as tokebot to the requested user w/ the requested body and signature
|
||||
* @param {String} to - String containing the email address to send to
|
||||
* @param {String} subject - Subject line of the email to send
|
||||
* @param {String} body - Body contents, either HTML or Plaintext
|
||||
* @param {Boolean} htmlBody - Whether or not Body contents should be sent as HTML or Plaintext
|
||||
* @returns {Object} Sent mail info
|
||||
*/
|
||||
module.exports.mailem = async function(to, subject, body, htmlBody = false){
|
||||
//Create mail object
|
||||
const mailObj = {
|
||||
from: `"Tokebot🤖💨"<${config.mail.address}>`,
|
||||
to,
|
||||
subject
|
||||
};
|
||||
|
||||
//If we're sending HTML
|
||||
if(htmlBody){
|
||||
//set body as html
|
||||
mailObj.html = body;
|
||||
//If we're sending plaintext
|
||||
}else{
|
||||
//Set body as plaintext
|
||||
mailObj.text = body
|
||||
}
|
||||
|
||||
//Send mail based on mail object
|
||||
const sentMail = await transporter.sendMail(mailObj);
|
||||
|
||||
//return the mail info
|
||||
return sentMail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends address verification email
|
||||
* @param {Mongoose.Document} requestDB - DB Document Object for the current email change request token
|
||||
* @param {Mongoose.Document} userDB - DB Document Object for the user we're verifying email against
|
||||
* @param {String} newEmail - New email address to send to
|
||||
*/
|
||||
module.exports.sendAddressVerification = async function(requestDB, userDB, newEmail){
|
||||
//Send the reset url via email
|
||||
await module.exports.mailem(
|
||||
newEmail,
|
||||
`Email Change Request - ${userDB.user}`,
|
||||
`<h1>Email Change Request</h1>
|
||||
<p>A request to change the email associated with the ${config.instanceName} account '${userDB.user}' to this address has been requested.<br>
|
||||
<a href="${requestDB.getChangeURL()}">Click here</a> to confirm this change.</p>
|
||||
<sup>If you received this email without request, feel free to ignore and delete it! -Tokebot</sup>`,
|
||||
true
|
||||
);
|
||||
|
||||
//If the user has a pre-existing email address
|
||||
if(userDB.email != null && userDB.email != ""){
|
||||
await module.exports.mailem(
|
||||
userDB.email,
|
||||
`Email Change Request - ${userDB.user}`,
|
||||
`<h1>Email Change Request Notification</h1>
|
||||
<p>A request to change the email associated with the ${config.instanceName} account '${userDB.user}' to another address has been requested.<br>
|
||||
<sup>If you received this email without request, you should <strong>immediately</strong> change your password and contact the server adminsitrator! -Tokebot</sup>`,
|
||||
true
|
||||
);
|
||||
}
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
163
www/doc/utils_media_internetArchiveUtils.js.html
Normal file
163
www/doc/utils_media_internetArchiveUtils.js.html
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: utils/media/internetArchiveUtils.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: utils/media/internetArchiveUtils.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//Node Imports
|
||||
const validator = require('validator');
|
||||
|
||||
//Local Imports
|
||||
const media = require('../../app/channel/media/media.js');
|
||||
const regexUtils = require('../regexUtils.js');
|
||||
const loggerUtils = require('../loggerUtils.js')
|
||||
|
||||
/**
|
||||
* Pulls metadate for a given archive.org item
|
||||
* @param {String} fullID - Full path of the requested upload
|
||||
* @param {String} title - Title to add to media object
|
||||
* @returns {Array} Generated list of media objects from given upload path
|
||||
*/
|
||||
module.exports.fetchMetadata = async function(fullID, title){
|
||||
//Split fullID by first slash
|
||||
const [itemID, requestedPath] = decodeURIComponent(fullID).split(/\/(.*)/);
|
||||
//Create empty list to hold media objects
|
||||
const mediaList = [];
|
||||
//Create empty variable to hold return data object
|
||||
let data;
|
||||
|
||||
//Create metadata link from itemID
|
||||
const metadataLink = `https://archive.org/metadata/${itemID}`;
|
||||
|
||||
//Fetch item metadata from the internet archive
|
||||
const response = await fetch(metadataLink,
|
||||
{
|
||||
method: "GET"
|
||||
}
|
||||
);
|
||||
|
||||
//If we hit a snag
|
||||
if(!response.ok){
|
||||
//Scream and shout
|
||||
const errorBody = await response.text();
|
||||
throw loggerUtils.exceptionSmith(`Internet Archive Error '${response.status}': ${errorBody}`, "queue");
|
||||
}
|
||||
|
||||
//Collect our metadata
|
||||
const rawMetadata = await response.json();
|
||||
|
||||
//Filter out any in-compatible files
|
||||
const compatibleFiles = rawMetadata.files.filter(compatibilityFilter);
|
||||
|
||||
|
||||
//If we're requesting an empty path
|
||||
if(requestedPath == '' || requestedPath == null){
|
||||
//Return item metadata and compatible files
|
||||
data = {
|
||||
files: compatibleFiles,
|
||||
metadata: rawMetadata.metadata
|
||||
}
|
||||
//Other wise
|
||||
}else{
|
||||
//Return item metadata and matching compatible files
|
||||
data = {
|
||||
//Filter files out that don't match requested path and return remaining list
|
||||
files: compatibleFiles.filter(pathFilter),
|
||||
metadata: rawMetadata.metadata
|
||||
}
|
||||
}
|
||||
|
||||
//for every compatible and relevant file returned from IA
|
||||
for(let file of data.files){
|
||||
//Split file path by directories
|
||||
const path = file.name.split('/');
|
||||
|
||||
//pull filename from path and escape in-case someone put something nasty in there
|
||||
const name = validator.escape(validator.trim(path[path.length - 1]));
|
||||
|
||||
//Construct link from pulled info
|
||||
const link = `https://archive.org/download/${data.metadata.identifier}/${file.name}`;
|
||||
|
||||
//if we where handed a null title
|
||||
if(title == null || title == ''){
|
||||
//Create new media object from file info substituting filename for title
|
||||
mediaList.push(new media(name, name, link, link, 'ia', Number(file.length)));
|
||||
}else{
|
||||
//Create new media object from file info
|
||||
mediaList.push(new media(title, name, link, link, 'ia', Number(file.length)));
|
||||
}
|
||||
}
|
||||
|
||||
//return media object list
|
||||
return mediaList;
|
||||
|
||||
function compatibilityFilter(file){
|
||||
//return true for all files that match for web-safe formats
|
||||
return file.format == "h.264 IA" || file.format == "h.264" || file.format == "Ogg Video" || file.format.match("MPEG4");
|
||||
}
|
||||
|
||||
function pathFilter(file){
|
||||
//return true for all file names which match the given requested file path
|
||||
return file.name.match(`^${regexUtils.escapeRegex(requestedPath)}`);
|
||||
}
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
202
www/doc/utils_media_yanker.js.html
Normal file
202
www/doc/utils_media_yanker.js.html
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: utils/media/yanker.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: utils/media/yanker.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
//const url = require("node:url");
|
||||
const validator = require('validator');//No express here, so regular validator it is!
|
||||
|
||||
//local import
|
||||
const iaUtil = require('./internetArchiveUtils');
|
||||
const ytdlpUtil = require('./ytdlpUtils');
|
||||
|
||||
/**
|
||||
* Checks a given URL and runs the proper metadata fetching function to create a media object from any supported URL
|
||||
* @param {String} url - URL to yank media against
|
||||
* @param {String} title - Title to apply to yanked media
|
||||
* @returns {Array} Returns list of yanked media objects on success
|
||||
*/
|
||||
module.exports.yankMedia = async function(url, title){
|
||||
//Get pull type
|
||||
const pullType = await this.getMediaType(url);
|
||||
|
||||
//Check pull type
|
||||
switch(pullType.type){
|
||||
case "ia":
|
||||
//return media object list from IA module
|
||||
return await iaUtil.fetchMetadata(pullType.id, title);
|
||||
case "yt":
|
||||
//return media object list from the YT-DLP module's youtube function
|
||||
return await ytdlpUtil.fetchYoutubeMetadata(pullType.id, title);
|
||||
case "ytp":
|
||||
//return media object list from YT-DLP module's youtube playlist function
|
||||
//return await ytdlpUtil.fetchYoutubePlaylistMetadata(pullType.id, title);
|
||||
//Holding off on this since YT-DLP takes 10 years to do a playlist as it needs to pull each and every video one-by-one
|
||||
//Maybe in the future a piped alternative might be in order, however this would most likely require us to host our own local instance.
|
||||
//Though it could give us added resistance against youtube/google's rolling IP bans
|
||||
return null;
|
||||
case "dm":
|
||||
//return mediao object list from the YT-DLP module's dailymotion function
|
||||
return await ytdlpUtil.fetchDailymotionMetadata(pullType.id, title);
|
||||
default:
|
||||
//return null to signify a bad url
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes raw links on relevant media objects
|
||||
*
|
||||
* Useful for sources like youtube, who only provide expiring raw links
|
||||
* @param {ScheduledMedia} mediaObj - Media Object to refresh
|
||||
* @returns {ScheduledMedia} Refreshed media object
|
||||
*/
|
||||
module.exports.refreshRawLink = async function(mediaObj){
|
||||
switch(mediaObj.type){
|
||||
case 'yt':
|
||||
//Scrape expiration from query strings
|
||||
const expires = mediaObj.rawLink.match(/expire=([0-9]+)/);
|
||||
//Went with regex for speed, but I figure I'd keep this around in case we want the accuracy of a battle-tested implementation
|
||||
//const expires = new URL(mediaObj.rawLink).searchParams.get("expire");
|
||||
|
||||
//If we have a valid raw file link that will be good by the end of the video
|
||||
if(expires != null && (expires * 1000) > mediaObj.getEndTime()){
|
||||
//Return null to tell the calling function there is no refresh required for this video at this time
|
||||
return null;
|
||||
}
|
||||
|
||||
//Re-fetch media metadata
|
||||
metadata = await ytdlpUtil.fetchYoutubeMetadata(mediaObj.id);
|
||||
//Refresh media rawlink from metadata
|
||||
mediaObj.rawLink = metadata[0].rawLink;
|
||||
|
||||
//return media object
|
||||
return mediaObj;
|
||||
}
|
||||
|
||||
//Return null to tell the calling function there is no refresh required for this media type
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Detects media type by URL
|
||||
*
|
||||
* I'd be lying if this didn't take at least some inspiration/regex patterns from extractQueryParam() in cytube/forest's browser-side 'util.js'
|
||||
* Still this has some improvements like url pre-checks and the fact that it's handled serverside, recuing possibility of bad requests.
|
||||
* Some of the regex expressions for certain services have also been improved, such as youtube, and the fore.st-unique archive.org
|
||||
*
|
||||
* @param {String} url - URL to determine media type of
|
||||
* @returns {Object} containing URL type and clipped ID string
|
||||
*/
|
||||
module.exports.getMediaType = async function(url){
|
||||
//Check if we have a valid url, encode it on the fly in case it's too humie-friendly
|
||||
if(!validator.isURL(encodeURI(url))){
|
||||
//If not toss the fucker out
|
||||
return {
|
||||
type: null,
|
||||
id: null
|
||||
}
|
||||
}
|
||||
|
||||
//If we have link to a resource from archive.org
|
||||
if(match = url.match(/archive\.org\/(?:details|download)\/([a-zA-Z0-9\/._-\s\%]+)/)){
|
||||
//return internet archive upload id and filepath
|
||||
return {
|
||||
type: "ia",
|
||||
id: match[1]
|
||||
}
|
||||
}
|
||||
|
||||
//If we have a match to a youtube video
|
||||
if((match = url.match(/youtube\.com\/watch\?v=([a-zA-Z0-9_-]{11})/)) || (match = url.match(/youtu\.be\/([a-zA-Z0-9_-]{11})/))){
|
||||
//return youtube video id
|
||||
return {
|
||||
type: "yt",
|
||||
id: match[1]
|
||||
}
|
||||
}
|
||||
|
||||
//If we have a match to a youtube playlist
|
||||
if((match = url.match(/youtube\.com\/playlist\?list=([a-zA-Z0-9_-]{34})/)) || (match = url.match(/youtu\.be\/playlist\?list=([a-zA-Z0-9_-]{34})/))){
|
||||
//return youtube playlist id
|
||||
return {
|
||||
type: "ytp",
|
||||
id: match[1]
|
||||
}
|
||||
}
|
||||
|
||||
//If we have a match to a dailymotion video
|
||||
if(match = url.match(/dailymotion\.com\/video\/([a-zA-Z0-9]+)/)){
|
||||
return {
|
||||
type: "dm",
|
||||
id: match[1]
|
||||
}
|
||||
}
|
||||
|
||||
//If we fell through all of our media types without a match
|
||||
return{
|
||||
type: null,
|
||||
id: null
|
||||
}
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
195
www/doc/utils_media_ytdlpUtils.js.html
Normal file
195
www/doc/utils_media_ytdlpUtils.js.html
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: utils/media/ytdlpUtils.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: utils/media/ytdlpUtils.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//Config
|
||||
const config = require('../../../config.json');
|
||||
|
||||
//Node Imports
|
||||
const { create: ytdlpMaker } = require('youtube-dl-exec');
|
||||
//Import ytdlp w/ custom path from config so we can force the newest build of yt-dlp from pip
|
||||
const ytdlp = ytdlpMaker(config.ytdlpPath);
|
||||
const url = require("node:url");
|
||||
const validator = require('validator');
|
||||
|
||||
//Local Imports
|
||||
const media = require('../../app/channel/media/media.js');
|
||||
const regexUtils = require('../regexUtils.js');
|
||||
const loggerUtils = require('../loggerUtils.js')
|
||||
|
||||
/**
|
||||
* Pulls metadata for a single youtube video via YT-DLP
|
||||
* @param {String} id - Youtube Video ID
|
||||
* @param {String} title - Title to add to the given media object
|
||||
* @returns {Media} Media object containing relevant metadata
|
||||
*/
|
||||
module.exports.fetchYoutubeMetadata = async function(id, title){
|
||||
try{
|
||||
//Try to pull media from youtube id
|
||||
const media = await fetchVideoMetadata(`https://youtu.be/${id}`, title, 'yt');
|
||||
|
||||
//Return found media
|
||||
return media;
|
||||
//If something went wrong
|
||||
}catch(err){
|
||||
//If our IP was banned by youtube
|
||||
if(err.message.match("Sign in to confirm you’re not a bot.")){
|
||||
//Make our own error with blackjack and hookers
|
||||
throw loggerUtils.exceptionSmith("The server's IP address has been banned by youtube. Please contact your server's administrator.", "queue");
|
||||
//Otherwise if we don't have a good way to handle it
|
||||
}else{
|
||||
//toss it back up
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls metadata for a playlist of youtube videos via YT-DLP
|
||||
* @param {String} id - Youtube Playlist ID
|
||||
* @param {String} title - Title to add to the given media objects
|
||||
* @returns {Array} Array of Media objects containing relevant metadata
|
||||
*/
|
||||
module.exports.fetchYoutubePlaylistMetadata = async function(id, title){
|
||||
try{
|
||||
//Try to pull media from youtube id
|
||||
const media = await fetchPlaylistMetadata(`https://youtu.be/playlist?list=${id}`, title, 'yt');
|
||||
|
||||
//Return found media
|
||||
return media;
|
||||
//If something went wrong
|
||||
}catch(err){
|
||||
//If our IP was banned by youtube
|
||||
if(err.message.match("Sign in to confirm you’re not a bot.")){
|
||||
//Make our own error with blackjack and hookers
|
||||
throw loggerUtils.exceptionSmith("The server's IP address has been banned by youtube. Please contact your server's administrator.", "queue");
|
||||
//Otherwise if we don't have a good way to handle it
|
||||
}else{
|
||||
//toss it back up
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This requires HLS embeds which, in-turn, require daily motion to add us to their CORS exception list
|
||||
* Not gonna happen, so we need to use their API for this, or proxy the video
|
||||
module.exports.fetchDailymotionMetadata = async function(id, title){
|
||||
//Pull media from dailymotion link
|
||||
const media = await fetchVideoMetadata(`https://dailymotion.com/video/${id}`, title, 'dm');
|
||||
|
||||
//Return found media;
|
||||
return media;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Generic single video YTDLP function meant to be used by service-sepecific fetchers which will then be used to fetch video metadata
|
||||
* @param {String} link - Link to video in question
|
||||
* @param {String} title - Title to add to the given media objects
|
||||
* @param {String} type - Link type to attach to the resulting media object
|
||||
* @returns {Array} Array of Media objects containing relevant metadata
|
||||
*/
|
||||
async function fetchVideoMetadata(link, title, type, format = 'b'){
|
||||
//Create media list
|
||||
const mediaList = [];
|
||||
|
||||
//Pull raw metadata from YT-DLP
|
||||
const rawMetadata = await ytdlpFetch(link, format);
|
||||
|
||||
//Pull data from rawMetadata, sanatizing title to prevent XSS
|
||||
const name = validator.escape(validator.trim(rawMetadata.title));
|
||||
const rawLink = rawMetadata.requested_downloads[0].url;
|
||||
const id = rawMetadata.id;
|
||||
|
||||
//if we where handed a null title
|
||||
if(title == null || title == ''){
|
||||
//Create new media object from file info substituting filename for title
|
||||
mediaList.push(new media(name, name, link, id, type, Number(rawMetadata.duration), rawLink));
|
||||
}else{
|
||||
//Create new media object from file info
|
||||
mediaList.push(new media(title, name, link, id, type, Number(rawMetadata.duration), rawLink));
|
||||
}
|
||||
|
||||
//Return list of media
|
||||
return mediaList;
|
||||
}
|
||||
|
||||
//YT-DLP takes forever to handle playlists, we'll handle this via piped in the future perhaps
|
||||
/*async function fetchPlaylistMetadata(link, title, type, format = 'b'){
|
||||
}*/
|
||||
|
||||
//Wrapper function for YT-DLP NPM package with pre-set cli-flags
|
||||
/**
|
||||
* Basic async YT-DLP Fetch wrapper, ensuring config
|
||||
* @param {String} link - Link to fetch using YT-DLP
|
||||
* @param {String} format - Format string to hand YT-DLP, defaults to 'b'
|
||||
* @returns {Object} Metadata dump from YT-DLP
|
||||
*/
|
||||
async function ytdlpFetch(link, format = 'b'){
|
||||
//return promise from ytdlp
|
||||
return ytdlp(link, {
|
||||
dumpSingleJson: true,
|
||||
format
|
||||
});
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
78
www/doc/utils_regexUtils.js.html
Normal file
78
www/doc/utils_regexUtils.js.html
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: utils/regexUtils.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: utils/regexUtils.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code> /*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
/**
|
||||
* I won't lie this line was whole-sale ganked from stack overflow like a fucking skid
|
||||
* In my defense I only did it because js-runtime-devs are taking fucking eons to implement RegExp.escape()
|
||||
* This should be replaced once that function becomes available in mainline versions of node.js:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/escape
|
||||
*
|
||||
* @param {String} string - Regex string to escape
|
||||
* @returns {String} The Escaped String
|
||||
*/
|
||||
module.exports.escapeRegex = function(string){
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
114
www/doc/utils_scheduler.js.html
Normal file
114
www/doc/utils_scheduler.js.html
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: utils/scheduler.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: utils/scheduler.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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/>.*/
|
||||
|
||||
//NPM Imports
|
||||
const cron = require('node-cron');
|
||||
|
||||
//Local Imports
|
||||
const {userModel} = require('../schemas/user/userSchema');
|
||||
const userBanModel = require('../schemas/user/userBanSchema');
|
||||
const passwordResetModel = require('../schemas/user/passwordResetSchema');
|
||||
const emailChangeModel = require('../schemas/user/emailChangeSchema');
|
||||
const channelModel = require('../schemas/channel/channelSchema');
|
||||
const sessionUtils = require('./sessionUtils');
|
||||
const { email } = require('../validators/accountValidator');
|
||||
|
||||
/**
|
||||
* Schedules all timed jobs accross the server
|
||||
*/
|
||||
module.exports.schedule = function(){
|
||||
//Process hashed IP Records that haven't been recorded in a week or more
|
||||
cron.schedule('0 0 * * *', ()=>{userModel.processAgedIPRecords()},{scheduled: true, timezone: "UTC"});
|
||||
//Process expired global bans every night at midnight
|
||||
cron.schedule('0 0 * * *', ()=>{userBanModel.processExpiredBans()},{scheduled: true, timezone: "UTC"});
|
||||
//Process expired channel bans every night at midnight
|
||||
cron.schedule('0 0 * * *', ()=>{channelModel.processExpiredBans()},{scheduled: true, timezone: "UTC"});
|
||||
//Process expired failed login attempts every night at midnight
|
||||
cron.schedule('0 0 * * *', ()=>{sessionUtils.processExpiredAttempts()},{scheduled: true, timezone: "UTC"});
|
||||
//Process expired password reset requests every night at midnight
|
||||
cron.schedule('0 0 * * *', ()=>{passwordResetModel.processExpiredRequests()},{scheduled: true, timezone: "UTC"});
|
||||
//Process expired email change requests every night at midnight
|
||||
cron.schedule('0 0 * * *', ()=>{emailChangeModel.processExpiredRequests()},{scheduled: true, timezone: "UTC"});
|
||||
}
|
||||
|
||||
/**
|
||||
* Kicks off first run of scheduled functions before scheduling functions for regular callback
|
||||
*/
|
||||
module.exports.kickoff = function(){
|
||||
//Process Hashed IP Records that haven't been recorded in a week or more
|
||||
userModel.processAgedIPRecords();
|
||||
//Process expired global bans that may have expired since last restart
|
||||
userBanModel.processExpiredBans();
|
||||
//Process expired channel bans that may have expired since last restart
|
||||
channelModel.processExpiredBans();
|
||||
//Process expired password reset requests that may have expired since last restart
|
||||
passwordResetModel.processExpiredRequests();
|
||||
//Process expired email change requests that may have expired since last restart
|
||||
emailChangeModel.processExpiredRequests();
|
||||
|
||||
//Schedule jobs
|
||||
module.exports.schedule();
|
||||
}</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
245
www/doc/utils_sessionUtils.js.html
Normal file
245
www/doc/utils_sessionUtils.js.html
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>JSDoc: Source: utils/sessionUtils.js</title>
|
||||
|
||||
<script src="scripts/prettify/prettify.js"> </script>
|
||||
<script src="scripts/prettify/lang-css.js"> </script>
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
||||
<![endif]-->
|
||||
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
||||
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main">
|
||||
|
||||
<h1 class="page-title">Source: utils/sessionUtils.js</h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<section>
|
||||
<article>
|
||||
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
||||
Copyright (C) 2024-2025 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 config = require('../../config.json');
|
||||
const {userModel} = require('../schemas/user/userSchema.js');
|
||||
const userBanModel = require('../schemas/user/userBanSchema.js')
|
||||
const altchaUtils = require('../utils/altchaUtils.js');
|
||||
const loggerUtils = require('../utils/loggerUtils.js');
|
||||
|
||||
/**
|
||||
* Create failed sign-in cache since it's easier and more preformant to implement it this way than adding extra burdon to the database
|
||||
* Server restarts are far and few between. It would take multiple during a single bruteforce attempt for this to become an issue.
|
||||
*/
|
||||
const failedAttempts = new Map();
|
||||
|
||||
/**
|
||||
* How many failed attempts required to throttle with altcha
|
||||
*/
|
||||
const throttleAttempts = 5;
|
||||
|
||||
/**
|
||||
* How many attempts to lock user account out for the day
|
||||
*/
|
||||
const maxAttempts = 200;
|
||||
|
||||
/**
|
||||
* Sole and Singular Session Authentication method.
|
||||
* All logins should happen through here, all other site-wide authentication should happen by sessions authenticated by this model.
|
||||
* This is important, as reducing authentication endpoints reduces attack surface.
|
||||
* @param {String} user - Username to login as
|
||||
* @param {String} pass - Password to authenticat session with
|
||||
* @param {express.Request} req - Express request object w/ session to authenticate
|
||||
* @returns Username of authticated user upon success
|
||||
*/
|
||||
module.exports.authenticateSession = async function(user, pass, req){
|
||||
//Fuck you yoda
|
||||
try{
|
||||
//Grab previous attempts
|
||||
const attempt = failedAttempts.get(user);
|
||||
|
||||
//If we're proxied use passthrough IP
|
||||
const ip = config.proxied ? req.headers['x-forwarded-for'] : req.ip;
|
||||
|
||||
//Look for ban by IP
|
||||
const ipBanDB = await userBanModel.checkBanByIP(ip);
|
||||
|
||||
//If this ip is randy bobandy
|
||||
if(ipBanDB != null){
|
||||
//Make the number a little prettier despite the lack of precision since we're not doing calculations here :P
|
||||
const expiration = ipBanDB.getDaysUntilExpiration() < 1 ? 0 : ipBanDB.getDaysUntilExpiration();
|
||||
|
||||
//If the ban is permanent
|
||||
if(ipBanDB.permanent){
|
||||
//tell it to fuck off
|
||||
throw loggerUtils.exceptionSmith(`The IP address you are trying to login from has been permanently banned. Your cleartext IP has been saved to the database. Any associated accounts will be nuked in ${expiration} day(s).`, "unauthorized");
|
||||
}else{
|
||||
//tell it to fuck off
|
||||
throw loggerUtils.exceptionSmith(`The IP address you are trying to login from has been temporarily banned. Your cleartext IP has been saved to the database until the ban expires in ${expiration} day(s).`, "unauthorized");
|
||||
}
|
||||
}
|
||||
|
||||
//If we have failed attempts
|
||||
if(attempt != null){
|
||||
//If we have more failed attempts than allowed
|
||||
if(attempt.count > maxAttempts){
|
||||
throw loggerUtils.exceptionSmith("This account has been locked for at 24 hours due to a large amount of failed log-in attempts", "unauthorized");
|
||||
}
|
||||
|
||||
//If we're throttling logins
|
||||
if(attempt.count > throttleAttempts){
|
||||
//Verification doesnt get sanatized or checked since that would most likely break the cryptography
|
||||
//Since we've already got access to the request and dont need to import anything, why bother getting it from a parameter?
|
||||
if(req.body.verification == null){
|
||||
throw loggerUtils.exceptionSmith("Verification failed!", "unauthorized");
|
||||
}else if(!altchaUtils.verify(req.body.verification, user)){
|
||||
throw loggerUtils.exceptionSmith("Verification failed!", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Authenticate the session
|
||||
const userDB = await userModel.authenticate(user, pass);
|
||||
|
||||
//Check for user ban
|
||||
const userBanDB = await userBanModel.checkBanByUserDoc(userDB);
|
||||
|
||||
//If the user is banned
|
||||
if(userBanDB){
|
||||
//Make the number a little prettier despite the lack of precision since we're not doing calculations here :P
|
||||
const expiration = userBanDB.getDaysUntilExpiration() < 1 ? 0 : userBanDB.getDaysUntilExpiration();
|
||||
if(userBanDB.permanent){
|
||||
throw loggerUtils.exceptionSmith(`Your account has been permanently banned, and will be nuked from the database in: ${expiration} day(s)`, "unauthorized");
|
||||
}else{
|
||||
throw loggerUtils.exceptionSmith(`Your account has been temporarily banned, and will be reinstated in: ${expiration} day(s)`, "unauthorized");
|
||||
}
|
||||
}
|
||||
|
||||
//Tattoo the session with user and metadata
|
||||
//unfortunately store.all() does not return sessions w/ their ID so we had to improvise...
|
||||
//Not sure if this is just how connect-mongo is implemented or if it's an express issue, but connect-mongodb-session seems to not implement the all() function what so ever...
|
||||
req.session.seshid = req.session.id;
|
||||
req.session.authdate = new Date();
|
||||
req.session.user = {
|
||||
user: userDB.user,
|
||||
id: userDB.id,
|
||||
rank: userDB.rank
|
||||
}
|
||||
|
||||
//Tattoo hashed IP address to user account for seven days
|
||||
userDB.tattooIPRecord(ip);
|
||||
|
||||
//If we got to here then the log-in was successful. We should clear-out any failed attempts.
|
||||
failedAttempts.delete(user);
|
||||
|
||||
//return user
|
||||
return userDB.user;
|
||||
}catch(err){
|
||||
//Look for previous failed attempts
|
||||
var attempt = failedAttempts.get(user);
|
||||
|
||||
//If this is the first attempt
|
||||
if(attempt == null){
|
||||
//Create new attempt object
|
||||
attempt = {
|
||||
count: 1,
|
||||
lastAttempt: new Date()
|
||||
}
|
||||
}else{
|
||||
//Create updated attempt object
|
||||
attempt = {
|
||||
count: attempt.count + 1,
|
||||
lastAttempt: new Date()
|
||||
}
|
||||
}
|
||||
|
||||
//Commit the failed attempt to the failed sign-in cache
|
||||
failedAttempts.set(user, attempt);
|
||||
|
||||
//y33t
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs user out and destroys all server-side traces of a given session
|
||||
* @param {express-session.session} session
|
||||
*/
|
||||
module.exports.killSession = async function(session){
|
||||
session.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns how many failed login attempts within the past day or so since the last login has occured for a given user
|
||||
* @param {String} user - User to check map against
|
||||
* @returns {Number} of failed login attempts
|
||||
*/
|
||||
module.exports.getLoginAttempts = function(user){
|
||||
//Read the code, i'm not explaining this
|
||||
return failedAttempts.get(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nightly Function Call which iterates through the failed login attempts map, removing any which haven't been attempted in over a da yeahy
|
||||
*/
|
||||
module.exports.processExpiredAttempts = function(){
|
||||
for(user of failedAttempts.keys()){
|
||||
//Get attempt by user
|
||||
const attempt = failedAttempts.get(user);
|
||||
//Check how long its been
|
||||
const daysSinceLastAttempt = ((new Date() - attempt.lastAttempt) / (1000 * 60 * 60 * 24)).toFixed(1);
|
||||
|
||||
//If it's been more than a day since anyones tried to log in as this user
|
||||
if(daysSinceLastAttempt >= 1){
|
||||
//Clear out the attempts so that they don't need to fuck with a captcha anymore
|
||||
failedAttempts.delete(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.throttleAttempts = throttleAttempts;
|
||||
module.exports.maxAttempts = maxAttempts;</code></pre>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="module.exports.html">exports</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
||||
</nav>
|
||||
|
||||
<br class="clear">
|
||||
|
||||
<footer>
|
||||
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:08:41 GMT-0400 (Eastern Daylight Time)
|
||||
</footer>
|
||||
|
||||
<script> prettyPrint(); </script>
|
||||
<script src="scripts/linenumber.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in a new issue