mirror of
https://github.com/ahmadk953/poixpixel-discord-bot.git
synced 2025-05-09 18:33:04 +00:00
chore: improve safety of commands
This commit is contained in:
parent
83bbf7b098
commit
7c2a99daf5
28 changed files with 329 additions and 235 deletions
|
@ -2,6 +2,7 @@
|
|||
"token": "DISCORD_BOT_TOKEN",
|
||||
"clientId": "DISCORD_BOT_ID",
|
||||
"guildId": "DISCORD_SERVER_ID",
|
||||
"serverInvite": "DISCORD_SERVER_INVITE_LINK",
|
||||
"database": {
|
||||
"dbConnectionString": "POSTGRESQL_CONNECTION_STRING",
|
||||
"maxRetryAttempts": "MAX_RETRY_ATTEMPTS",
|
||||
|
|
|
@ -148,8 +148,9 @@ const command = {
|
|||
),
|
||||
|
||||
async execute(interaction: ChatInputCommandInteraction) {
|
||||
await interaction.deferReply();
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
await interaction.deferReply();
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
|
||||
switch (subcommand) {
|
||||
|
|
|
@ -33,8 +33,9 @@ const command: SubcommandCommand = {
|
|||
),
|
||||
|
||||
execute: async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
await interaction.deferReply();
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
|
||||
if (subcommand === 'status') {
|
||||
|
@ -82,33 +83,30 @@ const command: SubcommandCommand = {
|
|||
});
|
||||
}
|
||||
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
} else if (subcommand === 'setcount') {
|
||||
if (
|
||||
!interaction.memberPermissions?.has(
|
||||
PermissionsBitField.Flags.Administrator,
|
||||
)
|
||||
) {
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'You need administrator permissions to use this command.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const count = interaction.options.getInteger('count');
|
||||
if (count === null) {
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'Invalid count specified.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await setCount(count);
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: `Count has been set to **${count}**. The next number should be **${count + 1}**.`,
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -74,12 +74,11 @@ const command: SubcommandCommand = {
|
|||
),
|
||||
|
||||
execute: async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
await interaction.deferReply({
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
await interaction.editReply('Processing...');
|
||||
|
||||
const config = loadConfig();
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
|
@ -100,7 +99,7 @@ const command: SubcommandCommand = {
|
|||
});
|
||||
|
||||
if (!isAdmin) {
|
||||
const approvalChannel = interaction.guild?.channels.cache.get(
|
||||
const approvalChannel = interaction.guild.channels.cache.get(
|
||||
config.channels.factApproval,
|
||||
);
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
builder,
|
||||
} from '@/util/giveaways/giveawayManager.js';
|
||||
import { createPaginationButtons } from '@/util/helpers.js';
|
||||
import { loadConfig } from '@/util/configLoader';
|
||||
|
||||
const command: SubcommandCommand = {
|
||||
data: new SlashCommandBuilder()
|
||||
|
@ -53,16 +54,30 @@ const command: SubcommandCommand = {
|
|||
),
|
||||
|
||||
execute: async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
const config = loadConfig();
|
||||
const communityManagerRoleId = config.roles.staffRoles.find(
|
||||
(role) => role.name === 'Community Manager',
|
||||
)?.roleId;
|
||||
|
||||
if (!communityManagerRoleId) {
|
||||
await interaction.reply({
|
||||
content:
|
||||
'Community Manager role not found in the configuration. Please contact a server admin.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions?.has(
|
||||
PermissionsBitField.Flags.ModerateMembers,
|
||||
)
|
||||
!interaction.guild.members.cache
|
||||
.find((member) => member.id === interaction.user.id)
|
||||
?.roles.cache.has(communityManagerRoleId)
|
||||
) {
|
||||
await interaction.reply({
|
||||
content: 'You do not have permission to manage giveaways.',
|
||||
ephemeral: true,
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -152,7 +167,7 @@ async function handleListGiveaways(interaction: ChatInputCommandInteraction) {
|
|||
if (i.user.id !== interaction.user.id) {
|
||||
await i.reply({
|
||||
content: 'You cannot use these buttons.',
|
||||
ephemeral: true,
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -22,13 +22,12 @@ const command: OptionsCommand = {
|
|||
.setRequired(false),
|
||||
),
|
||||
execute: async (interaction) => {
|
||||
if (!interaction.guild) return;
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
await interaction.deferReply();
|
||||
|
||||
try {
|
||||
const usersPerPage =
|
||||
(interaction.options.get('limit')?.value as number) || 10;
|
||||
const usersPerPage = interaction.options.getInteger('limit') || 10;
|
||||
|
||||
const allUsers = await getLevelLeaderboard(100);
|
||||
|
||||
|
|
|
@ -15,18 +15,16 @@ const command: OptionsCommand = {
|
|||
.setRequired(false),
|
||||
),
|
||||
execute: async (interaction) => {
|
||||
const member = await interaction.guild?.members.fetch(
|
||||
(interaction.options.get('user')?.value as string) || interaction.user.id,
|
||||
);
|
||||
|
||||
if (!member) {
|
||||
await interaction.reply('User not found in this server.');
|
||||
return;
|
||||
}
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
await interaction.deferReply();
|
||||
|
||||
try {
|
||||
const member = await interaction.guild.members.fetch(
|
||||
(interaction.options.get('user')?.value as string) ||
|
||||
interaction.user.id,
|
||||
);
|
||||
|
||||
const userData = await getUserLevel(member.id);
|
||||
const rankCard = await generateRankCard(member, userData);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import { PermissionsBitField, SlashCommandBuilder } from 'discord.js';
|
|||
import { updateMember, updateMemberModerationHistory } from '@/db/db.js';
|
||||
import { parseDuration, scheduleUnban } from '@/util/helpers.js';
|
||||
import { OptionsCommand } from '@/types/CommandTypes.js';
|
||||
import { loadConfig } from '@/util/configLoader.js';
|
||||
import logAction from '@/util/logging/logAction.js';
|
||||
|
||||
const command: OptionsCommand = {
|
||||
|
@ -30,40 +31,62 @@ const command: OptionsCommand = {
|
|||
.setRequired(false),
|
||||
),
|
||||
execute: async (interaction) => {
|
||||
const moderator = await interaction.guild?.members.fetch(
|
||||
interaction.user.id,
|
||||
);
|
||||
const member = await interaction.guild?.members.fetch(
|
||||
interaction.options.get('member')!.value as string,
|
||||
);
|
||||
const reason = interaction.options.get('reason')?.value as string;
|
||||
const banDuration = interaction.options.get('duration')?.value as
|
||||
| string
|
||||
| undefined;
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions?.has(
|
||||
PermissionsBitField.Flags.BanMembers,
|
||||
) ||
|
||||
moderator!.roles.highest.position <= member!.roles.highest.position ||
|
||||
!member?.bannable
|
||||
) {
|
||||
await interaction.reply({
|
||||
content:
|
||||
'You do not have permission to ban members or this member cannot be banned.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
await interaction.deferReply({ flags: ['Ephemeral'] });
|
||||
|
||||
try {
|
||||
await member.user.send(
|
||||
banDuration
|
||||
? `You have been banned from ${interaction.guild!.name} for ${banDuration}. Reason: ${reason}. You can join back at ${new Date(
|
||||
Date.now() + parseDuration(banDuration),
|
||||
).toUTCString()} using the link below:\nhttps://discord.gg/KRTGjxx7gY`
|
||||
: `You been indefinitely banned from ${interaction.guild!.name}. Reason: ${reason}.`,
|
||||
const moderator = await interaction.guild.members.fetch(
|
||||
interaction.user.id,
|
||||
);
|
||||
const member = await interaction.guild.members.fetch(
|
||||
interaction.options.get('member')!.value as string,
|
||||
);
|
||||
const reason = interaction.options.get('reason')?.value as string;
|
||||
const banDuration = interaction.options.get('duration')?.value as
|
||||
| string
|
||||
| undefined;
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions?.has(
|
||||
PermissionsBitField.Flags.BanMembers,
|
||||
)
|
||||
) {
|
||||
await interaction.reply({
|
||||
content: 'You do not have permission to ban members.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (moderator.roles.highest.position <= member.roles.highest.position) {
|
||||
await interaction.reply({
|
||||
content:
|
||||
'You cannot ban a member with equal or higher role than yours.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!member.bannable) {
|
||||
await interaction.reply({
|
||||
content: 'I do not have permission to ban this member.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await member.user.send(
|
||||
banDuration
|
||||
? `You have been banned from ${interaction.guild!.name} for ${banDuration}. Reason: ${reason}. You can join back at ${new Date(
|
||||
Date.now() + parseDuration(banDuration),
|
||||
).toUTCString()} using the link below:\n${interaction.guild.vanityURLCode ?? loadConfig().serverInvite}`
|
||||
: `You been indefinitely banned from ${interaction.guild!.name}. Reason: ${reason}.`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Failed to send DM:', error);
|
||||
}
|
||||
await member.ban({ reason });
|
||||
|
||||
if (banDuration) {
|
||||
|
@ -97,20 +120,19 @@ const command: OptionsCommand = {
|
|||
guild: interaction.guild!,
|
||||
action: 'ban',
|
||||
target: member,
|
||||
moderator: moderator!,
|
||||
moderator,
|
||||
reason,
|
||||
});
|
||||
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: banDuration
|
||||
? `<@${member.id}> has been banned for ${banDuration}. Reason: ${reason}`
|
||||
: `<@${member.id}> has been indefinitely banned. Reason: ${reason}`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Ban command error:', error);
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'Unable to ban member.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -2,6 +2,7 @@ import { PermissionsBitField, SlashCommandBuilder } from 'discord.js';
|
|||
|
||||
import { updateMemberModerationHistory } from '@/db/db.js';
|
||||
import { OptionsCommand } from '@/types/CommandTypes.js';
|
||||
import { loadConfig } from '@/util/configLoader.js';
|
||||
import logAction from '@/util/logging/logAction.js';
|
||||
|
||||
const command: OptionsCommand = {
|
||||
|
@ -21,33 +22,48 @@ const command: OptionsCommand = {
|
|||
.setRequired(true),
|
||||
),
|
||||
execute: async (interaction) => {
|
||||
const moderator = await interaction.guild?.members.fetch(
|
||||
interaction.user.id,
|
||||
);
|
||||
const member = await interaction.guild?.members.fetch(
|
||||
interaction.options.get('member')!.value as string,
|
||||
);
|
||||
const reason = interaction.options.get('reason')?.value as string;
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions?.has(
|
||||
PermissionsBitField.Flags.KickMembers,
|
||||
) ||
|
||||
moderator!.roles.highest.position <= member!.roles.highest.position ||
|
||||
!member?.kickable
|
||||
) {
|
||||
await interaction.reply({
|
||||
content:
|
||||
'You do not have permission to kick members or this member cannot be kicked.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
await interaction.deferReply({ flags: ['Ephemeral'] });
|
||||
|
||||
try {
|
||||
const moderator = await interaction.guild.members.fetch(
|
||||
interaction.user.id,
|
||||
);
|
||||
const member = await interaction.guild.members.fetch(
|
||||
interaction.options.get('member')!.value as string,
|
||||
);
|
||||
const reason = interaction.options.get('reason')?.value as string;
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions?.has(
|
||||
PermissionsBitField.Flags.KickMembers,
|
||||
)
|
||||
) {
|
||||
await interaction.editReply({
|
||||
content: 'You do not have permission to kick members.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (moderator!.roles.highest.position <= member.roles.highest.position) {
|
||||
await interaction.editReply({
|
||||
content:
|
||||
'You cannot kick a member with equal or higher role than yours.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!member.kickable) {
|
||||
await interaction.editReply({
|
||||
content: 'I do not have permission to kick this member.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await member.user.send(
|
||||
`You have been kicked from ${interaction.guild!.name}. Reason: ${reason}. You can join back at: \nhttps://discord.gg/KRTGjxx7gY`,
|
||||
`You have been kicked from ${interaction.guild!.name}. Reason: ${reason}. You can join back at: \n${interaction.guild.vanityURLCode ?? loadConfig().serverInvite}`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Failed to send DM to kicked user:', error);
|
||||
|
@ -68,18 +84,17 @@ const command: OptionsCommand = {
|
|||
guild: interaction.guild!,
|
||||
action: 'kick',
|
||||
target: member,
|
||||
moderator: moderator!,
|
||||
moderator,
|
||||
reason,
|
||||
});
|
||||
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: `<@${member.id}> has been kicked. Reason: ${reason}`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Kick command error:', error);
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'Unable to kick member.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -30,38 +30,52 @@ const command: OptionsCommand = {
|
|||
.setRequired(true),
|
||||
),
|
||||
execute: async (interaction) => {
|
||||
const moderator = await interaction.guild?.members.fetch(
|
||||
interaction.user.id,
|
||||
);
|
||||
const member = await interaction.guild?.members.fetch(
|
||||
interaction.options.get('member')!.value as string,
|
||||
);
|
||||
const reason = interaction.options.get('reason')?.value as string;
|
||||
const muteDuration = interaction.options.get('duration')?.value as string;
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions?.has(
|
||||
PermissionsBitField.Flags.ModerateMembers,
|
||||
) ||
|
||||
moderator!.roles.highest.position <= member!.roles.highest.position ||
|
||||
!member?.moderatable
|
||||
) {
|
||||
await interaction.reply({
|
||||
content:
|
||||
'You do not have permission to timeout members or this member cannot be timed out.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
await interaction.deferReply({ flags: ['Ephemeral'] });
|
||||
|
||||
try {
|
||||
const moderator = await interaction.guild.members.fetch(
|
||||
interaction.user.id,
|
||||
);
|
||||
const member = await interaction.guild.members.fetch(
|
||||
interaction.options.get('member')!.value as string,
|
||||
);
|
||||
const reason = interaction.options.get('reason')?.value as string;
|
||||
const muteDuration = interaction.options.get('duration')?.value as string;
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions?.has(
|
||||
PermissionsBitField.Flags.KickMembers,
|
||||
)
|
||||
) {
|
||||
await interaction.editReply({
|
||||
content: 'You do not have permission to mute members.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (moderator.roles.highest.position <= member.roles.highest.position) {
|
||||
await interaction.editReply({
|
||||
content:
|
||||
'You cannot mute a member with equal or higher role than yours.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!member.moderatable) {
|
||||
await interaction.editReply({
|
||||
content: 'I do not have permission to mute this member.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const durationMs = parseDuration(muteDuration);
|
||||
const maxTimeout = 28 * 24 * 60 * 60 * 1000;
|
||||
|
||||
if (durationMs > maxTimeout) {
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'Timeout duration cannot exceed 28 days.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -94,19 +108,18 @@ const command: OptionsCommand = {
|
|||
guild: interaction.guild!,
|
||||
action: 'mute',
|
||||
target: member,
|
||||
moderator: moderator!,
|
||||
moderator,
|
||||
reason,
|
||||
duration: muteDuration,
|
||||
});
|
||||
|
||||
await interaction.reply({
|
||||
content: `<@${member.id}> has been timed out for ${muteDuration}. Reason: ${reason}`,
|
||||
await interaction.editReply({
|
||||
content: `<@${member.id}> has been muted for ${muteDuration}. Reason: ${reason}`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Mute command error:', error);
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'Unable to timeout member.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -20,52 +20,53 @@ const command: OptionsCommand = {
|
|||
.setRequired(true),
|
||||
),
|
||||
execute: async (interaction) => {
|
||||
const userId = interaction.options.get('userid')!.value as string;
|
||||
const reason = interaction.options.get('reason')?.value as string;
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions?.has(PermissionsBitField.Flags.BanMembers)
|
||||
) {
|
||||
await interaction.reply({
|
||||
content: 'You do not have permission to unban users.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
interaction.deferReply({ flags: ['Ephemeral'] });
|
||||
|
||||
try {
|
||||
const userId = interaction.options.get('userid')?.value as string;
|
||||
const reason = interaction.options.get('reason')?.value as string;
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions?.has(
|
||||
PermissionsBitField.Flags.BanMembers,
|
||||
)
|
||||
) {
|
||||
await interaction.editReply({
|
||||
content: 'You do not have permission to unban users.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const ban = await interaction.guild?.bans.fetch(userId);
|
||||
const ban = await interaction.guild.bans.fetch(userId);
|
||||
if (!ban) {
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'This user is not banned.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'Error getting ban. Is this user banned?',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await executeUnban(
|
||||
interaction.client,
|
||||
interaction.guildId!,
|
||||
interaction.guild.id,
|
||||
userId,
|
||||
reason,
|
||||
);
|
||||
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: `<@${userId}> has been unbanned. Reason: ${reason}`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'Unable to unban user.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -20,43 +20,44 @@ const command: OptionsCommand = {
|
|||
.setRequired(true),
|
||||
),
|
||||
execute: async (interaction) => {
|
||||
const moderator = await interaction.guild?.members.fetch(
|
||||
interaction.user.id,
|
||||
);
|
||||
const member = await interaction.guild?.members.fetch(
|
||||
interaction.options.get('member')!.value as string,
|
||||
);
|
||||
const reason = interaction.options.get('reason')?.value as string;
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions?.has(
|
||||
PermissionsBitField.Flags.ModerateMembers,
|
||||
)
|
||||
) {
|
||||
await interaction.reply({
|
||||
content: 'You do not have permission to unmute members.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
await interaction.deferReply({ flags: ['Ephemeral'] });
|
||||
|
||||
try {
|
||||
const moderator = await interaction.guild.members.fetch(
|
||||
interaction.user.id,
|
||||
);
|
||||
const member = await interaction.guild.members.fetch(
|
||||
interaction.options.get('member')!.value as string,
|
||||
);
|
||||
const reason = interaction.options.get('reason')?.value as string;
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions?.has(
|
||||
PermissionsBitField.Flags.ModerateMembers,
|
||||
)
|
||||
) {
|
||||
await interaction.editReply({
|
||||
content: 'You do not have permission to unmute members.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
await executeUnmute(
|
||||
interaction.client,
|
||||
interaction.guild!.id,
|
||||
member!.id,
|
||||
interaction.guild.id,
|
||||
member.id,
|
||||
reason,
|
||||
moderator,
|
||||
);
|
||||
|
||||
await interaction.reply({
|
||||
content: `<@${member!.id}>'s timeout has been removed. Reason: ${reason}`,
|
||||
await interaction.editReply({
|
||||
content: `<@${member.id}>'s timeout has been removed. Reason: ${reason}`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Unmute command error:', error);
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'Unable to unmute member.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -21,29 +21,38 @@ const command: OptionsCommand = {
|
|||
.setRequired(true),
|
||||
),
|
||||
execute: async (interaction) => {
|
||||
const moderator = await interaction.guild?.members.fetch(
|
||||
interaction.user.id,
|
||||
);
|
||||
const member = await interaction.guild?.members.fetch(
|
||||
interaction.options.get('member')!.value as unknown as string,
|
||||
);
|
||||
const reason = interaction.options.get('reason')
|
||||
?.value as unknown as string;
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions?.has(
|
||||
PermissionsBitField.Flags.ModerateMembers,
|
||||
) ||
|
||||
moderator!.roles.highest.position <= member!.roles.highest.position
|
||||
) {
|
||||
await interaction.reply({
|
||||
content: 'You do not have permission to warn this member.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
await interaction.deferReply({ flags: ['Ephemeral'] });
|
||||
|
||||
try {
|
||||
const moderator = await interaction.guild.members.fetch(
|
||||
interaction.user.id,
|
||||
);
|
||||
const member = await interaction.guild.members.fetch(
|
||||
interaction.options.get('member')!.value as unknown as string,
|
||||
);
|
||||
const reason = interaction.options.getString('reason')!;
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions?.has(
|
||||
PermissionsBitField.Flags.ModerateMembers,
|
||||
)
|
||||
) {
|
||||
await interaction.editReply({
|
||||
content: 'You do not have permission to warn members.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (moderator.roles.highest.position <= member.roles.highest.position) {
|
||||
await interaction.editReply({
|
||||
content:
|
||||
'You cannot warn a member with equal or higher role than yours.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await updateMemberModerationHistory({
|
||||
discordId: member!.user.id,
|
||||
moderatorDiscordId: interaction.user.id,
|
||||
|
@ -61,14 +70,13 @@ const command: OptionsCommand = {
|
|||
moderator: moderator!,
|
||||
reason: reason,
|
||||
});
|
||||
await interaction.reply(
|
||||
await interaction.editReply(
|
||||
`<@${member!.user.id}> has been warned. Reason: ${reason}`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'There was an error trying to warn the member.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -8,25 +8,27 @@ const command: Command = {
|
|||
.setDescription('Simulates a new member joining'),
|
||||
|
||||
execute: async (interaction) => {
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
const guild = interaction.guild;
|
||||
|
||||
await interaction.deferReply({ flags: ['Ephemeral'] });
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions!.has(
|
||||
PermissionsBitField.Flags.Administrator,
|
||||
)
|
||||
) {
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'You do not have permission to use this command.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const fakeMember = await guild!.members.fetch(interaction.user.id);
|
||||
guild!.client.emit('guildMemberAdd', fakeMember);
|
||||
const fakeMember = await guild.members.fetch(interaction.user.id);
|
||||
guild.client.emit('guildMemberAdd', fakeMember);
|
||||
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'Triggered the join event!',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
@ -9,25 +9,26 @@ const command: Command = {
|
|||
.setDescription('Simulates a member leaving'),
|
||||
|
||||
execute: async (interaction) => {
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
const guild = interaction.guild;
|
||||
|
||||
await interaction.deferReply({ flags: ['Ephemeral'] });
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions!.has(
|
||||
PermissionsBitField.Flags.Administrator,
|
||||
)
|
||||
) {
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'You do not have permission to use this command.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
}
|
||||
|
||||
const fakeMember = await guild!.members.fetch(interaction.user.id);
|
||||
guild!.client.emit('guildMemberRemove', fakeMember);
|
||||
const fakeMember = await guild.members.fetch(interaction.user.id);
|
||||
guild.client.emit('guildMemberRemove', fakeMember);
|
||||
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'Triggered the leave event!',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
|
||||
await updateMember({
|
||||
|
|
|
@ -14,12 +14,15 @@ const command: Command = {
|
|||
.setDescription('(Admin Only) Display the current configuration')
|
||||
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
|
||||
execute: async (interaction) => {
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
await interaction.deferReply({ flags: ['Ephemeral'] });
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions?.has(PermissionFlagsBits.Administrator)
|
||||
) {
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'You do not have permission to use this command.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -180,10 +183,9 @@ const command: Command = {
|
|||
? [createPaginationButtons(pages.length, currentPage)]
|
||||
: [];
|
||||
|
||||
const reply = await interaction.reply({
|
||||
const reply = await interaction.editReply({
|
||||
embeds: [pages[currentPage]],
|
||||
components,
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
|
||||
if (pages.length <= 1) return;
|
||||
|
|
|
@ -26,9 +26,11 @@ const command: OptionsCommand = {
|
|||
),
|
||||
|
||||
execute: async (interaction) => {
|
||||
try {
|
||||
await interaction.deferReply();
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
await interaction.deferReply();
|
||||
|
||||
try {
|
||||
const client = interaction.client as ExtendedClient;
|
||||
const commandName = interaction.options.getString('command');
|
||||
|
||||
|
@ -178,7 +180,7 @@ async function handleSpecificCommand(
|
|||
const cmd = client.commands.get(commandName);
|
||||
|
||||
if (!cmd) {
|
||||
return interaction.reply({
|
||||
return interaction.editReply({
|
||||
content: `Command \`${commandName}\` not found.`,
|
||||
ephemeral: true,
|
||||
});
|
||||
|
@ -227,7 +229,7 @@ async function handleSpecificCommand(
|
|||
inline: false,
|
||||
});
|
||||
|
||||
return interaction.reply({ embeds: [embed] });
|
||||
return interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,6 +16,10 @@ const command: Command = {
|
|||
.setName('members')
|
||||
.setDescription('Lists all non-bot members of the server'),
|
||||
execute: async (interaction) => {
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
await interaction.deferReply();
|
||||
|
||||
let members = await getAllMembers();
|
||||
members = members.sort((a, b) =>
|
||||
(a.discordUsername ?? '').localeCompare(b.discordUsername ?? ''),
|
||||
|
@ -63,7 +67,7 @@ const command: Command = {
|
|||
const components =
|
||||
pages.length > 1 ? [getButtonActionRow(), getSelectMenuRow()] : [];
|
||||
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
embeds: [pages[currentPage]],
|
||||
components,
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@ const command: Command = {
|
|||
.setDescription('Check the latency from you to the bot'),
|
||||
execute: async (interaction) => {
|
||||
await interaction.reply(
|
||||
`Pong! Latency: ${Date.now() - interaction.createdTimestamp}ms`,
|
||||
`🏓 Pong! Latency: ${Date.now() - interaction.createdTimestamp}ms`,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -8,21 +8,22 @@ const command: Command = {
|
|||
.setName('recalculatelevels')
|
||||
.setDescription('(Admin Only) Recalculate all user levels'),
|
||||
execute: async (interaction) => {
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
await interaction.deferReply({ flags: ['Ephemeral'] });
|
||||
await interaction.editReply('Recalculating levels...');
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions?.has(
|
||||
PermissionsBitField.Flags.Administrator,
|
||||
)
|
||||
) {
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'You do not have permission to use this command.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await interaction.deferReply();
|
||||
await interaction.editReply('Recalculating levels...');
|
||||
|
||||
try {
|
||||
await recalculateUserLevels();
|
||||
await interaction.editReply('Levels recalculated successfully!');
|
||||
|
|
|
@ -36,7 +36,9 @@ const command: SubcommandCommand = {
|
|||
),
|
||||
|
||||
execute: async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
await interaction.deferReply({ flags: ['Ephemeral'] });
|
||||
|
||||
const config = loadConfig();
|
||||
const managerRoleId = config.roles.staffRoles.find(
|
||||
|
@ -52,18 +54,15 @@ const command: SubcommandCommand = {
|
|||
PermissionsBitField.Flags.Administrator,
|
||||
)
|
||||
) {
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content:
|
||||
'You do not have permission to use this command. This command is restricted to users with the Manager role.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
|
||||
await interaction.deferReply({ flags: ['Ephemeral'] });
|
||||
|
||||
try {
|
||||
if (subcommand === 'database') {
|
||||
await handleDatabaseReconnect(interaction);
|
||||
|
|
|
@ -18,6 +18,10 @@ const command: Command = {
|
|||
.setName('restart')
|
||||
.setDescription('(Manager Only) Restart the bot'),
|
||||
execute: async (interaction) => {
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
await interaction.deferReply({ flags: ['Ephemeral'] });
|
||||
|
||||
const config = loadConfig();
|
||||
const managerRoleId = config.roles.staffRoles.find(
|
||||
(role) => role.name === 'Manager',
|
||||
|
@ -32,17 +36,15 @@ const command: Command = {
|
|||
PermissionsBitField.Flags.Administrator,
|
||||
)
|
||||
) {
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content:
|
||||
'You do not have permission to restart the bot. This command is restricted to users with the Manager role.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'Restarting the bot... This may take a few moments.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
|
||||
const dbConnected = await ensureDatabaseConnection();
|
||||
|
|
|
@ -7,8 +7,10 @@ const command: Command = {
|
|||
.setName('server')
|
||||
.setDescription('Provides information about the server.'),
|
||||
execute: async (interaction) => {
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
await interaction.reply(
|
||||
`The server **${interaction!.guild!.name}** has **${interaction!.guild!.memberCount}** members and was created on **${interaction!.guild!.createdAt}**. It is **${new Date().getFullYear() - interaction!.guild!.createdAt.getFullYear()!}** years old.`,
|
||||
`The server **${interaction.guild.name}** has **${interaction.guild.memberCount}** members and was created on **${interaction.guild.createdAt}**. It is **${new Date().getFullYear() - interaction.guild.createdAt.getFullYear()}** years old.`,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -19,13 +19,17 @@ const command: OptionsCommand = {
|
|||
.setRequired(true),
|
||||
),
|
||||
execute: async (interaction) => {
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
await interaction.deferReply();
|
||||
|
||||
const userOption = interaction.options.get(
|
||||
'user',
|
||||
) as unknown as GuildMember;
|
||||
const user = userOption.user;
|
||||
|
||||
if (!userOption || !user) {
|
||||
await interaction.reply('User not found');
|
||||
await interaction.editReply('User not found');
|
||||
return;
|
||||
}
|
||||
if (
|
||||
|
@ -33,7 +37,7 @@ const command: OptionsCommand = {
|
|||
PermissionsBitField.Flags.ModerateMembers,
|
||||
)
|
||||
) {
|
||||
await interaction.reply(
|
||||
await interaction.editReply(
|
||||
'You do not have permission to view member information.',
|
||||
);
|
||||
return;
|
||||
|
@ -140,7 +144,7 @@ const command: OptionsCommand = {
|
|||
iconURL: interaction.user.displayAvatarURL(),
|
||||
});
|
||||
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -71,12 +71,16 @@ const command: SubcommandCommand = {
|
|||
),
|
||||
),
|
||||
execute: async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
if (!interaction.isChatInputCommand() || !interaction.guild) return;
|
||||
|
||||
const commandUser = interaction.guild?.members.cache.get(
|
||||
const commandUser = interaction.guild.members.cache.get(
|
||||
interaction.user.id,
|
||||
);
|
||||
|
||||
await interaction.deferReply({
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
|
||||
const config = loadConfig();
|
||||
const managerRoleId = config.roles.staffRoles.find(
|
||||
(role) => role.name === 'Manager',
|
||||
|
@ -87,18 +91,12 @@ const command: SubcommandCommand = {
|
|||
!managerRoleId ||
|
||||
commandUser.roles.highest.comparePositionTo(managerRoleId) < 0
|
||||
) {
|
||||
await interaction.reply({
|
||||
await interaction.editReply({
|
||||
content: 'You do not have permission to use this command',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await interaction.deferReply({
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
await interaction.editReply('Processing...');
|
||||
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
const user = interaction.options.getUser('user', true);
|
||||
const amount = interaction.options.getInteger('amount', false);
|
||||
|
|
|
@ -12,9 +12,10 @@ async function startBot() {
|
|||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildMembers,
|
||||
GatewayIntentBits.GuildMessages,
|
||||
GatewayIntentBits.GuildModeration,
|
||||
GatewayIntentBits.GuildInvites,
|
||||
GatewayIntentBits.MessageContent,
|
||||
GatewayIntentBits.GuildMessageReactions,
|
||||
GatewayIntentBits.GuildModeration,
|
||||
],
|
||||
},
|
||||
config,
|
||||
|
|
|
@ -33,6 +33,10 @@ export class ExtendedClient extends Client {
|
|||
console.log('Skipping command deployment (SKIP_COMMAND_DEPLOY=true)');
|
||||
const commandFiles = await this.loadCommandsWithoutDeploying();
|
||||
|
||||
if (!commandFiles?.length) {
|
||||
throw new Error('No commands found');
|
||||
}
|
||||
|
||||
await registerEvents(this);
|
||||
console.log(
|
||||
`Loaded ${commandFiles.length} commands and registered events (without deployment)`,
|
||||
|
|
|
@ -5,6 +5,7 @@ export interface Config {
|
|||
token: string;
|
||||
clientId: string;
|
||||
guildId: string;
|
||||
serverInvite: string;
|
||||
database: {
|
||||
dbConnectionString: string;
|
||||
maxRetryAttempts: number;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue