feat: improve error handling and type safety in points modal processing
This commit is contained in:
@@ -83,8 +83,8 @@ export default {
|
|||||||
await interaction.showModal(modal);
|
await interaction.showModal(modal);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
//@ts-ignore
|
// @ts-ignore
|
||||||
logger.error('Error en ldManagePoints:', e);
|
logger.error('Error en ldManagePoints:', e as Error);
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
content: '❌ Error al abrir el modal de gestión.',
|
content: '❌ Error al abrir el modal de gestión.',
|
||||||
flags: MessageFlags.Ephemeral
|
flags: MessageFlags.Ephemeral
|
||||||
|
|||||||
@@ -3,10 +3,27 @@ import {
|
|||||||
ModalSubmitInteraction,
|
ModalSubmitInteraction,
|
||||||
MessageFlags,
|
MessageFlags,
|
||||||
PermissionFlagsBits,
|
PermissionFlagsBits,
|
||||||
EmbedBuilder
|
EmbedBuilder,
|
||||||
|
User,
|
||||||
|
Collection,
|
||||||
|
Snowflake
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import { prisma } from '../../core/database/prisma';
|
import { prisma } from '../../core/database/prisma';
|
||||||
|
|
||||||
|
interface UserSelectComponent {
|
||||||
|
custom_id: string;
|
||||||
|
type: number;
|
||||||
|
values: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ComponentData {
|
||||||
|
components?: ComponentData[];
|
||||||
|
component?: ComponentData;
|
||||||
|
custom_id?: string;
|
||||||
|
type?: number;
|
||||||
|
values?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
customId: 'ld_points_modal',
|
customId: 'ld_points_modal',
|
||||||
run: async (interaction: ModalSubmitInteraction) => {
|
run: async (interaction: ModalSubmitInteraction) => {
|
||||||
@@ -30,15 +47,16 @@ export default {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Obtener valores del modal con manejo seguro de errores
|
// Obtener valores del modal con manejo seguro de errores
|
||||||
let totalInput: string;
|
let totalInput: string = '';
|
||||||
let selectedUsers: any;
|
let selectedUsers: ReturnType<typeof interaction.components.getSelectedUsers> = null;
|
||||||
let userId: string;
|
let userId: string | undefined = undefined;
|
||||||
let userName: string;
|
let userName: string | undefined = undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
totalInput = interaction.components.getTextInputValue('points_input').trim();
|
totalInput = interaction.components.getTextInputValue('points_input').trim();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error obteniendo points_input:', error);
|
// @ts-ignore
|
||||||
|
logger.error('Error obteniendo points_input:', String(error));
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
content: '❌ Error al obtener el valor de puntos del modal.',
|
content: '❌ Error al obtener el valor de puntos del modal.',
|
||||||
flags: MessageFlags.Ephemeral
|
flags: MessageFlags.Ephemeral
|
||||||
@@ -51,10 +69,10 @@ export default {
|
|||||||
|
|
||||||
if (!selectedUsers || selectedUsers.size === 0) {
|
if (!selectedUsers || selectedUsers.size === 0) {
|
||||||
// Fallback: intentar obtener los IDs directamente de los datos raw
|
// Fallback: intentar obtener los IDs directamente de los datos raw
|
||||||
const rawData = (interaction as any).data?.components;
|
const rawData = (interaction as any).data?.components as ComponentData[] | undefined;
|
||||||
if (rawData) {
|
if (rawData) {
|
||||||
const userSelectComponent = this.findUserSelectComponent(rawData, 'user_select');
|
const userSelectComponent = findUserSelectComponent(rawData, 'user_select');
|
||||||
if (userSelectComponent?.values?.length > 0) {
|
if (userSelectComponent?.values?.length && userSelectComponent.values.length > 0) {
|
||||||
userId = userSelectComponent.values[0];
|
userId = userSelectComponent.values[0];
|
||||||
logger.info(`🔄 Fallback: UserId extraído de datos raw: ${userId}`);
|
logger.info(`🔄 Fallback: UserId extraído de datos raw: ${userId}`);
|
||||||
}
|
}
|
||||||
@@ -67,26 +85,30 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const selectedUser = Array.from(selectedUsers.values())[0];
|
const selectedUser = Array.from(selectedUsers.values())[0] as User;
|
||||||
userId = selectedUser?.id;
|
if (selectedUser) {
|
||||||
userName = selectedUser?.tag ?? selectedUser?.username ?? userId;
|
userId = selectedUser.id;
|
||||||
|
userName = selectedUser.tag ?? selectedUser.username ?? userId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error procesando UserSelect, intentando fallback:', error);
|
// @ts-ignore
|
||||||
|
logger.error('Error procesando UserSelect, intentando fallback:', String(error));
|
||||||
|
|
||||||
// Fallback más agresivo: obtener directamente de los datos raw
|
// Fallback más agresivo: obtener directamente de los datos raw
|
||||||
try {
|
try {
|
||||||
const rawData = (interaction as any).data?.components;
|
const rawData = (interaction as any).data?.components as ComponentData[] | undefined;
|
||||||
const userSelectComponent = this.findUserSelectComponent(rawData, 'user_select');
|
const userSelectComponent = findUserSelectComponent(rawData, 'user_select');
|
||||||
|
|
||||||
if (userSelectComponent?.values?.length > 0) {
|
if (userSelectComponent?.values?.length && userSelectComponent.values.length > 0) {
|
||||||
userId = userSelectComponent.values[0];
|
userId = userSelectComponent.values[0];
|
||||||
logger.info(`🔄 Fallback agresivo: UserId extraído: ${userId}`);
|
logger.info(`🔄 Fallback agresivo: UserId extraído: ${userId}`);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('No se pudo extraer userId de los datos raw');
|
throw new Error('No se pudo extraer userId de los datos raw');
|
||||||
}
|
}
|
||||||
} catch (fallbackError) {
|
} catch (fallbackError) {
|
||||||
logger.error('Falló el fallback:', fallbackError);
|
// @ts-ignore
|
||||||
|
logger.error('Falló el fallback:', String(fallbackError));
|
||||||
return interaction.reply({
|
return interaction.reply({
|
||||||
content: '❌ Error procesando la selección de usuario. Inténtalo de nuevo.',
|
content: '❌ Error procesando la selección de usuario. Inténtalo de nuevo.',
|
||||||
flags: MessageFlags.Ephemeral
|
flags: MessageFlags.Ephemeral
|
||||||
@@ -117,7 +139,8 @@ export default {
|
|||||||
const targetMember = await interaction.guild.members.fetch(userId);
|
const targetMember = await interaction.guild.members.fetch(userId);
|
||||||
userName = targetMember.displayName || targetMember.user.username;
|
userName = targetMember.displayName || targetMember.user.username;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn(`No se pudo obtener info del usuario ${userId}:`, error);
|
// @ts-ignore
|
||||||
|
logger.warn(`No se pudo obtener info del usuario ${userId}:`, String(error));
|
||||||
userName = `Usuario ${userId}`;
|
userName = `Usuario ${userId}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -273,8 +296,9 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
//@ts-ignore
|
// @ts-ignore
|
||||||
logger.error('❌ Error en ldPointsModal:', error);
|
// @ts-ignore
|
||||||
|
logger.error('❌ Error en ldPointsModal:', String(error));
|
||||||
|
|
||||||
if (!interaction.replied && !interaction.deferred) {
|
if (!interaction.replied && !interaction.deferred) {
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
@@ -283,31 +307,31 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
// Función auxiliar para buscar componentes UserSelect en datos raw
|
|
||||||
findUserSelectComponent(components: any[], customId: string): any {
|
|
||||||
if (!components) return null;
|
|
||||||
|
|
||||||
for (const comp of components) {
|
|
||||||
if (comp.components) {
|
|
||||||
const found = this.findUserSelectComponent(comp.components, customId);
|
|
||||||
if (found) return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (comp.component) {
|
|
||||||
if (comp.component.custom_id === customId) {
|
|
||||||
return comp.component;
|
|
||||||
}
|
|
||||||
const found = this.findUserSelectComponent([comp.component], customId);
|
|
||||||
if (found) return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (comp.custom_id === customId) {
|
|
||||||
return comp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Función auxiliar para buscar componentes UserSelect en datos raw
|
||||||
|
function findUserSelectComponent(components: ComponentData[] | undefined, customId: string): UserSelectComponent | null {
|
||||||
|
if (!components) return null;
|
||||||
|
|
||||||
|
for (const comp of components) {
|
||||||
|
if (comp.components) {
|
||||||
|
const found = findUserSelectComponent(comp.components, customId);
|
||||||
|
if (found) return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comp.component) {
|
||||||
|
if (comp.component.custom_id === customId) {
|
||||||
|
return comp.component as UserSelectComponent;
|
||||||
|
}
|
||||||
|
const found = findUserSelectComponent([comp.component], customId);
|
||||||
|
if (found) return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comp.custom_id === customId) {
|
||||||
|
return comp as UserSelectComponent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,17 +8,13 @@ bot.on(Events.ClientReady, () => {
|
|||||||
// ============================================
|
// ============================================
|
||||||
// 🛡️ HANDLER GLOBAL PARA ERRORES DE DISCORD.JS
|
// 🛡️ HANDLER GLOBAL PARA ERRORES DE DISCORD.JS
|
||||||
// ============================================
|
// ============================================
|
||||||
// Interceptar errores específicos de ModalSubmitInteraction con UserSelect
|
|
||||||
|
|
||||||
process.on('uncaughtException', (error) => {
|
process.on('uncaughtException', (error: Error) => {
|
||||||
// Interceptar errores específicos de Discord.js GuildMemberManager
|
// Interceptar errores específicos de Discord.js GuildMemberManager
|
||||||
if (error.message?.includes("Cannot read properties of undefined (reading 'id')") &&
|
if (error.message?.includes("Cannot read properties of undefined (reading 'id')") &&
|
||||||
error.stack?.includes('GuildMemberManager._add')) {
|
error.stack?.includes('GuildMemberManager._add')) {
|
||||||
|
|
||||||
logger.warn('🔧 Discord.js bug interceptado: GuildMemberManager error con UserSelect en modal');
|
logger.warn('🔧 Discord.js bug interceptado: GuildMemberManager error con UserSelect en modal');
|
||||||
// @ts-ignore
|
|
||||||
logger.warn('Stack trace:', error.stack);
|
|
||||||
|
|
||||||
// NO terminar el proceso, solo logear el error
|
// NO terminar el proceso, solo logear el error
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -29,24 +25,23 @@ bot.on(Events.ClientReady, () => {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('unhandledRejection', (reason, promise) => {
|
process.on('unhandledRejection', (reason: unknown) => {
|
||||||
// Interceptar rechazos relacionados con el mismo bug
|
// Interceptar rechazos relacionados con el mismo bug
|
||||||
if (reason && typeof reason === 'object' &&
|
if (reason &&
|
||||||
|
typeof reason === 'object' &&
|
||||||
|
reason !== null &&
|
||||||
'message' in reason &&
|
'message' in reason &&
|
||||||
// @ts-ignore
|
typeof (reason as any).message === 'string' &&
|
||||||
reason.message?.includes("Cannot read properties of undefined (reading 'id')")) {
|
(reason as any).message.includes("Cannot read properties of undefined (reading 'id')")) {
|
||||||
|
|
||||||
logger.warn('🔧 Discord.js promise rejection interceptada: GuildMemberManager error');
|
logger.warn('🔧 Discord.js promise rejection interceptada: GuildMemberManager error');
|
||||||
// @ts-ignore
|
|
||||||
logger.warn('Reason:', reason);
|
|
||||||
|
|
||||||
// NO terminar el proceso
|
// NO terminar el proceso
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Para otras promesas rechazadas, logear pero continuar
|
// Para otras promesas rechazadas, logear pero continuar
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
logger.error('🚨 UnhandledRejection:', reason);
|
logger.error('🚨 UnhandledRejection:', reason as Error);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
|
|||||||
Reference in New Issue
Block a user