Files
amayo/src/components/modals/ldPointsModal.ts
2025-12-01 18:59:48 +00:00

287 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import logger from "../../core/lib/logger";
import {
ModalSubmitInteraction,
MessageFlags,
EmbedBuilder,
} from "discord.js";
import { prisma } from "../../core/database/prisma";
import { hasManageGuildOrStaff } from "../../core/lib/permissions";
export default {
customId: "ld_points_modal",
run: async (interaction: ModalSubmitInteraction) => {
logger.info(
`🔍 Modal ldPointsModal ejecutado. CustomId: ${interaction.customId}`
);
if (!interaction.guild) {
return interaction.reply({
content: "❌ Solo disponible en servidores.",
flags: MessageFlags.Ephemeral,
});
}
// Verificar permisos (ManageGuild o rol staff)
const member = await interaction.guild.members.fetch(interaction.user.id);
const allowed = await hasManageGuildOrStaff(
member,
interaction.guild.id,
prisma
);
if (!allowed) {
return interaction.reply({
content: "❌ Solo admins o staff pueden gestionar puntos.",
flags: MessageFlags.Ephemeral,
});
}
try {
// Read values from modal text inputs
let userId: string = "";
let userName: string = "";
let totalInput: string = "";
try {
// Get user ID from text input
userId = interaction.fields.getTextInputValue("user_id_input");
// Get points modification from text input
totalInput = interaction.fields.getTextInputValue("points_input");
logger.info(`🔍 Input recibido - UserId: ${userId}, Puntos: ${totalInput}`);
} catch (error) {
logger.error({ error }, "Error al leer campos del modal");
return interaction.reply({
content: "❌ Error al leer los campos del formulario.",
flags: MessageFlags.Ephemeral,
});
}
if (!totalInput) {
return interaction.reply({
content: "❌ Debes ingresar un valor para modificar.",
flags: MessageFlags.Ephemeral,
});
}
if (!userId) {
return interaction.reply({
content: "❌ Debes ingresar el ID del usuario.",
flags: MessageFlags.Ephemeral,
});
}
// Validar que el userId sea un número válido
if (!/^\d{17,20}$/.test(userId)) {
return interaction.reply({
content: "❌ El ID del usuario debe ser un número de 17-20 dígitos.",
flags: MessageFlags.Ephemeral,
});
}
// Intentar obtener el nombre del usuario
try {
const targetMember = await interaction.guild.members.fetch(userId);
userName = targetMember.displayName || targetMember.user.username;
} catch (error) {
logger.warn(
{ error },
`No se pudo obtener info del usuario ${userId}`
);
userName = `Usuario ${userId}`;
}
// ✅ ARREGLO: Asegurar que el User exista en la base de datos antes de crear PartnershipStats
await prisma.user.upsert({
where: { id: userId },
update: {}, // No actualizar nada si ya existe
create: { id: userId }, // Crear si no existe
});
logger.info(`✅ User verificado/creado en BD: ${userId}`);
// Obtener o crear el registro de stats del usuario
let stats = await prisma.partnershipStats.findUnique({
where: {
userId_guildId: {
userId,
guildId: interaction.guild.id,
},
},
});
if (!stats) {
logger.info(
`🔍 Creando nuevo registro de stats para userId: ${userId}`
);
// Crear nuevo registro si no existe
stats = await prisma.partnershipStats.create({
data: {
userId,
guildId: interaction.guild.id,
totalPoints: 0,
weeklyPoints: 0,
monthlyPoints: 0,
},
});
}
logger.info(
`🔍 Stats actuales - Total: ${stats.totalPoints}, Semanal: ${stats.weeklyPoints}, Mensual: ${stats.monthlyPoints}`
);
// Función para parsear el input y calcular el nuevo valor
const calculateNewValue = (
input: string,
currentValue: number
): number => {
const firstChar = input[0];
if (firstChar === "+") {
// Añadir puntos
const numValue = parseInt(input.substring(1)) || 0;
return Math.max(0, currentValue + numValue);
} else if (firstChar === "-") {
// Quitar puntos (los últimos N puntos añadidos)
const numValue = parseInt(input.substring(1)) || 0;
return Math.max(0, currentValue - numValue);
} else if (firstChar === "=") {
// Establecer valor absoluto
const numValue = parseInt(input.substring(1)) || 0;
return Math.max(0, numValue);
} else {
// Si no tiene símbolo, tratar como valor absoluto
const parsedValue = parseInt(input);
return isNaN(parsedValue) ? currentValue : Math.max(0, parsedValue);
}
};
// Calcular nuevo valor de puntos totales
const newTotalPoints = calculateNewValue(totalInput, stats.totalPoints);
const totalDifference = newTotalPoints - stats.totalPoints;
logger.info(
`🔍 Nuevo total calculado: ${newTotalPoints} (diferencia: ${totalDifference})`
);
// Calcular nuevos puntos semanales y mensuales
let newWeeklyPoints = stats.weeklyPoints;
let newMonthlyPoints = stats.monthlyPoints;
if (totalInput[0] === "+") {
// Si añadimos puntos, sumar a semanal y mensual también
const pointsToAdd = parseInt(totalInput.substring(1)) || 0;
newWeeklyPoints = stats.weeklyPoints + pointsToAdd;
newMonthlyPoints = stats.monthlyPoints + pointsToAdd;
logger.info(
` Añadiendo ${pointsToAdd} puntos a todas las categorías`
);
} else if (totalInput[0] === "-") {
// Si quitamos puntos, restar proporcionalmente de semanal y mensual
const pointsToRemove = parseInt(totalInput.substring(1)) || 0;
newWeeklyPoints = Math.max(0, stats.weeklyPoints - pointsToRemove);
newMonthlyPoints = Math.max(0, stats.monthlyPoints - pointsToRemove);
logger.info(
` Quitando ${pointsToRemove} puntos de todas las categorías`
);
} else if (totalInput[0] === "=") {
// Si establecemos un valor absoluto, ajustar semanal y mensual proporcionalmente
const targetTotal = parseInt(totalInput.substring(1)) || 0;
if (stats.totalPoints > 0) {
// Calcular el ratio y aplicarlo
const ratio = targetTotal / stats.totalPoints;
newWeeklyPoints = Math.round(stats.weeklyPoints * ratio);
newMonthlyPoints = Math.round(stats.monthlyPoints * ratio);
} else {
// Si no había puntos antes, establecer todo a 0
newWeeklyPoints = 0;
newMonthlyPoints = 0;
}
logger.info(
`🎯 Estableciendo total a ${targetTotal} y ajustando proporcionalmente`
);
}
// Asegurar que semanal no exceda mensual, y mensual no exceda total
newWeeklyPoints = Math.min(
newWeeklyPoints,
newMonthlyPoints,
newTotalPoints
);
newMonthlyPoints = Math.min(newMonthlyPoints, newTotalPoints);
logger.info(
`🔍 Nuevos valores calculados - Total: ${newTotalPoints}, Semanal: ${newWeeklyPoints}, Mensual: ${newMonthlyPoints}`
);
// Actualizar en base de datos (todos los puntos)
await prisma.partnershipStats.update({
where: {
userId_guildId: {
userId,
guildId: interaction.guild.id,
},
},
data: {
totalPoints: newTotalPoints,
weeklyPoints: newWeeklyPoints,
monthlyPoints: newMonthlyPoints,
},
});
logger.info(`✅ Puntos actualizados exitosamente en la base de datos`);
// Calcular las diferencias
const totalDiff = newTotalPoints - stats.totalPoints;
const weeklyDiff = newWeeklyPoints - stats.weeklyPoints;
const monthlyDiff = newMonthlyPoints - stats.monthlyPoints;
const totalDiffText = totalDiff > 0 ? `+${totalDiff}` : `${totalDiff}`;
const weeklyDiffText =
weeklyDiff > 0 ? `+${weeklyDiff}` : `${weeklyDiff}`;
const monthlyDiffText =
monthlyDiff > 0 ? `+${monthlyDiff}` : `${monthlyDiff}`;
// Crear embed de confirmación
const embed = new EmbedBuilder()
.setColor(totalDiff >= 0 ? 0x00ff00 : 0xff9900)
.setTitle("✅ Puntos Actualizados")
.setDescription(`Se han actualizado los puntos de **${userName}**`)
.addFields(
{
name: "📊 Puntos Totales",
value: `${stats.totalPoints} → **${newTotalPoints}** (${totalDiffText})`,
inline: true,
},
{
name: "🗓️ Puntos Mensuales",
value: `${stats.monthlyPoints} → **${newMonthlyPoints}** (${monthlyDiffText})`,
inline: true,
},
{
name: "📅 Puntos Semanales",
value: `${stats.weeklyPoints} → **${newWeeklyPoints}** (${weeklyDiffText})`,
inline: true,
}
)
.setFooter({ text: `Actualizado por ${interaction.user.username}` })
.setTimestamp();
await interaction.reply({
embeds: [embed],
flags: MessageFlags.Ephemeral,
});
} catch (error) {
logger.error({ error }, "❌ Error en ldPointsModal");
if (!interaction.replied && !interaction.deferred) {
await interaction.reply({
content:
"❌ Error interno al procesar la solicitud. Revisa los logs para más detalles.",
flags: MessageFlags.Ephemeral,
});
}
}
},
};