287 lines
10 KiB
TypeScript
287 lines
10 KiB
TypeScript
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,
|
||
});
|
||
}
|
||
}
|
||
},
|
||
};
|