Per-Channel Emotes Implemented.
This commit is contained in:
parent
163cecf9f0
commit
c3d016e1af
|
|
@ -56,6 +56,7 @@ module.exports = class{
|
||||||
//await this.sendClientMetadata(userDB, socket);
|
//await this.sendClientMetadata(userDB, socket);
|
||||||
await userObj.sendClientMetadata();
|
await userObj.sendClientMetadata();
|
||||||
await userObj.sendSiteEmotes();
|
await userObj.sendSiteEmotes();
|
||||||
|
await userObj.sendChanEmotes();
|
||||||
|
|
||||||
//Send out the userlist
|
//Send out the userlist
|
||||||
this.broadcastUserList(socket.chan);
|
this.broadcastUserList(socket.chan);
|
||||||
|
|
@ -101,4 +102,18 @@ module.exports = class{
|
||||||
|
|
||||||
this.server.io.in(this.name).emit("userList", userList);
|
this.server.io.in(this.name).emit("userList", userList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -15,9 +15,10 @@ 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/>.*/
|
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||||
|
|
||||||
//local imports
|
//local imports
|
||||||
|
const channelModel = require('../../schemas/channel/channelSchema');
|
||||||
|
const permissionModel = require('../../schemas/permissionSchema');
|
||||||
const flairModel = require('../../schemas/flairSchema');
|
const flairModel = require('../../schemas/flairSchema');
|
||||||
const emoteModel = require('../../schemas/emoteSchema');
|
const emoteModel = require('../../schemas/emoteSchema');
|
||||||
const permissionModel = require('../../schemas/permissionSchema');
|
|
||||||
|
|
||||||
module.exports = class{
|
module.exports = class{
|
||||||
constructor(userDB, chanRank, channel, socket){
|
constructor(userDB, chanRank, channel, socket){
|
||||||
|
|
@ -96,6 +97,20 @@ module.exports = class{
|
||||||
this.emit('siteEmotes', emoteList);
|
this.emit('siteEmotes', emoteList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
updateFlair(flair){
|
updateFlair(flair){
|
||||||
this.flair = flair;
|
this.flair = flair;
|
||||||
|
|
||||||
|
|
|
||||||
167
src/controllers/api/channel/emoteController.js
Normal file
167
src/controllers/api/channel/emoteController.js
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
/*Canopy - The next generation of stoner streaming software
|
||||||
|
Copyright (C) 2024 Rainbownapkin and the TTN Community
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
||||||
|
|
||||||
|
//npm imports
|
||||||
|
const {validationResult, matchedData} = require('express-validator');
|
||||||
|
|
||||||
|
//local imports
|
||||||
|
const {exceptionHandler, errorHandler} = require('../../../utils/loggerUtils');
|
||||||
|
const channelModel = require('../../../schemas/channel/channelSchema');
|
||||||
|
const linkUtils = require('../../../utils/linkUtils');
|
||||||
|
|
||||||
|
module.exports.get = async function(req, res){
|
||||||
|
try{
|
||||||
|
//get validation error results
|
||||||
|
const validResult = validationResult(req);
|
||||||
|
|
||||||
|
//if they're empty
|
||||||
|
if(validResult.isEmpty()){
|
||||||
|
//Pull sanatized/validated input
|
||||||
|
const data = matchedData(req);
|
||||||
|
//Get the requested channel
|
||||||
|
const chanDB = await channelModel.findOne({name: data.chanName});
|
||||||
|
|
||||||
|
//If the requested channel doesn't exist
|
||||||
|
if(chanDB == null){
|
||||||
|
//ACK
|
||||||
|
return errorHandler(res, "Channel not found.", "Bad Query");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Return found channel's emotes
|
||||||
|
res.status(200);
|
||||||
|
return res.send(chanDB.getEmotes());
|
||||||
|
}else{
|
||||||
|
//otherwise scream
|
||||||
|
res.status(400);
|
||||||
|
return res.send({errors: validResult.array()})
|
||||||
|
}
|
||||||
|
}catch(err){
|
||||||
|
return exceptionHandler(res, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.post = async function(req, res){
|
||||||
|
try{
|
||||||
|
//get validation error results
|
||||||
|
const validResult = validationResult(req);
|
||||||
|
|
||||||
|
//if they're empty
|
||||||
|
if(validResult.isEmpty()){
|
||||||
|
//get matched data
|
||||||
|
const {chanName, emoteName, link} = matchedData(req);
|
||||||
|
//query for requested channel
|
||||||
|
const chanDB = await channelModel.findOne({name: chanName});
|
||||||
|
|
||||||
|
//If the requested channel doesn't exist
|
||||||
|
if(chanDB == null){
|
||||||
|
//ACK
|
||||||
|
return errorHandler(res, "Channel not found.", "Bad Query");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Filter emote list for...
|
||||||
|
const foundEmotes = chanDB.emotes.filter((emote) => {
|
||||||
|
//emotes with a name that matches emoteName
|
||||||
|
return emote.name == emoteName;
|
||||||
|
});
|
||||||
|
|
||||||
|
//If we found one
|
||||||
|
if(foundEmotes.length > 0){
|
||||||
|
//ACK
|
||||||
|
return errorHandler(res, "Emote already exists!");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Initialize the emote object from linkUtils
|
||||||
|
var emoteObj = await linkUtils.markLink(link);
|
||||||
|
|
||||||
|
//If we didn't get a valid image/video file
|
||||||
|
if(emoteObj.type != 'image' && emoteObj.type != 'video'){
|
||||||
|
//AAAAAAAAAAAAAAAAAA
|
||||||
|
return errorHandler(res, 'URL either does not lead to a valid image/video file, or leads to one that is larger than 4MB');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Add the name to the emoteObj
|
||||||
|
emoteObj.name = emoteName;
|
||||||
|
|
||||||
|
//Push the newly created emote object into the channels emotes list
|
||||||
|
chanDB.emotes.push(emoteObj);
|
||||||
|
|
||||||
|
//Save the channel DB document
|
||||||
|
chanDB.save();
|
||||||
|
|
||||||
|
//Return the updated command list
|
||||||
|
res.status(200);
|
||||||
|
return res.send(chanDB.getEmotes());
|
||||||
|
}else{
|
||||||
|
//otherwise scream
|
||||||
|
res.status(400);
|
||||||
|
return res.send({errors: validResult.array()})
|
||||||
|
}
|
||||||
|
}catch(err){
|
||||||
|
return exceptionHandler(res, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.delete = async function(req, res){
|
||||||
|
try{
|
||||||
|
//get validation error results
|
||||||
|
const validResult = validationResult(req);
|
||||||
|
|
||||||
|
//if they're empty
|
||||||
|
if(validResult.isEmpty()){
|
||||||
|
//get matched data
|
||||||
|
const {chanName, emoteName, link} = matchedData(req);
|
||||||
|
//query for requested channel
|
||||||
|
const chanDB = await channelModel.findOne({name: chanName});
|
||||||
|
|
||||||
|
//If the requested channel doesn't exist
|
||||||
|
if(chanDB == null){
|
||||||
|
//ACK
|
||||||
|
return errorHandler(res, "Channel not found.", "Bad Query");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Filter emote list for...
|
||||||
|
const foundEmotes = chanDB.emotes.filter((emote, emoteIndex) => {
|
||||||
|
//If we find an emote that matches the emote name
|
||||||
|
if(emote.name == emoteName){
|
||||||
|
//Splice out the emote
|
||||||
|
chanDB.emotes.splice(emoteIndex, 1);
|
||||||
|
//Return true to signify we found and deleted one
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//If didn't find one
|
||||||
|
if(foundEmotes.length <= 0){
|
||||||
|
//ACK
|
||||||
|
return errorHandler(res, "Cannot delete non-existant emote!");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Save the channel DB document
|
||||||
|
chanDB.save();
|
||||||
|
|
||||||
|
//Return the updated command list
|
||||||
|
res.status(200);
|
||||||
|
return res.send(chanDB.getEmotes());
|
||||||
|
}else{
|
||||||
|
//otherwise scream
|
||||||
|
res.status(400);
|
||||||
|
return res.send({errors: validResult.array()})
|
||||||
|
}
|
||||||
|
}catch(err){
|
||||||
|
return exceptionHandler(res, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,12 +19,16 @@ const { body, checkExact } = require('express-validator');
|
||||||
const { Router } = require('express');
|
const { Router } = require('express');
|
||||||
|
|
||||||
//local imports
|
//local imports
|
||||||
const permissionSchema = require("../../schemas/permissionSchema");
|
//Models
|
||||||
|
const permissionModel = require("../../schemas/permissionSchema");
|
||||||
const channelModel = require("../../schemas/channel/channelSchema");
|
const channelModel = require("../../schemas/channel/channelSchema");
|
||||||
|
//Valudators
|
||||||
const channelValidator = require("../../validators/channelValidator");
|
const channelValidator = require("../../validators/channelValidator");
|
||||||
const accountValidator = require("../../validators/accountValidator");
|
const accountValidator = require("../../validators/accountValidator");
|
||||||
const {channelPermissionValidator} = require("../../validators/permissionsValidator");
|
const {channelPermissionValidator} = require("../../validators/permissionsValidator");
|
||||||
const tokebotValidator = require("../../validators/tokebotValidator");
|
const tokebotValidator = require("../../validators/tokebotValidator");
|
||||||
|
const emoteValidator = require("../../validators/emoteValidator");
|
||||||
|
//Controllers
|
||||||
const registerController = require("../../controllers/api/channel/registerController");
|
const registerController = require("../../controllers/api/channel/registerController");
|
||||||
const listController = require("../../controllers/api/channel/listController");
|
const listController = require("../../controllers/api/channel/listController");
|
||||||
const settingsController = require("../../controllers/api/channel/settingsController");
|
const settingsController = require("../../controllers/api/channel/settingsController");
|
||||||
|
|
@ -33,18 +37,20 @@ const rankController = require("../../controllers/api/channel/rankController");
|
||||||
const deleteController = require("../../controllers/api/channel/deleteController");
|
const deleteController = require("../../controllers/api/channel/deleteController");
|
||||||
const banController = require("../../controllers/api/channel/banController");
|
const banController = require("../../controllers/api/channel/banController");
|
||||||
const tokeCommandController = require("../../controllers/api/channel/tokeCommandController");
|
const tokeCommandController = require("../../controllers/api/channel/tokeCommandController");
|
||||||
|
const emoteController = require('../../controllers/api/channel/emoteController');
|
||||||
|
|
||||||
//globals
|
//globals
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
//user authentication middleware
|
//user authentication middleware
|
||||||
router.use("/register",permissionSchema.reqPermCheck("registerChannel"));
|
router.use("/register",permissionModel.reqPermCheck("registerChannel"));
|
||||||
router.use("/settings", channelValidator.name('chanName'));
|
router.use("/settings", channelValidator.name('chanName'));
|
||||||
router.use("/permissions", channelValidator.name('chanName'));
|
router.use("/permissions", channelValidator.name('chanName'));
|
||||||
router.use("/rank", channelValidator.name('chanName'));
|
router.use("/rank", channelValidator.name('chanName'));
|
||||||
router.use("/delete", channelValidator.name('chanName'));
|
router.use("/delete", channelValidator.name('chanName'));
|
||||||
router.use("/ban", channelValidator.name('chanName'));
|
router.use("/ban", channelValidator.name('chanName'));
|
||||||
router.use("/tokeCommand", channelValidator.name('chanName'));
|
router.use("/tokeCommand", channelValidator.name('chanName'));
|
||||||
|
router.use("/emote", channelValidator.name('chanName'));
|
||||||
|
|
||||||
//routing functions
|
//routing functions
|
||||||
//register
|
//register
|
||||||
|
|
@ -70,5 +76,9 @@ router.delete('/ban', channelModel.reqPermCheck("banUser"), accountValidator.u
|
||||||
router.get('/tokeCommand', channelModel.reqPermCheck("manageChannel"), tokeCommandController.get);
|
router.get('/tokeCommand', channelModel.reqPermCheck("manageChannel"), tokeCommandController.get);
|
||||||
router.post('/tokeCommand', tokebotValidator.command(), channelModel.reqPermCheck("editTokeCommands"), tokeCommandController.post);
|
router.post('/tokeCommand', tokebotValidator.command(), channelModel.reqPermCheck("editTokeCommands"), tokeCommandController.post);
|
||||||
router.delete('/tokeCommand', tokebotValidator.command(), channelModel.reqPermCheck("editTokeCommands"), tokeCommandController.delete);
|
router.delete('/tokeCommand', tokebotValidator.command(), channelModel.reqPermCheck("editTokeCommands"), tokeCommandController.delete);
|
||||||
|
//emote
|
||||||
|
router.get('/emote', channelModel.reqPermCheck("manageChannel"), emoteController.get);
|
||||||
|
router.post('/emote', channelModel.reqPermCheck("editEmotes"), emoteValidator.name('emoteName'), emoteValidator.link(), emoteController.post);
|
||||||
|
router.delete('/emote', channelModel.reqPermCheck("editEmotes"), emoteValidator.name('emoteName'), emoteController.delete);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
@ -76,6 +76,12 @@ const channelPermissionSchema = new mongoose.Schema({
|
||||||
default: "admin",
|
default: "admin",
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
editEmotes: {
|
||||||
|
type: mongoose.SchemaTypes.String,
|
||||||
|
enum: rankEnum,
|
||||||
|
default: "admin",
|
||||||
|
required: true
|
||||||
|
},
|
||||||
deleteChannel: {
|
deleteChannel: {
|
||||||
type: mongoose.SchemaTypes.String,
|
type: mongoose.SchemaTypes.String,
|
||||||
enum: rankEnum,
|
enum: rankEnum,
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ const server = require('../../server');
|
||||||
const statModel = require('../statSchema');
|
const statModel = require('../statSchema');
|
||||||
const {userModel} = require('../userSchema');
|
const {userModel} = require('../userSchema');
|
||||||
const permissionModel = require('../permissionSchema');
|
const permissionModel = require('../permissionSchema');
|
||||||
|
const emoteModel = require('../emoteSchema');
|
||||||
const channelPermissionSchema = require('./channelPermissionSchema');
|
const channelPermissionSchema = require('./channelPermissionSchema');
|
||||||
const channelBanSchema = require('./channelBanSchema');
|
const channelBanSchema = require('./channelBanSchema');
|
||||||
const { exceptionHandler, errorHandler } = require('../../utils/loggerUtils');
|
const { exceptionHandler, errorHandler } = require('../../utils/loggerUtils');
|
||||||
|
|
@ -76,6 +77,23 @@ const channelSchema = new mongoose.Schema({
|
||||||
type: mongoose.SchemaTypes.String,
|
type: mongoose.SchemaTypes.String,
|
||||||
required: true
|
required: true
|
||||||
}],
|
}],
|
||||||
|
//Not re-using the site-wide schema because post 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]
|
||||||
|
}
|
||||||
|
}],
|
||||||
//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
|
//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]
|
banList: [channelBanSchema]
|
||||||
});
|
});
|
||||||
|
|
@ -160,6 +178,17 @@ channelSchema.pre('save', async function (next){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -439,6 +468,24 @@ channelSchema.methods.checkBanByUserDoc = async function(userDB){
|
||||||
return foundBan;
|
return foundBan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
channelSchema.methods.getChanBans = async function(){
|
channelSchema.methods.getChanBans = async function(){
|
||||||
//Create an empty list to hold our found bans
|
//Create an empty list to hold our found bans
|
||||||
var banList = [];
|
var banList = [];
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ const emoteSchema = new mongoose.Schema({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//pre-save function
|
//post-save function
|
||||||
emoteSchema.post('save', async function (next){
|
emoteSchema.post('save', async function (next){
|
||||||
//broadcast updated emotes
|
//broadcast updated emotes
|
||||||
server.channelManager.broadcastSiteEmotes();
|
server.channelManager.broadcastSiteEmotes();
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
|
||||||
<%- include('partial/channelSettings/settings.ejs'); %>
|
<%- include('partial/channelSettings/settings.ejs'); %>
|
||||||
<%- include('partial/channelSettings/permList.ejs'); %>
|
<%- include('partial/channelSettings/permList.ejs'); %>
|
||||||
<%- include('partial/channelSettings/tokeCommandList.ejs'); %>
|
<%- include('partial/channelSettings/tokeCommandList.ejs'); %>
|
||||||
|
<%- include('partial/channelSettings/emoteList.ejs'); %>
|
||||||
</div>
|
</div>
|
||||||
<button href="javascript:" class="danger-button" id="chan-delete">Delete Channel</button>
|
<button href="javascript:" class="danger-button" id="chan-delete">Delete Channel</button>
|
||||||
<footer>
|
<footer>
|
||||||
|
|
|
||||||
28
src/views/partial/channelSettings/emoteList.ejs
Normal file
28
src/views/partial/channelSettings/emoteList.ejs
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
<!--Canopy - The next generation of stoner streaming software
|
||||||
|
Copyright (C) 2024 Rainbownapkin and the TTN Community
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.-->
|
||||||
|
<div id="emote-list-div" class="admin-list-div">
|
||||||
|
<h3>Emote List:</h3>
|
||||||
|
<div class="control-prompt">
|
||||||
|
<!-- Probably not the cleanest way to do this but fuggit -->
|
||||||
|
[<input placeholder="Emote Name..." id="new-emote-name-input" class="control-prompt">]
|
||||||
|
<button id="new-emote-button" class="positive-button">Add</button>
|
||||||
|
</div>
|
||||||
|
<div class="control-prompt">
|
||||||
|
<input placeholder="Emote Link..." id="new-emote-link-input" class="control-prompt">
|
||||||
|
</div>
|
||||||
|
<div class="dynamic-container" id="emote-list">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -155,8 +155,7 @@ p.panel-head-element{
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-entry-username{
|
.chat-entry-username{
|
||||||
margin: 0.2em;
|
margin: auto 0.2em auto 0;
|
||||||
margin-left: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-entry-body{
|
.chat-entry-body{
|
||||||
|
|
@ -169,8 +168,7 @@ p.panel-head-element{
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-entry-high-level{
|
.chat-entry-high-level{
|
||||||
margin: 0.2em;
|
margin: auto 0 auto 0.2em;
|
||||||
margin-right: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.high-level{
|
.high-level{
|
||||||
|
|
|
||||||
|
|
@ -539,8 +539,6 @@ class adminEmoteList{
|
||||||
//Strip name from element id
|
//Strip name from element id
|
||||||
const name = event.target.id.replace('emote-list-delete-','');
|
const name = event.target.id.replace('emote-list-delete-','');
|
||||||
|
|
||||||
console.log(name);
|
|
||||||
|
|
||||||
//Delete emote and pull list
|
//Delete emote and pull list
|
||||||
const list = await adminUtil.deleteEmote(name);
|
const list = await adminUtil.deleteEmote(name);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ class commandPreprocessor{
|
||||||
defineListeners(){
|
defineListeners(){
|
||||||
//When we receive site-wide emote list
|
//When we receive site-wide emote list
|
||||||
this.client.socket.on("siteEmotes", this.setSiteEmotes.bind(this));
|
this.client.socket.on("siteEmotes", this.setSiteEmotes.bind(this));
|
||||||
|
this.client.socket.on("chanEmotes", this.setChanEmotes.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
preprocess(command){
|
preprocess(command){
|
||||||
|
|
@ -53,23 +54,30 @@ class commandPreprocessor{
|
||||||
}
|
}
|
||||||
|
|
||||||
processEmotes(){
|
processEmotes(){
|
||||||
//Does checking each word for an emote use more or less cycles than running replace against each emote?
|
//inject invisible whitespace in-between emotes to prevent from mushing links together
|
||||||
//Not sure, but it's sure as fuck easier to write it that way lmao
|
this.message = this.message.replaceAll('][',']ㅤ[');
|
||||||
this.emotes.site.forEach((emote) => {
|
|
||||||
|
//For each list of emotes
|
||||||
|
Object.keys(this.emotes).forEach((key) => {
|
||||||
|
//For each emote in the current list
|
||||||
|
this.emotes[key].forEach((emote) => {
|
||||||
|
//Inject emote links into the message
|
||||||
this.message = this.message.replaceAll(`[${emote.name}]`, emote.link);
|
this.message = this.message.replaceAll(`[${emote.name}]`, emote.link);
|
||||||
});
|
});
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
processLinks(){
|
processLinks(){
|
||||||
//Strip out file seperators in-case the user is being a smart-ass
|
//Strip out file seperators in-case the user is being a smart-ass
|
||||||
this.message = this.message.replaceAll('␜','');
|
this.message = this.message.replaceAll('␜','');
|
||||||
//Split message by links
|
//Split message by links
|
||||||
var splitMessage = this.message.split(/(https?:\/\/[^\s]+)/g);
|
var splitMessage = this.message.split(/(https?:\/\/[^\sㅤ]+)/g);
|
||||||
//Create an empty array to hold links
|
//Create an empty array to hold links
|
||||||
this.links = [];
|
this.links = [];
|
||||||
|
|
||||||
splitMessage.forEach((chunk, chunkIndex) => {
|
splitMessage.forEach((chunk, chunkIndex) => {
|
||||||
if(chunk.match(/(https?:\/\/[^\s]+)/g)){
|
//For each chunk that is a link
|
||||||
|
if(chunk.match(/(https?:\/\/[^\sㅤ]+)/g)){
|
||||||
//I looked online for obscure characters that no one would use to prevent people from chatting embed placeholders
|
//I looked online for obscure characters that no one would use to prevent people from chatting embed placeholders
|
||||||
//Then I found this fucker, turns out it's literally made for the job lmao (even if it was originally intended for paper/magnetic tape)
|
//Then I found this fucker, turns out it's literally made for the job lmao (even if it was originally intended for paper/magnetic tape)
|
||||||
//Replace link with indexed placeholder
|
//Replace link with indexed placeholder
|
||||||
|
|
@ -92,6 +100,10 @@ class commandPreprocessor{
|
||||||
this.emotes.site = data;
|
this.emotes.site = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setChanEmotes(data){
|
||||||
|
this.emotes.chan = data;
|
||||||
|
}
|
||||||
|
|
||||||
getEmoteByLink(link){
|
getEmoteByLink(link){
|
||||||
//Create an empty variable to hold the found emote
|
//Create an empty variable to hold the found emote
|
||||||
var foundEmote = null;
|
var foundEmote = null;
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ class channelSettingsPage{
|
||||||
this.permList = new permList(this.channel);
|
this.permList = new permList(this.channel);
|
||||||
this.prefrenceList = new prefrenceList(this.channel);
|
this.prefrenceList = new prefrenceList(this.channel);
|
||||||
this.tokeCommandList = new tokeCommandList(this.channel);
|
this.tokeCommandList = new tokeCommandList(this.channel);
|
||||||
|
this.emoteList = new emoteList(this.channel);
|
||||||
this.deleteBtn = new deleteBtn(this.channel);
|
this.deleteBtn = new deleteBtn(this.channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -413,6 +414,124 @@ class tokeCommandList{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class emoteList{
|
||||||
|
constructor(channel){
|
||||||
|
this.channel = channel
|
||||||
|
this.linkPrompt = document.querySelector('#new-emote-link-input');
|
||||||
|
this.namePrompt = document.querySelector('#new-emote-name-input');
|
||||||
|
this.addButton = document.querySelector('#new-emote-button');
|
||||||
|
this.emoteList = document.querySelector('#emote-list');
|
||||||
|
|
||||||
|
//Setup input
|
||||||
|
this.setupInput();
|
||||||
|
|
||||||
|
//Pull and render emote list
|
||||||
|
this.updateList();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupInput(){
|
||||||
|
this.addButton.addEventListener('click', this.addEmote.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteEmote(event){
|
||||||
|
//Strip name from element id
|
||||||
|
const name = event.target.id.replace('emote-list-delete-','');
|
||||||
|
|
||||||
|
//Delete emote and pull list
|
||||||
|
const list = await utils.ajax.deleteChanEmote(this.channel, name);
|
||||||
|
|
||||||
|
//If we received a list
|
||||||
|
if(list != null){
|
||||||
|
//Pass updated liste to renderEmoteList function instead of pulling it twice
|
||||||
|
this.renderEmoteList(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async addEmote(event){
|
||||||
|
//Add emote to list and ingest returned updates list
|
||||||
|
const list = await utils.ajax.addChanEmote(this.channel, this.namePrompt.value, this.linkPrompt.value);
|
||||||
|
|
||||||
|
//If we received a list
|
||||||
|
if(list != null){
|
||||||
|
//Pass updated liste to renderEmoteList function instead of pulling it twice
|
||||||
|
this.renderEmoteList(list);
|
||||||
|
|
||||||
|
//Clear out the prompts
|
||||||
|
this.namePrompt.value = '';
|
||||||
|
this.linkPrompt.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateList(){
|
||||||
|
const list = await utils.ajax.getChanEmotes(this.channel);
|
||||||
|
this.renderEmoteList(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderEmoteList(list){
|
||||||
|
//Clear the current list
|
||||||
|
this.emoteList.innerHTML = "";
|
||||||
|
|
||||||
|
//For each emote in the list
|
||||||
|
list.forEach((emote) => {
|
||||||
|
//Create span to hold emote
|
||||||
|
const emoteDiv = document.createElement('div');
|
||||||
|
emoteDiv.classList.add('emote-list-emote');
|
||||||
|
|
||||||
|
//If the emote is an image
|
||||||
|
if(emote.type == 'image'){
|
||||||
|
//Create image node
|
||||||
|
var emoteMedia = document.createElement('img');
|
||||||
|
//if emote is a video
|
||||||
|
}else if(emote.type == 'video'){
|
||||||
|
//create video node
|
||||||
|
var emoteMedia = document.createElement('video');
|
||||||
|
//Set video properties
|
||||||
|
emoteMedia.autoplay = true;
|
||||||
|
emoteMedia.muted = true;
|
||||||
|
emoteMedia.controls = false;
|
||||||
|
emoteMedia.loop = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//set media link as source
|
||||||
|
emoteMedia.src = emote.link;
|
||||||
|
//Set media class
|
||||||
|
emoteMedia.classList.add('emote-list-media');
|
||||||
|
|
||||||
|
//Create title span
|
||||||
|
const titleSpan = document.createElement('span');
|
||||||
|
titleSpan.classList.add('emote-list-title');
|
||||||
|
|
||||||
|
//Create paragraph tag
|
||||||
|
const emoteTitle = document.createElement('p');
|
||||||
|
//Set title class
|
||||||
|
emoteTitle.classList.add('emote-list-title');
|
||||||
|
//Set emote title
|
||||||
|
emoteTitle.innerHTML = `[${emote.name}]`;
|
||||||
|
|
||||||
|
//Create delete icon
|
||||||
|
const deleteIcon = document.createElement('i');
|
||||||
|
//Set delete icon id and class
|
||||||
|
deleteIcon.classList.add('bi-trash-fill', 'emote-list-delete');
|
||||||
|
deleteIcon.id = `emote-list-delete-${emote.name}`;
|
||||||
|
//Add delete icon event listener
|
||||||
|
deleteIcon.addEventListener('click',this.deleteEmote.bind(this));
|
||||||
|
|
||||||
|
|
||||||
|
//Add the emote media to the emote span
|
||||||
|
emoteDiv.appendChild(emoteMedia);
|
||||||
|
//Add title paragraph node
|
||||||
|
titleSpan.appendChild(emoteTitle);
|
||||||
|
//Add trash icon node
|
||||||
|
titleSpan.appendChild(deleteIcon);
|
||||||
|
//Add title span
|
||||||
|
emoteDiv.appendChild(titleSpan);
|
||||||
|
|
||||||
|
//Append the mote span to the emote list
|
||||||
|
this.emoteList.appendChild(emoteDiv);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class deleteBtn{
|
class deleteBtn{
|
||||||
constructor(channel){
|
constructor(channel){
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
|
|
@ -444,12 +563,11 @@ class deleteAccountPopup{
|
||||||
}
|
}
|
||||||
|
|
||||||
setupInput(){
|
setupInput(){
|
||||||
this.prompt.addEventListener("keydown", this.nukeAccount.bind(this));
|
this.prompt.addEventListener("keydown", this.nukeChannel.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
async nukeAccount(event){
|
async nukeChannel(event){
|
||||||
if(event.key == "Enter"){
|
if(event.key == "Enter"){
|
||||||
console.log(this.channel);
|
|
||||||
if(this.channel === event.target.value){
|
if(this.channel === event.target.value){
|
||||||
await utils.ajax.deleteChannel(this.channel, event.target.value);
|
await utils.ajax.deleteChannel(this.channel, event.target.value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -561,6 +561,54 @@ class canopyAjaxUtils{
|
||||||
utils.ux.displayResponseError(await response.json());
|
utils.ux.displayResponseError(await response.json());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getChanEmotes(chanName){
|
||||||
|
var response = await fetch(`/api/channel/emote?chanName=${chanName}`,{
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(response.status == 200){
|
||||||
|
return await response.json();
|
||||||
|
}else{
|
||||||
|
utils.ux.displayResponseError(await response.json());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async addChanEmote(chanName, emoteName, link){
|
||||||
|
var response = await fetch('/api/channel/emote',{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({chanName, emoteName, link})
|
||||||
|
});
|
||||||
|
|
||||||
|
if(response.status == 200){
|
||||||
|
return await response.json();
|
||||||
|
}else{
|
||||||
|
utils.ux.displayResponseError(await response.json());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteChanEmote(chanName, emoteName){
|
||||||
|
var response = await fetch('/api/channel/emote',{
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({chanName, emoteName})
|
||||||
|
});
|
||||||
|
|
||||||
|
if(response.status == 200){
|
||||||
|
return await response.json();
|
||||||
|
}else{
|
||||||
|
utils.ux.displayResponseError(await response.json());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const utils = new canopyUtils()
|
const utils = new canopyUtils()
|
||||||
Loading…
Reference in a new issue