diff --git a/src/app/channel/channelManager.js b/src/app/channel/channelManager.js
index 66a14cd..4b594ad 100644
--- a/src/app/channel/channelManager.js
+++ b/src/app/channel/channelManager.js
@@ -24,6 +24,7 @@ const {userModel} = require('../../schemas/user/userSchema');
const userBanModel = require('../../schemas/user/userBanSchema');
const loggerUtils = require('../../utils/loggerUtils');
const csrfUtils = require('../../utils/csrfUtils');
+const presenceUtils = require('../../utils/presenceUtils');
const activeChannel = require('./activeChannel');
const chatHandler = require('./chatHandler');
@@ -259,7 +260,7 @@ class channelManager{
/**
* Handles a disconnection event for a single active user within a given channel (when all sockets disconnect)
- * @param {*} userObj
+ * @param {connectedUser} userObj - Connected user object to handle disconnection of
*/
handleUserDisconnect(userObj){
//Create array to hold
@@ -282,6 +283,9 @@ class channelManager{
}else{
//Delete the user from the status map
this.activeUsers.delete(userObj.user);
+
+ //Mark last disconnection as user activity, as they'll no longer be marked as streaming.
+ presenceUtils.handlePresence(userObj.user);
}
}
diff --git a/src/routers/adminPanelRouter.js b/src/routers/adminPanelRouter.js
index 1d54411..129b403 100644
--- a/src/routers/adminPanelRouter.js
+++ b/src/routers/adminPanelRouter.js
@@ -17,16 +17,17 @@ along with this program. If not, see .*/
//npm imports
const { Router } = require('express');
-
//local imports
const permissionSchema = require("../schemas/permissionSchema");
const adminPanelController = require("../controllers/adminPanelController");
+const presenceUtils = require("../utils/presenceUtils");
//globals
const router = Router();
//Use authentication middleware
router.use(permissionSchema.reqPermCheck("adminPanel"))
+router.use(presenceUtils.presenceMiddleware);
//routing functions
router.get('/', adminPanelController.get);
diff --git a/src/routers/channelRouter.js b/src/routers/channelRouter.js
index c8343ad..fb32167 100644
--- a/src/routers/channelRouter.js
+++ b/src/routers/channelRouter.js
@@ -22,6 +22,7 @@ const { Router } = require('express');
const channelModel = require("../schemas/channel/channelSchema");
const channelController = require("../controllers/channelController");
const channelSettingsController = require("../controllers/channelSettingsController");
+const presenceUtils = require("../utils/presenceUtils");
//globals
const router = Router();
@@ -29,6 +30,9 @@ const router = Router();
//User authentication middleware
router.use("/*/settings",channelModel.reqPermCheck("manageChannel","/c/"));
+//Use presence middleware
+router.use(presenceUtils.presenceMiddleware);
+
//routing functions
router.get('/*/settings', channelSettingsController.get);
router.get('/*/', channelController.get);
diff --git a/src/routers/indexRouter.js b/src/routers/indexRouter.js
index 0b3528b..92135dc 100644
--- a/src/routers/indexRouter.js
+++ b/src/routers/indexRouter.js
@@ -20,10 +20,14 @@ const { Router } = require('express');
//local imports
const indexController = require("../controllers/indexController");
+const presenceUtils = require("../utils/presenceUtils");
//globals
const router = Router();
+//Use presence middleware
+router.use(presenceUtils.presenceMiddleware);
+
//routing functions
router.get('/', indexController.get);
diff --git a/src/routers/newChannelRouter.js b/src/routers/newChannelRouter.js
index 0dd998c..e77808b 100644
--- a/src/routers/newChannelRouter.js
+++ b/src/routers/newChannelRouter.js
@@ -21,6 +21,7 @@ const { Router } = require('express');
//local imports
const permissionSchema = require("../schemas/permissionSchema");
const newChannelController = require("../controllers/newChannelController");
+const presenceUtils = require("../utils/presenceUtils");
//globals
const router = Router();
@@ -28,6 +29,9 @@ const router = Router();
//user authentication middleware
router.use("/",permissionSchema.reqPermCheck("registerChannel"));
+//Use presence middleware
+router.use(presenceUtils.presenceMiddleware);
+
//routing functions
router.get('/', newChannelController.get);
diff --git a/src/schemas/user/userSchema.js b/src/schemas/user/userSchema.js
index 40a3e6e..5f718ed 100644
--- a/src/schemas/user/userSchema.js
+++ b/src/schemas/user/userSchema.js
@@ -60,6 +60,11 @@ const userSchema = new mongoose.Schema({
required: true,
default: new Date()
},
+ lastActive: {
+ type: mongoose.SchemaTypes.Date,
+ required: true,
+ default: new Date()
+ },
rank: {
type: mongoose.SchemaTypes.String,
required: true,
diff --git a/src/utils/presenceUtils.js b/src/utils/presenceUtils.js
new file mode 100644
index 0000000..603b5c3
--- /dev/null
+++ b/src/utils/presenceUtils.js
@@ -0,0 +1,75 @@
+/*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 .*/
+
+//local includes
+const userSchema = require('../schemas/user/userSchema');
+
+//User activity map to keep us from constantly reading off of the DB
+let activityMap = new Map();
+
+//How much difference between last write and now until we hit the DB again (in millis)
+//Defaults to two minutes
+const tolerance = 2 * (60 * 1000);
+
+module.exports.presenceMiddleware = function(req, res, next){
+ //Pull user from session
+ const user = req.session.user;
+
+ //if we have a user object
+ if(user != null){
+ //Handle Presence
+ module.exports.handlePresence(user.user);
+ }
+
+ //Go on to next part of the middleware chain
+ next();
+}
+
+module.exports.handlePresence = async function(user, userDB, noSave = false){
+ //If we don't have a user
+ if(user == null || user == ''){
+ //Drop that shit
+ return;
+ }
+
+ //Get current date as epoch (millis)
+ const now = new Date();
+ const millis = now.getTime();
+
+ //Check last user activity
+ const activity = activityMap.get(user);
+
+ //If we have no recorded activity, or if the the time between now and the last activity is greater than two minutes
+ if(activity == null || millis - activity > tolerance){
+ //Set last user activity
+ activityMap.set(user, millis);
+
+ //If we wheren't handed a free user doc
+ if(userDB == null){
+ //Pull one from the username
+ userDB = await userSchema.userModel.findOne({user: user});
+ }
+
+ //Set last active in user's DB document
+ userDB.lastActive = now;
+
+ //If saving is enabled
+ if(!noSave){
+ //Save document to
+ await userDB.save();
+ }
+ }
+}
\ No newline at end of file