Skip to content

Commit

Permalink
Update dm.js
Browse files Browse the repository at this point in the history
  • Loading branch information
GrishMahat authored Jul 9, 2024
1 parent 3bef78a commit bd799f0
Showing 1 changed file with 84 additions and 77 deletions.
161 changes: 84 additions & 77 deletions src/commands/developer/dm.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,22 @@ import {
ButtonBuilder,
ButtonStyle,
} from 'discord.js';
import Bottleneck from 'bottleneck';

const limiter = new Bottleneck({
minTime: 1000, // Minimum time between each request (1 second)
maxConcurrent: 1 // Only allow 1 request at a time
});

export default {
data: new SlashCommandBuilder()
.setName('dm')
.setDescription('Send a direct message to a user or role')
.setDescription('Send a direct message to a user, role, or all members in the server')
.addSubcommand(subcommand =>
subcommand
.setName('user')
.setDescription('Send a DM to a specific user')
.addUserOption(option =>
.addUserOption(option =>
option.setName('target')
.setDescription('The user to send the DM to')
.setRequired(true))
Expand All @@ -36,8 +41,17 @@ export default {
option.setName('message')
.setDescription('The message to send (Use {user} for recipient\'s name)')
.setRequired(true))
).toJSON(),

)
.addSubcommand(subcommand =>
subcommand
.setName('all')
.setDescription('Send a DM to all members in the server')
.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],
Expand All @@ -49,14 +63,15 @@ export default {
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' };
if (!user || user.bot) {
return { success: false, reason: 'USER_NOT_FOUND_OR_BOT' };
}

try {
const personalizedMessage = message.replace(/{user}/g, user.displayName);
await user.send(personalizedMessage);
await limiter.schedule(() => user.send(personalizedMessage));
return { success: true };
} catch (error) {
if (error.code === 50007) {
Expand All @@ -72,36 +87,7 @@ export default {
}
};

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 handleProcess = async (members, description) => {
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
Expand All @@ -115,7 +101,7 @@ export default {
);

await interaction.reply({
content: `Are you sure you want to send this message to ${members.size} members with the ${role.name} role?`,
content: `Are you sure you want to send this message to ${members.size} ${description}?`,
components: [row],
ephemeral: true
});
Expand All @@ -135,43 +121,28 @@ export default {
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();
const processMember = async (member) => {
if (cancelled) return;
const result = await sendMessage(member.user);
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
});
}
};

processBatch();

const cancelListener = interaction.channel.createMessageComponentCollector({ filter, time: 600000 });
cancelListener.on('collect', async i => {
if (i.customId === 'cancel_sending') {
Expand All @@ -180,8 +151,9 @@ export default {
}
});

while (queue.some(item => !item.sent) && !cancelled) {
await delay(1000);
for (const member of members.values()) {
await processMember(member);
if (cancelled) break;
}

cancelListener.stop();
Expand All @@ -194,7 +166,42 @@ export default {
content: finalMessage,
components: []
});
}
};

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;
await handleProcess(members, `members with the ${role.name} role`);
} else if (subcommand === 'all') {
const members = await interaction.guild.members.fetch();
const humanMembers = members.filter(member => !member.user.bot);
await handleProcess(humanMembers, 'members in the server');
}
},
};
};

0 comments on commit bd799f0

Please sign in to comment.