diff --git a/src/discord/commands/guild/d.admin.ts b/src/discord/commands/guild/d.admin.ts index 1d075ca6..7f93e2f6 100644 --- a/src/discord/commands/guild/d.admin.ts +++ b/src/discord/commands/guild/d.admin.ts @@ -6,6 +6,10 @@ import { } from 'discord.js'; import axios from 'axios'; import { stripIndents } from 'common-tags'; +import { + experience_category, experience_type, +} from '@prisma/client'; +import { findXPfromLevel } from '../../../global/utils/experience'; import { SlashCommand } from '../../@types/commandDef'; import commandContext from '../../utils/context'; import deployCommands from '../../utils/commandDeploy'; @@ -144,6 +148,72 @@ async function setStatus( await interaction.editReply(`Status set to ${statusType} ${status}`); } +async function overwriteUserData( + interaction: ChatInputCommandInteraction, +): Promise { + const member = interaction.options.getUser('user'); + if (!member) { + await interaction.editReply('Error: User not found.'); + return; + } + + const category = interaction.options.getString('category') as experience_category; + const type = interaction.options.getString('type') as experience_type; + const level = interaction.options.getInteger('level'); + + if (!category || !type || level === null) { + await interaction.editReply('Error: Missing category, type, or level.'); + return; + } + + const userData = await db.users.upsert({ + where: { + discord_id: member.id, + }, + create: { + discord_id: member.id, + }, + update: {}, + }); + + const experienceData = await db.user_experience.findFirst({ + where: { + user_id: userData.id, + category, + type, + }, + }); + + if (!experienceData) { + log.debug(F, `No experience data found for user ${userData.id} in category ${category} type ${type}.`); + await interaction.editReply('Error: No experience data found for the user.'); + return; + } + + const levelPoints = await findXPfromLevel(level); + log.debug(F, `Overwriting user data for user ${userData.id} in category ${category} type ${type} to level ${level} with ${levelPoints} XP points.`); + + try { + const result = await db.user_experience.updateMany({ + where: { + user_id: userData.id, + category, + type, + }, + data: { + level, + level_points: levelPoints, + total_points: levelPoints, + }, + }); + console.log(`Update result: ${JSON.stringify(result)}`); + } catch (error) { + console.error(`Error updating database: ${(error as Error).message}`); + } + + await interaction.editReply(`User level and points updated for category ${category} to level ${level} with ${levelPoints} points.`); +} + export const dAdmin: SlashCommand = { data: new SlashCommandBuilder() .setName('admin') @@ -187,12 +257,37 @@ export const dAdmin: SlashCommand = { .addStringOption(option => option .setName('url') .setDescription('The URL of the banner') - .setRequired(true))), + .setRequired(true))) + .addSubcommand(subcommand => subcommand + .setName('overwriteuserdata') + .setDescription('Overwrite user data') + .addUserOption(option => option.setName('user').setDescription('The user to update').setRequired(true)) + .addStringOption(option => option.setName('category') + .setDescription('The category to update') + .setRequired(true) + .addChoices( + { name: 'General', value: 'GENERAL' }, + { name: 'Tripsitter', value: 'TRIPSITTER' }, + { name: 'Developer', value: 'DEVELOPER' }, + { name: 'Team', value: 'TEAM' }, + { name: 'Ignored', value: 'IGNORED' }, + // Add more categories as needed + )) + .addStringOption(option => option.setName('type') + .setDescription('The type to update') + .setRequired(true) + .addChoices( + { name: 'Text', value: 'TEXT' }, + { name: 'Voice', value: 'VOICE' }, + // Add more types as needed + )) + .addIntegerOption(option => option.setName('level').setDescription('The level to set').setRequired(true))), + async execute(interaction) { if (!interaction.channel) return false; if (!interaction.guild) return false; log.info(F, await commandContext(interaction)); - const command = interaction.options.getSubcommand() as 'restart' | 'rebuild' | 'deploy' | 'setstatus' | 'setavatar' | 'setbanner'; + const command = interaction.options.getSubcommand() as 'restart' | 'rebuild' | 'deploy' | 'setstatus' | 'setavatar' | 'setbanner' | 'overwriteuserdata'; // By default we want to make the reply private await interaction.deferReply({ ephemeral: true }); // eslint-disable-next-line sonarjs/no-small-switch @@ -221,6 +316,10 @@ export const dAdmin: SlashCommand = { await setStatus(interaction, interaction.options.getString('prefix') as string, interaction.options.getString('status') as string); break; } + case 'overwriteuserdata': { + await overwriteUserData(interaction); + break; + } default: { log.debug(F, `default ${command}`); await interaction.editReply('Command not found'); diff --git a/src/global/utils/experience.ts b/src/global/utils/experience.ts index 92a41e6f..2ebb79d8 100644 --- a/src/global/utils/experience.ts +++ b/src/global/utils/experience.ts @@ -33,13 +33,24 @@ const announcementEmojis = [ '🎇', ]; -export async function expForNextLevel( - level:number, -):Promise { +export async function expForNextLevel(level: number): Promise { // This is a simple formula, making sure it's standardized across the system return 5 * (level ** 2) + (50 * level) + 100; } +export async function findXPfromLevel(level: number): Promise { + let totalXP = 0; + + const xpPromises = []; + for (let currentLevel = 1; currentLevel < level; currentLevel += 1) { + xpPromises.push(expForNextLevel(currentLevel)); + } + const xpResults = await Promise.all(xpPromises); + totalXP = xpResults.reduce((acc, xp) => acc + xp, 0); + + return totalXP; +} + export async function getTotalLevel( totalExp:number, ):Promise<{ level: number, level_points: number }> {