Skip to content

Commit

Permalink
add dm
Browse files Browse the repository at this point in the history
  • Loading branch information
GrishMahat committed Jul 9, 2024
1 parent 647d2fd commit d825a95
Showing 1 changed file with 200 additions and 0 deletions.
200 changes: 200 additions & 0 deletions src/commands/developer/dm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import {
SlashCommandBuilder,
PermissionFlagsBits,
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
} from 'discord.js';


export default {
data: new SlashCommandBuilder()
.setName('dm')
.setDescription('Send a direct message to a user or role')
.addSubcommand(subcommand =>
subcommand
.setName('user')
.setDescription('Send a DM to a specific user')
.addUserOption(option =>
option.setName('target')
.setDescription('The user to send the DM to')
.setRequired(true))
.addStringOption(option =>
option.setName('message')
.setDescription('The message to send (Use {user} for recipient\'s name)')
.setRequired(true))
)
.addSubcommand(subcommand =>
subcommand
.setName('role')
.setDescription('Send a DM to all users with a specific role')
.addRoleOption(option =>
option.setName('target')
.setDescription('The role to send the DM to')
.setRequired(true))
.addStringOption(option =>
option.setName('message')
.setDescription('The message to send (Use {user} for recipient\'s name)')
.setRequired(true))
).toJSON(),


userPermissions: [PermissionFlagsBits.ManageMessages],
botPermissions: [PermissionFlagsBits.SendMessages],
cooldown: 5,
nwfwMode: false,
testMode: false,
devOnly: true,

run: async (client, interaction) => {
const subcommand = interaction.options.getSubcommand();
let message = interaction.options.getString('message').trim();
const sendMessage = async (user) => {
if (!user) {
return { success: false, reason: 'USER_NOT_FOUND' };
}

try {
const personalizedMessage = message.replace(/{user}/g, user.displayName);
await user.send(personalizedMessage);
return { success: true };
} catch (error) {
if (error.code === 50007) {
return { success: false, reason: 'DM_CLOSED' };
} else if (error.code === 10013) {
return { success: false, reason: 'USER_NOT_FOUND' };
} else if (error.code === 50013) {
return { success: false, reason: 'MISSING_PERMISSIONS' };
} else if (error.code === 50016) {
return { success: false, reason: 'RATE_LIMIT' };
}
return { success: false, reason: 'UNKNOWN', error };
}
};

if (subcommand === 'user') {
const user = interaction.options.getUser('target');
const result = await sendMessage(user);

if (result.success) {
await interaction.reply({ content: `Message sent to ${user.tag}`, ephemeral: true });
} else {
let errorMessage = `Failed to send message to ${user.tag}. `;
switch (result.reason) {
case 'DM_CLOSED':
errorMessage += 'They have their DMs closed.';
break;
case 'USER_NOT_FOUND':
errorMessage += 'User not found.';
break;
case 'MISSING_PERMISSIONS':
errorMessage += 'Missing permissions to send message.';
break;
case 'RATE_LIMIT':
errorMessage += 'Rate limit hit. Please try again later.';
break;
default:
errorMessage += 'An unknown error occurred.';
}
await interaction.reply({ content: errorMessage, ephemeral: true });
}
} else if (subcommand === 'role') {
const role = interaction.options.getRole('target');
const members = role.members;

const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setCustomId('confirm')
.setLabel('Confirm')
.setStyle(ButtonStyle.Primary),
new ButtonBuilder()
.setCustomId('cancel')
.setLabel('Cancel')
.setStyle(ButtonStyle.Danger),
);

await interaction.reply({
content: `Are you sure you want to send this message to ${members.size} members with the ${role.name} role?`,
components: [row],
ephemeral: true
});

const filter = i => i.user.id === interaction.user.id;
const confirmation = await interaction.channel.awaitMessageComponent({ filter, time: 30000 })
.catch(() => null);

if (!confirmation || confirmation.customId === 'cancel') {
return interaction.editReply({ content: 'Command cancelled.', components: [] });
}

let successCount = 0;
let failureCount = 0;
let count = 0;
const totalMembers = members.size;
const updateInterval = Math.max(1, Math.floor(totalMembers / 20)); // Update every 5%
let cancelled = false;

const queue = members.map(member => ({ member, sent: false }));
const batchSize = 10;
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

const processBatch = async () => {
const batch = queue.filter(item => !item.sent).slice(0, batchSize);
if (batch.length === 0 || cancelled) return;

await Promise.all(batch.map(async (item) => {
if (cancelled) return;
const result = await sendMessage(item.member);
item.sent = true;
if (result.success) successCount++; else failureCount++;
count++;

if (count % updateInterval === 0 || count === totalMembers) {
const progress = (count / totalMembers * 100).toFixed(2);
const progressBar = '█'.repeat(Math.floor(progress / 5)) + '░'.repeat(20 - Math.floor(progress / 5));
await interaction.editReply({
content: `Progress: ${progressBar} ${progress}%\n${count}/${totalMembers} messages sent`,
components: [new ActionRowBuilder().addComponents(
new ButtonBuilder()
.setCustomId('cancel_sending')
.setLabel('Cancel Sending')
.setStyle(ButtonStyle.Danger)
)],
ephemeral: true
});
}
}));

await delay(1000);
await processBatch();
};

processBatch();

const cancelListener = interaction.channel.createMessageComponentCollector({ filter, time: 600000 });
cancelListener.on('collect', async i => {
if (i.customId === 'cancel_sending') {
cancelled = true;
await i.update({ content: 'Cancelling the operation. Please wait...', components: [] });
}
});

while (queue.some(item => !item.sent) && !cancelled) {
await delay(1000);
}

cancelListener.stop();

const finalMessage = cancelled
? `Operation cancelled. ${successCount} messages sent, ${failureCount} failed.`
: `Finished! ${successCount} messages sent, ${failureCount} failed.`;

await interaction.editReply({
content: finalMessage,
components: []
});
}

},
};

0 comments on commit d825a95

Please sign in to comment.