From d0ef60dd4fc2fada77c0d7c6cbd68f5490e5f36c Mon Sep 17 00:00:00 2001 From: rainbow napkin Date: Thu, 15 Oct 2026 01:15:36 -0400 Subject: [PATCH] Added join/leave messages a-la fore.st --- src/app/channel/activeChannel.js | 75 +++++++++++++++++++++++++++ src/app/channel/chatHandler.js | 6 +++ src/app/channel/connectedUser.js | 3 ++ www/css/channel.css | 6 +++ www/css/theme/the-network-classic.css | 14 ++--- www/js/channel/chatPostprocessor.js | 25 +++++++-- 6 files changed, 119 insertions(+), 10 deletions(-) diff --git a/src/app/channel/activeChannel.js b/src/app/channel/activeChannel.js index 44b170d..18d059e 100644 --- a/src/app/channel/activeChannel.js +++ b/src/app/channel/activeChannel.js @@ -14,6 +14,9 @@ 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 .*/ +//config imports +const config = require('../../../config.json'); + //local imports const connectedUser = require('./connectedUser'); const chatBuffer = require('./chatBuffer'); @@ -77,6 +80,61 @@ class activeChannel{ * Child Chat Buffer Object */ this.chatBuffer = new chatBuffer(server, chanDB, this); + + this.userAnnouncements = { + join: [ + "%UNAME% joined.", + "%UNAME% arrived.", + "%UNAME% appeared.", + "%UNAME% hopped in.", + "%UNAME% checked in.", + "%UNAME% checked in to see what condition their condition was in.", + "%UNAME% logged in.", + "%UNAME% turned on, tuned in, and dropped out.", + `%UNAME% is now using ${config.instanceName}.`, + `%UNAME% is now watching #${this.name}.`, + "%UNAME% tuned in.", + "%UNAME% is ready to sparkem.", + "%UNAME% connected.", + "%UNAME% joins the battle.", + "%UNAME% hopped on.", + "%UNAME% logged on.", + `${config.instanceName}, population: %UNAME%`, + "Welcome, %UNAME%.", + "Salutations, %UNAME%.", + "Hello, %UNAME%.", + "Greetings, %UNAME%.", + "Sup, %UNAME%.", + "I AM THE GOD OF HELLFIRE, AND I BRING YOU: %UNAME%!", + "A wild %UNAME% has appeared!" + ], + leave: [ + "%UNAME% left.", + "%UNAME% dropped out.", + "%UNAME% checked out.", + "%UNAME% quit.", + "%UNAME% is no longer with us.", + `%UNAME% is no longer using ${config.instanceName}.`, + "%UNAME% dipped.", + "%UNAME% booked it.", + "%UNAME% cheesed it.", + "%UNAME% vanished.", + "%UNAME% said dueces.", + "%UNAME% has left the building.", + "%UNAME% bounced.", + "%UNAME% is beyond the horizon.", + "%UNAME% has drifted into space.", + "%UNAME% is outskies.", + "%UNAME% went to the shadow realm.", + "Goodbye, %UNAME%.", + "Dueces, %UNAME%.", + "Bye, %UNAME%.", + "Farewell, %UNAME%.", + "l8r %UNAME%.", + "That'll do, %UNAME%." + ] + } + } @@ -150,6 +208,9 @@ class activeChannel{ //If this is the last connection for this user, remove them from the userlist this.userList.delete(socket.user.user); + + //Broadcast the un-welcome wagon + this.welcomeWagon(socket.user.user, true); } //and send out the filtered list @@ -195,6 +256,20 @@ class activeChannel{ //Broadcast that sumbitch this.server.io.in(this.name).emit('chanEmotes', emoteList); } + + /** + * Basic function to welcome/send off connecting/disconnecting users + * @param {string} user - user to welcome + * @param {boolean} leaving - true if user is leaving instead of joining + */ + async welcomeWagon(user, leaving = false){ + //Grab the applicable list depending on if the user is leaving or not + const activeList = leaving ? this.userAnnouncements.leave : this.userAnnouncements.join; + //Randomly yoink the applicable message + const msg = activeList[Math.floor(Math.random() * activeList.length)]; + + this.server.chatHandler.relayChat(user, "", 0, msg.replace("%UNAME%", user), "serverwhisper", this.name, []); + } } module.exports = activeChannel; \ No newline at end of file diff --git a/src/app/channel/chatHandler.js b/src/app/channel/chatHandler.js index 42b6e4a..56dae9d 100644 --- a/src/app/channel/chatHandler.js +++ b/src/app/channel/chatHandler.js @@ -230,6 +230,12 @@ class chatHandler{ //Send out chat this.server.io.in(chan).emit("chatMessage", chat); + //If we're relaying a channel-wide serverwhisper + if(chat.type == "serverwhisper"){ + //Don't bother throwing it into the buffer + return; + } + const channel = this.server.activeChannels.get(chan); //If chat buffer length is over mandated size diff --git a/src/app/channel/connectedUser.js b/src/app/channel/connectedUser.js index 5347cb4..b70e63a 100644 --- a/src/app/channel/connectedUser.js +++ b/src/app/channel/connectedUser.js @@ -237,6 +237,9 @@ class connectedUser{ //Send off the metadata to our user's clients this.emit("clientMetadata", {user: userObj, flairList, queueLock, chatBuffer, streamType: chanDB.settings.streamType}); + + //Broadcast the welcome wagon + this.channel.welcomeWagon(userDB.user); } /** diff --git a/www/css/channel.css b/www/css/channel.css index 76fa2ab..c520354 100644 --- a/www/css/channel.css +++ b/www/css/channel.css @@ -176,6 +176,12 @@ p.panel-head-element{ flex-direction: var(--chat-entry-flex-direction); } +.chat-entry-serverwhisper, .chat-entry-tokewhisper{ + overflow: hidden; + transition: max-height 0.5s; + max-height: 5em; +} + .chat-entry-nameplate, .pm-panel-sesh-entry-nameplate{ display: flex; } diff --git a/www/css/theme/the-network-classic.css b/www/css/theme/the-network-classic.css index 6a4d8ac..5b4ba10 100644 --- a/www/css/theme/the-network-classic.css +++ b/www/css/theme/the-network-classic.css @@ -499,31 +499,31 @@ p.channel-guide-entry-item{ color: var(--userlist-color6); } -.chat-userlist-color0:not(.chat-entry-toke, .chat-entry-whisper, .chat-entry-tokewhisper, .chat-entry-announcement), .pm-panel-sesh-userlist-color0{/*green0*/ +.chat-userlist-color0:not(.chat-entry-toke, .chat-entry-serverwhisper, .chat-entry-tokewhisper, .chat-entry-announcement), .pm-panel-sesh-userlist-color0{/*green0*/ border-left: solid 5px var(--userlist-color0); } -.chat-userlist-color1:not(.chat-entry-toke, .chat-entry-whisper, .chat-entry-tokewhisper, .chat-entry-announcement), .pm-panel-sesh-userlist-color1{/*red0*/ +.chat-userlist-color1:not(.chat-entry-toke, .chat-entry-serverwhisper, .chat-entry-tokewhisper, .chat-entry-announcement), .pm-panel-sesh-userlist-color1{/*red0*/ border-left: solid 5px var(--userlist-color1); } -.chat-userlist-color2:not(.chat-entry-toke, .chat-entry-whisper, .chat-entry-tokewhisper, .chat-entry-announcement), .pm-panel-sesh-userlist-color2{/*blue0*/ +.chat-userlist-color2:not(.chat-entry-toke, .chat-entry-serverwhisper, .chat-entry-tokewhisper, .chat-entry-announcement), .pm-panel-sesh-userlist-color2{/*blue0*/ border-left: solid 5px var(--userlist-color2); } -.chat-userlist-color3:not(.chat-entry-toke, .chat-entry-whisper, .chat-entry-tokewhisper, .chat-entry-announcement), .pm-panel-sesh-userlist-color3{/*tan0*/ +.chat-userlist-color3:not(.chat-entry-toke, .chat-entry-serverwhisper, .chat-entry-tokewhisper, .chat-entry-announcement), .pm-panel-sesh-userlist-color3{/*tan0*/ border-left: solid 5px var(--userlist-color3); } -.chat-userlist-color4:not(.chat-entry-toke, .chat-entry-whisper, .chat-entry-tokewhisper, .chat-entry-announcement), .pm-panel-sesh-userlist-color4{/*pink0*/ +.chat-userlist-color4:not(.chat-entry-toke, .chat-entry-serverwhisper, .chat-entry-tokewhisper, .chat-entry-announcement), .pm-panel-sesh-userlist-color4{/*pink0*/ border-left: solid 5px var(--userlist-color4); } -.chat-userlist-color5:not(.chat-entry-toke, .chat-entry-whisper, .chat-entry-tokewhisper, .chat-entry-announcement), .pm-panel-sesh-userlist-color5{/*orange*/ +.chat-userlist-color5:not(.chat-entry-toke, .chat-entry-serverwhisper, .chat-entry-tokewhisper, .chat-entry-announcement), .pm-panel-sesh-userlist-color5{/*orange*/ border-left: solid 5px var(--userlist-color5); } -.chat-userlist-color6:not(.chat-entry-toke, .chat-entry-whisper, .chat-entry-tokewhisper, .chat-entry-announcement), .pm-panel-sesh-userlist-color6{/*violet*/ +.chat-userlist-color6:not(.chat-entry-toke, .chat-entry-serverwhisper, .chat-entry-tokewhisper, .chat-entry-announcement), .pm-panel-sesh-userlist-color6{/*violet*/ border-left: solid 5px var(--userlist-color6); } diff --git a/www/js/channel/chatPostprocessor.js b/www/js/channel/chatPostprocessor.js index ccd6ed6..6babd9a 100644 --- a/www/js/channel/chatPostprocessor.js +++ b/www/js/channel/chatPostprocessor.js @@ -696,15 +696,27 @@ class chatPostprocessor{ //Add toke/tokewhisper class this.chatBody.classList.add("toke"); - }else if(this.rawData.type == "tokewhisper"){ + }else if(this.rawData.type == "tokewhisper" || this.rawData.type == "serverwhisper"){ //Squash the high-level this.chatEntry.querySelector('.high-level').remove(); //remove the username this.chatEntry.querySelector('.chat-entry-username').remove(); - //Add toke/tokewhisper class - this.chatBody.classList.add("tokewhisper","serverwhisper"); + //Add serverwhisper class + this.chatBody.classList.add("serverwhisper"); + + //If this is a toke whisper + if(this.rawData.type == "tokewhisper"){ + //Add tokewhisper class + this.chatBody.classList.add("tokewhisper"); + } + + //Set variable to hold chatEntry so the one after doesnt get clobbered by the killServerWhisper function + const currentWhisper = this.chatEntry; + + //Kill server whispers after 10s, just like fore.st XP + setTimeout(()=>{this.killServerWhisper(currentWhisper)}, 10000); }else if(this.rawData.type == "spoiler"){ //Set whole-body spoiler this.chatBody.classList.add("spoiler"); @@ -719,4 +731,11 @@ class chatPostprocessor{ this.chatBody.classList.add("italics"); } } + + killServerWhisper(whisper, delay = 500){ + //Drop max height to 0 to trigger the fancy animation + whisper.style.maxHeight = 0; + //Remove whisper after animation is done + setTimeout(()=>{whisper.remove()}, delay); + } } \ No newline at end of file