// statsHelper.js import { getRequestHeaders, callPopup, token } from "../script.js"; import { humanizeGenTime } from "./RossAscends-mods.js"; let charStats = {}; /** * Creates an HTML stat block. * * @param {string} statName - The name of the stat to be displayed. * @param {number|string} statValue - The value of the stat to be displayed. * @returns {string} - An HTML string representing the stat block. */ function createStatBlock(statName, statValue) { return `
${statName}:
${statValue}
`; } /** * Verifies and returns a numerical stat value. If the provided stat is not a number, returns 0. * * @param {number|string} stat - The stat value to be checked and returned. * @returns {number} - The stat value if it is a number, otherwise 0. */ function verifyStatValue(stat) { return isNaN(stat) ? 0 : stat; } /** * Calculates total stats from character statistics. * * @param {Object} charStats - Object containing character statistics. * @returns {Object} - Object containing total statistics. */ function calculateTotalStats() { let totalStats = { total_gen_time: 0, user_msg_count: 0, non_user_msg_count: 0, user_word_count: 0, non_user_word_count: 0, total_swipe_count: 0, }; for (let stats of Object.values(charStats)) { totalStats.total_gen_time += verifyStatValue(stats.total_gen_time); totalStats.user_msg_count += verifyStatValue(stats.user_msg_count); totalStats.non_user_msg_count += verifyStatValue( stats.non_user_msg_count ); totalStats.user_word_count += verifyStatValue(stats.user_word_count); totalStats.non_user_word_count += verifyStatValue( stats.non_user_word_count ); totalStats.total_swipe_count += verifyStatValue(stats.total_swipe_count); } return totalStats; } /** * Generates an HTML report of stats. * * @param {string} statsType - The type of stats (e.g., "User", "Character"). * @param {Object} stats - The stats data. */ function createHtml(statsType, stats) { console.log(stats); // Get time string let timeStirng = humanizeGenTime(stats.total_gen_time); let chatAge = "Never"; console.log(stats.date_first_chat, Date.now()); if(stats.date_first_chat < Date.now()) { console.log(moment.duration(stats.date_last_chat - stats.date_first_chat).humanize()); chatAge = moment.duration(stats.date_last_chat - stats.date_first_chat).humanize(); } // Create popup HTML with stats let html = `

${statsType} Stats

`; html += createStatBlock("Chat Age", chatAge); html += createStatBlock("Chat Time", timeStirng); html += createStatBlock("Total User Messages", stats.user_msg_count); html += createStatBlock( "Total Character Messages", stats.non_user_msg_count - stats.total_swipe_count ); html += createStatBlock("Total User Words", stats.user_word_count); html += createStatBlock("Total Character Words", stats.non_user_word_count); html += createStatBlock("Swipes", stats.total_swipe_count); callPopup(html, "text"); } /** * Handles the user stats by getting them from the server, calculating the total and generating the HTML report. * * @param {Object} charStats - Object containing character statistics. */ async function userStatsHandler() { // Get stats from server await getStats(); // Calculate total stats let totalStats = calculateTotalStats(charStats); // Create HTML with stats createHtml("User", totalStats); } /** * Handles the character stats by getting them from the server and generating the HTML report. * * @param {Object} charStats - Object containing character statistics. * @param {Object} characters - Object containing character data. * @param {string} this_chid - The character id. */ async function characterStatsHandler(characters, this_chid) { // Get stats from server await getStats(); console.log(charStats); // Get character stats let myStats = charStats[characters[this_chid].avatar]; if (myStats === undefined) { myStats = { total_gen_time: 0, user_msg_count: 0, non_user_msg_count: 0, user_word_count: 0, non_user_word_count: countWords(characters[this_chid].first_mes), total_swipe_count: 0, date_last_chat: 0, date_first_chat: new Date('9999-12-31T23:59:59.999Z').getTime(), }; charStats[characters[this_chid].avatar] = myStats; updateStats(); } // Create HTML with stats createHtml("Character", myStats); } /** * Fetches the character stats from the server. * * @param {Object} charStats - Object containing character statistics. * @returns {Object} - Object containing fetched character statistics. */ async function getStats() { const response = await fetch("/getstats", { method: "POST", headers: getRequestHeaders(), body: JSON.stringify({}), cache: "no-cache", }); if (!response.ok) { toastr.error("Stats could not be loaded. Try reloading the page."); throw new Error("Error getting stats"); } charStats = await response.json(); } /** * Calculates the generation time based on start and finish times. * * @param {string} gen_started - The start time in ISO 8601 format. * @param {string} gen_finished - The finish time in ISO 8601 format. * @returns {number} - The difference in time in milliseconds. */ function calculateGenTime(gen_started, gen_finished) { if (gen_started === undefined || gen_finished === undefined) { return 0; } let startDate = new Date(gen_started); let endDate = new Date(gen_finished); return endDate - startDate; } /** * Sends a POST request to the server to update the statistics. * * @param {Object} stats - The stats data to update. */ async function updateStats() { const response = await fetch("/updatestats", { method: "POST", headers: getRequestHeaders(), body: JSON.stringify(charStats), }); if (response.status !== 200) { console.error("Failed to update stats"); console.log(response).status; } } /** * Returns the count of words in the given string. * A word is a sequence of alphanumeric characters (including underscore). * * @param {string} str - The string to count words in. * @returns {number} - Number of words. */ function countWords(str) { const match = str.match(/\b\w+\b/g); return match ? match.length : 0; } /** * Handles stat processing for messages. * * @param {Object} line - Object containing message data. * @param {string} type - The type of the message processing (e.g., 'append', 'continue', 'appendFinal', 'swipe'). * @param {Object} characters - Object containing character data. * @param {string} this_chid - The character id. * @param {Object} charStats - Object containing character statistics. * @param {string} oldMesssage - The old message that's being processed. */ async function statMesProcess( line, type, characters, this_chid, oldMesssage ) { if (this_chid === undefined) { return; } await getStats(); let stat = charStats[characters[this_chid].avatar]; console.log(stat); stat.total_gen_time += calculateGenTime( line.gen_started, line.gen_finished ); if (line.is_user) { if (type != "append" && type != "continue" && type != "appendFinal") { stat.user_msg_count++; stat.user_word_count += countWords(line.mes); } else { let oldLen = oldMesssage.split(" ").length; stat.user_word_count += countWords(line.mes) - oldLen; } } else { // if continue, don't add a message, get the last message and subtract it from the word count of // the new message if (type != "append" && type != "continue" && type != "appendFinal") { stat.non_user_msg_count++; stat.non_user_word_count += countWords(line.mes); } else { let oldLen = oldMesssage.split(" ").length; stat.non_user_word_count += countWords(line.mes) - oldLen; } } if (type === "swipe") { stat.total_swipe_count++; } stat.date_last_chat = Date.now(); stat.first_chat_time = Math.min(stat.date_first_chat ?? new Date('9999-12-31T23:59:59.999Z').getTime(), Date.now()); console.log(stat); updateStats(); } export { userStatsHandler, characterStatsHandler, getStats, statMesProcess };