/*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 .*/ //NPM Imports const {mongoose} = require('mongoose'); //Local Imports const config = require('./../../../config.json'); const loggerUtils = require('../../utils/loggerUtils'); /** * DB Schema for single document for keeping track of a single toke */ const tokeSchema = new mongoose.Schema({ toke: { type: mongoose.SchemaTypes.Map, required: true, default: new Map() }, date: { type: mongoose.SchemaTypes.Date, required: true, default: new Date() } }); //statics /** * Cached map containing counts of individual toke commands */ tokeSchema.statics.tokeMap = new Map(); /** * Cached number of total tokes */ tokeSchema.statics.count = 0; /** * Cached number of times a user has successfully ran a '!toke' command * Not to be confused with tokeSchema.statics.count, which counts total amount of tokes called out */ tokeSchema.statics.commandCount = 0; /** * Calculates cached toke map from existing */ tokeSchema.statics.calculateTokeMap = async function(){ //Pull full toke collection const tokes = await this.find(); //Drop existing toke map this.tokeMap = new Map(); //Iterate through DB of tokes for(const toke of tokes){ //Increment toke count this.count++; //For each command callout for(const command of toke.toke){ //Increment Command Count this.commandCount++; //Pull current count of respective toke command let curCount = this.tokeMap.get(command[1]); //If this is an unset toke command if(curCount == null){ //Set it to one this.tokeMap.set(command[1], 1); //Otherwise }else{ //Increment the existing count this.tokeMap.set(command[1], ++curCount); } } } //Display calculated toke sats for funsies if(config.verbose){ console.log(`Processed ${this.commandCount} toke command callouts accross ${await this.estimatedDocumentCount()} tokes.`); } } /** * Tattoos toke into toke DB colleciton * * We use this instead of a pre-save function as we need to fuck w/ statics */ tokeSchema.statics.tattooToke = async function(toke){ //Write toke to DB await this.create({toke}); //Increment RAM-backed toke count this.count++; //Iterate through tokers for(const curToke of toke){ //Pull current toke count let curCount = this.tokeMap.get(curToke[1]); //If this command hasn't been counted if(curCount == null){ //Set new command count to one this.tokeMap.set(curToke[1], 1); }else{ //Increment current toke count and commit it to the RAM-backed tokeMap this.tokeMap.set(curToke[1], ++curCount); } //Increment RAM-Backed command count this.commandCount++; } } /** * Ingests legacy tokes handed over by the migration model * @param {Array} rawLegacyTokes - List of strings containing contents of legacy cytube/fore.st toke logs */ tokeSchema.statics.ingestLegacyTokes = async function(rawLegacyTokes){ //If migration is disabled if(!config.migrate){ //BAIL! return; } try{ //For each toke log for(const tokeLog of rawLegacyTokes){ //Split and iterate toke log by new line for(const tokeLine of tokeLog.split('\n')){ //Ensure line is a valid toke log line (this will break if your tokes take place after 12:46:40PM on Nov 20th 2286... Or before 21:46:40 Sep 08 2001) //You'll probably want to have migrated from cytube/fore.st to canopy by then :) //Also splits tokers array off for easier processing const splitToke = tokeLine.match(/^\[.+\]|,[0-9]{1,4},|[0-9]{13}$/g) if(splitToke != null){ //Create empty tokers map const toke = new Map(); //Add qoutes around strings in the tokers line let tokersLine = splitToke[0].replaceAll('[', '["'); tokersLine = tokersLine.replaceAll(']','"]'); tokersLine = tokersLine.replaceAll(',','","'); //Force feed doctored line into the JSON parser, and iterate by the array it shits out for(const toker of JSON.parse(tokersLine)){ toke.set(toker,"Legacy Tokes"); } const date = new Date(Number(splitToke[2])); //Push toke on to statDB this.create({ toke, date }); console.log(`Adding legacy toke: ${tokersLine} from: ${date.toLocaleString()}`); } } } console.log("Legacy tokes commited to server-wide database statistics file!"); }catch(err){ return loggerUtils.localExceptionHandler(err); } } tokeSchema.statics.dropLegacyTokes = async function(){ try{ //If legacy toke dropping is disabled or migration is enabled if(!config.dropLegacyTokes || config.migrate){ //return return; } //Pull tokes from DB const oldTokes = await this.find(); //Create temporary toke array const tokes = []; //Nuke the toke collection await this.deleteMany({}); //Iterate through server toke history for(const toke of oldTokes){ //If it's not a legacy toke or a dupe if(Array.from(toke.toke)[0][1] != "Legacy Tokes"){ //Re-add it to the database, scraping out the old ID this.create({ toke: toke.toke, date: toke.date }); } } //Tell of our success console.log("Removed migration tokes!"); }catch(err){ return loggerUtils.localExceptionHandler(err); } } module.exports = mongoose.model("toke", tokeSchema);