diff --git a/src/app/channel/activeChannel.js b/src/app/channel/activeChannel.js
index 5e9ebd7..1e62f42 100644
--- a/src/app/channel/activeChannel.js
+++ b/src/app/channel/activeChannel.js
@@ -16,6 +16,7 @@ along with this program. If not, see .*/
//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')
@@ -30,7 +31,7 @@ module.exports = class{
this.queue = new queue(server, chanDB, this);
this.playlistHandler = new playlistHandler(server, chanDB, this);
//Define the chat buffer
- this.chatBuffer = [];
+ this.chatBuffer = new chatBuffer(server, chanDB, this);
}
async handleConnection(userDB, chanDB, socket){
diff --git a/src/app/channel/chatBuffer.js b/src/app/channel/chatBuffer.js
new file mode 100644
index 0000000..f51745e
--- /dev/null
+++ b/src/app/channel/chatBuffer.js
@@ -0,0 +1,109 @@
+/*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 .*/
+const config = require('../../../config.json');
+const channelModel = require('../../schemas/channel/channelSchema');
+
+class chatBuffer{
+ 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;
+ }
+
+ 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);
+ }
+ }
+
+ shift(){
+ //remove chat from RAM buffer, no need to handle DB timing here, since push should be called when this is called.
+ 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.`);
+ }
+
+ 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;
\ No newline at end of file
diff --git a/src/app/channel/chatHandler.js b/src/app/channel/chatHandler.js
index 26dea9d..17266e1 100644
--- a/src/app/channel/chatHandler.js
+++ b/src/app/channel/chatHandler.js
@@ -141,7 +141,7 @@ module.exports = class{
const channel = this.server.activeChannels.get(chan);
//If chat buffer length is over mandated size
- if(channel.chatBuffer.length >= this.chatBufferSize){
+ if(channel.chatBuffer.buffer.length >= this.chatBufferSize){
//Take out oldest chat
channel.chatBuffer.shift();
}
diff --git a/src/app/channel/connectedUser.js b/src/app/channel/connectedUser.js
index f7c3176..659fb69 100644
--- a/src/app/channel/connectedUser.js
+++ b/src/app/channel/connectedUser.js
@@ -160,7 +160,7 @@ module.exports = class{
const queueLock = this.channel.queue.locked;
//Get chat buffer
- const chatBuffer = this.channel.chatBuffer;
+ const chatBuffer = this.channel.chatBuffer.buffer;
//Send off the metadata to our user's clients
this.emit("clientMetadata", {user: userObj, flairList, queue, queueLock, chatBuffer});
diff --git a/src/schemas/channel/chatSchema.js b/src/schemas/channel/chatSchema.js
index b54f9b6..e5ac423 100644
--- a/src/schemas/channel/chatSchema.js
+++ b/src/schemas/channel/chatSchema.js
@@ -17,6 +17,11 @@ along with this program. If not, see .*/
//NPM Imports
const {mongoose} = require('mongoose');
+const linkSchema = new mongoose.Schema({
+ link: mongoose.SchemaTypes.String,
+ type: mongoose.SchemaTypes.String
+});
+
const chatSchema = new mongoose.Schema({
user: {
type: mongoose.SchemaTypes.String,
@@ -39,10 +44,9 @@ const chatSchema = new mongoose.Schema({
required: true,
},
links: {
- type: [mongoose.SchemaTypes.Number],
+ type: [linkSchema],
required: true,
},
});
-
module.exports = chatSchema;
\ No newline at end of file
diff --git a/src/utils/loggerUtils.js b/src/utils/loggerUtils.js
index 6265dac..b6daae9 100644
--- a/src/utils/loggerUtils.js
+++ b/src/utils/loggerUtils.js
@@ -84,6 +84,9 @@ module.exports.socketCriticalExceptionHandler = function(socket, err){
//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."});
}
diff --git a/www/js/channel/chat.js b/www/js/channel/chat.js
index 5ba1b3b..a6cfa84 100644
--- a/www/js/channel/chat.js
+++ b/www/js/channel/chat.js
@@ -145,6 +145,8 @@ class chatBox{
chatBody.classList.add("chat-panel-buffer","chat-entry-body");
chatEntry.appendChild(chatBody);
+ console.log(data);
+
//Append the post-processed chat-body to the chat buffer
this.chatBuffer.appendChild(this.chatPostprocessor.postprocess(chatEntry, data));