const { SlashCommandBuilder, PermissionsBitField } = require('discord.js'); // Helper function to parse user from mention or ID function parseUser(input, guild) { // Check if it's a mention <@123456> or <@!123456> const mentionMatch = input.match(/^<@!?(\d+)>$/); if (mentionMatch) { return guild.members.cache.get(mentionMatch[1])?.user; } // Check if it's a user ID if (/^\d{15,20}$/.test(input)) { return guild.members.cache.get(input)?.user; } // Try to find by username or global name const member = guild.members.cache.find(m => (m.user.global_name && m.user.global_name.toLowerCase().includes(input.toLowerCase())) || m.user.username.toLowerCase().includes(input.toLowerCase()) || (m.user.global_name && m.user.global_name.toLowerCase() === input.toLowerCase()) || m.user.username.toLowerCase() === input.toLowerCase() ); return member?.user; } // Helper function to log moderation actions async function logModerationAction(guildId, action, targetUserId, targetUsername, moderatorUserId, moderatorUsername, reason, duration = null, endDate = null) { try { const logData = { guildId, action, targetUserId, targetUsername, moderatorUserId, moderatorUsername, reason, duration, endDate }; const response = await fetch(`${process.env.BACKEND_BASE || 'http://localhost:3001'}/internal/log-moderation`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(logData) }); if (!response.ok) { console.error('Failed to log moderation action:', response.statusText); } } catch (error) { console.error('Error logging moderation action:', error); } } module.exports = { name: 'timeout', description: 'Timeout a user in the server', enabled: true, builder: new SlashCommandBuilder() .setName('timeout') .setDescription('Timeout a user in the server') .addStringOption(option => option.setName('user') .setDescription('The user to timeout (mention or user ID)') .setRequired(true)) .addIntegerOption(option => option.setName('duration') .setDescription('Duration in minutes (max 40320 minutes = 28 days)') .setRequired(true) .setMinValue(1) .setMaxValue(40320)) .addStringOption(option => option.setName('reason') .setDescription('Reason for the timeout (minimum 3 words)') .setRequired(true)), async execute(interaction) { // Check if user has moderate members permissions (required for timeout) if (!interaction.member.permissions.has(PermissionsBitField.Flags.ModerateMembers)) { return await interaction.reply({ content: 'You do not have permission to timeout members.', flags: 64 }); } // Check if bot has moderate members permissions if (!interaction.guild.members.me.permissions.has(PermissionsBitField.Flags.ModerateMembers)) { return await interaction.reply({ content: 'I do not have permission to timeout members.', flags: 64 }); } const userInput = interaction.options.getString('user'); const duration = interaction.options.getInteger('duration'); const reason = interaction.options.getString('reason'); // Parse the user from the input const user = parseUser(userInput, interaction.guild); if (!user) { return await interaction.reply({ content: 'Could not find that user. Please provide a valid user mention or user ID.', flags: 64 }); } // Validate reason has at least 3 words const reasonWords = reason.trim().split(/\s+/); if (reasonWords.length < 3) { return await interaction.reply({ content: 'Reason must be at least 3 words long.', flags: 64 }); } // Cannot timeout yourself if (user.id === interaction.user.id) { return await interaction.reply({ content: 'You cannot timeout yourself.', flags: 64 }); } // Cannot timeout the bot if (user.id === interaction.guild.members.me.id) { return await interaction.reply({ content: 'I cannot timeout myself.', flags: 64 }); } // Check if user is in the server const member = interaction.guild.members.cache.get(user.id); if (!member) { return await interaction.reply({ content: 'That user is not in this server.', flags: 64 }); } // Check role hierarchy if (member.roles.highest.position >= interaction.member.roles.highest.position && interaction.user.id !== interaction.guild.ownerId) { return await interaction.reply({ content: 'You cannot timeout a member with a higher or equal role.', flags: 64 }); } if (member.roles.highest.position >= interaction.guild.members.me.roles.highest.position) { return await interaction.reply({ content: 'I cannot timeout a member with a higher or equal role.', flags: 64 }); } // Check if user is already timed out if (member.communicationDisabledUntil) { return await interaction.reply({ content: 'This user is already timed out.', flags: 64 }); } try { const timeoutDuration = duration * 60 * 1000; // Convert minutes to milliseconds const timeoutUntil = new Date(Date.now() + timeoutDuration); await member.timeout(timeoutDuration, reason); await interaction.reply({ content: `Successfully timed out ${user.global_name || user.username} for ${duration} minutes. Reason: ${reason}`, flags: 64 }); // Log the action const durationString = duration >= 1440 ? `${Math.floor(duration / 1440)}d ${Math.floor((duration % 1440) / 60)}h ${duration % 60}m` : duration >= 60 ? `${Math.floor(duration / 60)}h ${duration % 60}m` : `${duration}m`; await logModerationAction(interaction.guildId, 'timeout', user.id, user.global_name || user.username, interaction.user.id, interaction.user.global_name || interaction.user.username, reason, durationString, timeoutUntil); } catch (error) { console.error('Error timing out user:', error); await interaction.reply({ content: 'Failed to timeout the user. Please try again.', flags: 64 }); } } };