Source: schemas/emoteSchema.js

/*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);