mirror of
https://github.com/ahmadk953/poixpixel-discord-bot.git
synced 2025-05-10 10:43:06 +00:00
Added Warn and Ban Commands, Added Logging, and Much More
This commit is contained in:
parent
d89de72e08
commit
86adac3f08
33 changed files with 2200 additions and 204 deletions
127
src/commands/moderation/ban.ts
Normal file
127
src/commands/moderation/ban.ts
Normal file
|
@ -0,0 +1,127 @@
|
|||
import {
|
||||
CommandInteraction,
|
||||
PermissionsBitField,
|
||||
SlashCommandBuilder,
|
||||
SlashCommandOptionsOnlyBuilder,
|
||||
} from 'discord.js';
|
||||
import { updateMember, updateMemberModerationHistory } from '../../db/db.js';
|
||||
import { parseDuration, scheduleUnban } from '../../util/helpers.js';
|
||||
import logAction from '../../util/logging/logAction.js';
|
||||
|
||||
interface Command {
|
||||
data: SlashCommandOptionsOnlyBuilder;
|
||||
execute: (interaction: CommandInteraction) => Promise<void>;
|
||||
}
|
||||
|
||||
const command: Command = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('ban')
|
||||
.setDescription('Ban a member from the server')
|
||||
.addUserOption((option) =>
|
||||
option
|
||||
.setName('member')
|
||||
.setDescription('The member to ban')
|
||||
.setRequired(true),
|
||||
)
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName('reason')
|
||||
.setDescription('The reason for the ban')
|
||||
.setRequired(true),
|
||||
)
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName('duration')
|
||||
.setDescription(
|
||||
'The duration of the ban (ex. 5m, 1h, 1d, 1w). Leave blank for permanent ban.',
|
||||
)
|
||||
.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.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;
|
||||
}
|
||||
|
||||
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}.`,
|
||||
);
|
||||
await member.ban({ reason });
|
||||
|
||||
if (banDuration) {
|
||||
const durationMs = parseDuration(banDuration);
|
||||
const expiresAt = new Date(Date.now() + durationMs);
|
||||
|
||||
await scheduleUnban(
|
||||
interaction.client,
|
||||
interaction.guild!.id,
|
||||
member.id,
|
||||
expiresAt,
|
||||
);
|
||||
}
|
||||
|
||||
await updateMemberModerationHistory({
|
||||
discordId: member.id,
|
||||
moderatorDiscordId: interaction.user.id,
|
||||
action: 'ban',
|
||||
reason,
|
||||
duration: banDuration ?? 'indefinite',
|
||||
createdAt: new Date(),
|
||||
active: true,
|
||||
});
|
||||
|
||||
await updateMember({
|
||||
discordId: member.id,
|
||||
currentlyBanned: true,
|
||||
});
|
||||
|
||||
await logAction({
|
||||
guild: interaction.guild!,
|
||||
action: 'ban',
|
||||
target: member,
|
||||
moderator: moderator!,
|
||||
reason,
|
||||
});
|
||||
|
||||
await interaction.reply({
|
||||
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({
|
||||
content: 'Unable to ban member.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default command;
|
82
src/commands/moderation/unban.ts
Normal file
82
src/commands/moderation/unban.ts
Normal file
|
@ -0,0 +1,82 @@
|
|||
import {
|
||||
CommandInteraction,
|
||||
PermissionsBitField,
|
||||
SlashCommandBuilder,
|
||||
SlashCommandOptionsOnlyBuilder,
|
||||
} from 'discord.js';
|
||||
import { executeUnban } from '../../util/helpers.js';
|
||||
|
||||
interface Command {
|
||||
data: SlashCommandOptionsOnlyBuilder;
|
||||
execute: (interaction: CommandInteraction) => Promise<void>;
|
||||
}
|
||||
|
||||
const command: Command = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('unban')
|
||||
.setDescription('Unban a user from the server')
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName('userid')
|
||||
.setDescription('The Discord ID of the user to unban')
|
||||
.setRequired(true),
|
||||
)
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName('reason')
|
||||
.setDescription('The reason for the unban')
|
||||
.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.memberPermissions?.has(PermissionsBitField.Flags.BanMembers)
|
||||
) {
|
||||
await interaction.reply({
|
||||
content: 'You do not have permission to unban users.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
const ban = await interaction.guild?.bans.fetch(userId);
|
||||
if (!ban) {
|
||||
await interaction.reply({
|
||||
content: 'This user is not banned.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
await interaction.reply({
|
||||
content: 'Error getting ban. Is this user banned?',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await executeUnban(
|
||||
interaction.client,
|
||||
interaction.guildId!,
|
||||
userId,
|
||||
reason,
|
||||
);
|
||||
|
||||
await interaction.reply({
|
||||
content: `<@${userId}> has been unbanned. Reason: ${reason}`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
await interaction.reply({
|
||||
content: 'Unable to unban user.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default command;
|
85
src/commands/moderation/warn.ts
Normal file
85
src/commands/moderation/warn.ts
Normal file
|
@ -0,0 +1,85 @@
|
|||
import {
|
||||
CommandInteraction,
|
||||
PermissionsBitField,
|
||||
SlashCommandBuilder,
|
||||
SlashCommandOptionsOnlyBuilder,
|
||||
} from 'discord.js';
|
||||
import { updateMemberModerationHistory } from '../../db/db.js';
|
||||
import logAction from '../../util/logging/logAction.js';
|
||||
|
||||
interface Command {
|
||||
data: SlashCommandOptionsOnlyBuilder;
|
||||
execute: (interaction: CommandInteraction) => Promise<void>;
|
||||
}
|
||||
|
||||
const command: Command = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('warn')
|
||||
.setDescription('Warn a member')
|
||||
.addUserOption((option) =>
|
||||
option
|
||||
.setName('member')
|
||||
.setDescription('The member to warn')
|
||||
.setRequired(true),
|
||||
)
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName('reason')
|
||||
.setDescription('The reason for the warning')
|
||||
.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.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;
|
||||
}
|
||||
|
||||
try {
|
||||
await updateMemberModerationHistory({
|
||||
discordId: member!.user.id,
|
||||
moderatorDiscordId: interaction.user.id,
|
||||
action: 'warning',
|
||||
reason: reason,
|
||||
duration: '',
|
||||
});
|
||||
await member!.user.send(
|
||||
`You have been warned in **${interaction?.guild?.name}**. Reason: **${reason}**.`,
|
||||
);
|
||||
await interaction.reply(
|
||||
`<@${member!.user.id}> has been warned. Reason: ${reason}`,
|
||||
);
|
||||
await logAction({
|
||||
guild: interaction.guild!,
|
||||
action: 'warn',
|
||||
target: member!,
|
||||
moderator: moderator!,
|
||||
reason: reason,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
await interaction.reply({
|
||||
content: 'There was an error trying to warn the member.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default command;
|
42
src/commands/testing/testJoin.ts
Normal file
42
src/commands/testing/testJoin.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import {
|
||||
CommandInteraction,
|
||||
PermissionsBitField,
|
||||
SlashCommandBuilder,
|
||||
SlashCommandOptionsOnlyBuilder,
|
||||
} from 'discord.js';
|
||||
|
||||
interface Command {
|
||||
data: SlashCommandOptionsOnlyBuilder;
|
||||
execute: (interaction: CommandInteraction) => Promise<void>;
|
||||
}
|
||||
|
||||
const command: Command = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('testjoin')
|
||||
.setDescription('Simulates a new member joining'),
|
||||
|
||||
execute: async (interaction) => {
|
||||
const guild = interaction.guild;
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions!.has(
|
||||
PermissionsBitField.Flags.Administrator,
|
||||
)
|
||||
) {
|
||||
await interaction.reply({
|
||||
content: 'You do not have permission to use this command.',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
}
|
||||
|
||||
const fakeMember = await guild!.members.fetch(interaction.user.id);
|
||||
guild!.client.emit('guildMemberAdd', fakeMember);
|
||||
|
||||
await interaction.reply({
|
||||
content: 'Triggered the join event!',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default command;
|
48
src/commands/testing/testLeave.ts
Normal file
48
src/commands/testing/testLeave.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import {
|
||||
CommandInteraction,
|
||||
PermissionsBitField,
|
||||
SlashCommandBuilder,
|
||||
SlashCommandOptionsOnlyBuilder,
|
||||
} from 'discord.js';
|
||||
import { updateMember } from '../../db/db.js';
|
||||
|
||||
interface Command {
|
||||
data: SlashCommandOptionsOnlyBuilder;
|
||||
execute: (interaction: CommandInteraction) => Promise<void>;
|
||||
}
|
||||
|
||||
const command: Command = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('testleave')
|
||||
.setDescription('Simulates a member leaving'),
|
||||
|
||||
execute: async (interaction) => {
|
||||
const guild = interaction.guild;
|
||||
|
||||
if (
|
||||
!interaction.memberPermissions!.has(
|
||||
PermissionsBitField.Flags.Administrator,
|
||||
)
|
||||
) {
|
||||
await interaction.reply({
|
||||
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);
|
||||
|
||||
await interaction.reply({
|
||||
content: 'Triggered the leave event!',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
|
||||
await updateMember({
|
||||
discordId: interaction.user.id,
|
||||
currentlyInServer: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default command;
|
|
@ -2,8 +2,14 @@ import {
|
|||
SlashCommandBuilder,
|
||||
CommandInteraction,
|
||||
EmbedBuilder,
|
||||
ButtonBuilder,
|
||||
ActionRowBuilder,
|
||||
ButtonStyle,
|
||||
StringSelectMenuBuilder,
|
||||
APIEmbed,
|
||||
JSONEncodable,
|
||||
} from 'discord.js';
|
||||
import { getAllMembers } from '../../util/db.js';
|
||||
import { getAllMembers } from '../../db/db.js';
|
||||
|
||||
interface Command {
|
||||
data: Omit<SlashCommandBuilder, 'addSubcommand' | 'addSubcommandGroup'>;
|
||||
|
@ -15,16 +21,112 @@ const command: Command = {
|
|||
.setName('members')
|
||||
.setDescription('Lists all non-bot members of the server'),
|
||||
execute: async (interaction) => {
|
||||
const members = await getAllMembers();
|
||||
const memberList = members
|
||||
.map((m) => `**${m.discordUsername}** (${m.discordId})`)
|
||||
.join('\n');
|
||||
const membersEmbed = new EmbedBuilder()
|
||||
.setTitle('Members')
|
||||
.setDescription(memberList)
|
||||
.setColor(0x0099ff)
|
||||
.addFields({ name: 'Total Members', value: members.length.toString() });
|
||||
await interaction.reply({ embeds: [membersEmbed] });
|
||||
let members = await getAllMembers();
|
||||
members = members.sort((a, b) =>
|
||||
a.discordUsername.localeCompare(b.discordUsername),
|
||||
);
|
||||
|
||||
const ITEMS_PER_PAGE = 15;
|
||||
const pages: (APIEmbed | JSONEncodable<APIEmbed>)[] = [];
|
||||
for (let i = 0; i < members.length; i += ITEMS_PER_PAGE) {
|
||||
const pageMembers = members.slice(i, i + ITEMS_PER_PAGE);
|
||||
const memberList = pageMembers
|
||||
.map((m) => `**${m.discordUsername}** (${m.discordId})`)
|
||||
.join('\n');
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle('Members')
|
||||
.setDescription(memberList || 'No members to display.')
|
||||
.setColor(0x0099ff)
|
||||
.addFields({ name: 'Total Members', value: members.length.toString() })
|
||||
.setFooter({
|
||||
text: `Page ${Math.floor(i / ITEMS_PER_PAGE) + 1} of ${Math.ceil(members.length / ITEMS_PER_PAGE)}`,
|
||||
});
|
||||
pages.push(embed);
|
||||
}
|
||||
|
||||
let currentPage = 0;
|
||||
const getButtonActionRow = () =>
|
||||
new ActionRowBuilder<ButtonBuilder>().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('previous')
|
||||
.setLabel('Previous')
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
.setDisabled(currentPage === 0),
|
||||
new ButtonBuilder()
|
||||
.setCustomId('next')
|
||||
.setLabel('Next')
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
.setDisabled(currentPage === pages.length - 1),
|
||||
);
|
||||
|
||||
const getSelectMenuRow = () => {
|
||||
const options = pages.map((_, index) => ({
|
||||
label: `Page ${index + 1}`,
|
||||
value: index.toString(),
|
||||
default: index === currentPage,
|
||||
}));
|
||||
|
||||
const select = new StringSelectMenuBuilder()
|
||||
.setCustomId('select_page')
|
||||
.setPlaceholder('Jump to a page')
|
||||
.addOptions(options);
|
||||
|
||||
return new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
|
||||
select,
|
||||
);
|
||||
};
|
||||
|
||||
const components =
|
||||
pages.length > 1 ? [getButtonActionRow(), getSelectMenuRow()] : [];
|
||||
|
||||
await interaction.reply({
|
||||
embeds: [pages[currentPage]],
|
||||
components,
|
||||
});
|
||||
|
||||
const message = await interaction.fetchReply();
|
||||
|
||||
if (pages.length <= 1) return;
|
||||
|
||||
const collector = message.createMessageComponentCollector({
|
||||
time: 60000,
|
||||
});
|
||||
|
||||
collector.on('collect', async (i) => {
|
||||
if (i.user.id !== interaction.user.id) {
|
||||
await i.reply({
|
||||
content: 'These controls are not for you!',
|
||||
flags: ['Ephemeral'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (i.isButton()) {
|
||||
if (i.customId === 'previous' && currentPage > 0) {
|
||||
currentPage--;
|
||||
} else if (i.customId === 'next' && currentPage < pages.length - 1) {
|
||||
currentPage++;
|
||||
}
|
||||
}
|
||||
|
||||
if (i.isStringSelectMenu()) {
|
||||
const selected = parseInt(i.values[0]);
|
||||
if (!isNaN(selected) && selected >= 0 && selected < pages.length) {
|
||||
currentPage = selected;
|
||||
}
|
||||
}
|
||||
|
||||
await i.update({
|
||||
embeds: [pages[currentPage]],
|
||||
components: [getButtonActionRow(), getSelectMenuRow()],
|
||||
});
|
||||
});
|
||||
|
||||
collector.on('end', async () => {
|
||||
if (message.editable) {
|
||||
await message.edit({ components: [] });
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ const rulesEmbed = new EmbedBuilder()
|
|||
{
|
||||
name: '**Rule #3: Use Common Sense**',
|
||||
value:
|
||||
'Think before you act or post. If something seems questionable, it’s probably best not to do it.',
|
||||
'Think before you act or post. If something seems questionable, it is probably best not to do it.',
|
||||
},
|
||||
{
|
||||
name: '**Rule #4: No Spamming**',
|
||||
|
@ -69,7 +69,7 @@ const rulesEmbed = new EmbedBuilder()
|
|||
{
|
||||
name: '**Rule #10: No Ping Abuse**',
|
||||
value:
|
||||
'Do not ping staff members unless it\'s absolutely necessary. Use pings responsibly for all members.',
|
||||
'Do not ping staff members unless it is absolutely necessary. Use pings responsibly for all members.',
|
||||
},
|
||||
{
|
||||
name: '**Rule #11: Use Appropriate Channels**',
|
|
@ -11,7 +11,7 @@ const command: Command = {
|
|||
.setDescription('Provides information about the server.'),
|
||||
execute: async (interaction) => {
|
||||
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.`,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -3,8 +3,10 @@ import {
|
|||
CommandInteraction,
|
||||
EmbedBuilder,
|
||||
SlashCommandOptionsOnlyBuilder,
|
||||
GuildMember,
|
||||
PermissionsBitField,
|
||||
} from 'discord.js';
|
||||
import { getMember } from '../../util/db.js';
|
||||
import { getMember } from '../../db/db.js';
|
||||
|
||||
interface Command {
|
||||
data: SlashCommandOptionsOnlyBuilder;
|
||||
|
@ -14,7 +16,7 @@ interface Command {
|
|||
const command: Command = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('userinfo')
|
||||
.setDescription('Provides information about the user.')
|
||||
.setDescription('Provides information about the specified user.')
|
||||
.addUserOption((option) =>
|
||||
option
|
||||
.setName('user')
|
||||
|
@ -22,46 +24,127 @@ const command: Command = {
|
|||
.setRequired(true),
|
||||
),
|
||||
execute: async (interaction) => {
|
||||
const userOption = interaction.options.get('user');
|
||||
if (!userOption) {
|
||||
await interaction.reply('User not found');
|
||||
return;
|
||||
}
|
||||
const userOption = interaction.options.get(
|
||||
'user',
|
||||
) as unknown as GuildMember;
|
||||
const user = userOption.user;
|
||||
if (!user) {
|
||||
|
||||
if (!userOption || !user) {
|
||||
await interaction.reply('User not found');
|
||||
return;
|
||||
}
|
||||
const member = await getMember(user.id);
|
||||
const [memberData] = member;
|
||||
if (
|
||||
!interaction.memberPermissions!.has(
|
||||
PermissionsBitField.Flags.ModerateMembers,
|
||||
)
|
||||
) {
|
||||
await interaction.reply(
|
||||
'You do not have permission to view member information.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const memberData = await getMember(user.id);
|
||||
|
||||
const numberOfWarnings = memberData?.moderations.filter(
|
||||
(moderation) => moderation.action === 'warning',
|
||||
).length;
|
||||
const recentWarnings = memberData?.moderations
|
||||
.filter((moderation) => moderation.action === 'warning')
|
||||
.sort((a, b) => b.createdAt!.getTime() - a.createdAt!.getTime())
|
||||
.slice(0, 5);
|
||||
|
||||
const numberOfMutes = memberData?.moderations.filter(
|
||||
(moderation) => moderation.action === 'mute',
|
||||
).length;
|
||||
const currentMute = memberData?.moderations
|
||||
.filter((moderation) => moderation.action === 'mute')
|
||||
.sort((a, b) => b.createdAt!.getTime() - a.createdAt!.getTime())[0];
|
||||
|
||||
const numberOfBans = memberData?.moderations.filter(
|
||||
(moderation) => moderation.action === 'ban',
|
||||
).length;
|
||||
const currentBan = memberData?.moderations
|
||||
.filter((moderation) => moderation.action === 'ban')
|
||||
.sort((a, b) => b.createdAt!.getTime() - a.createdAt!.getTime())[0];
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(`User Information - ${user?.username}`)
|
||||
.setColor(user.accentColor || 'Default')
|
||||
.setColor(user.accentColor || '#5865F2')
|
||||
.setThumbnail(user.displayAvatarURL({ size: 256 }))
|
||||
.setTimestamp()
|
||||
.addFields(
|
||||
{ name: 'Username', value: user.username, inline: false },
|
||||
{ name: 'User ID', value: user.id, inline: false },
|
||||
{
|
||||
name: 'Joined Server',
|
||||
value:
|
||||
interaction.guild?.members.cache
|
||||
.get(user.id)
|
||||
?.joinedAt?.toLocaleString() || 'Not available',
|
||||
name: '👤 Basic Information',
|
||||
value: [
|
||||
`**Username:** ${user.username}`,
|
||||
`**Discord ID:** ${user.id}`,
|
||||
`**Account Created:** ${user.createdAt.toLocaleString()}`,
|
||||
`**Joined Server:** ${
|
||||
interaction.guild?.members.cache
|
||||
.get(user.id)
|
||||
?.joinedAt?.toLocaleString() || 'Not available'
|
||||
}`,
|
||||
`**Currently in Server:** ${memberData?.currentlyInServer ? '✅ Yes' : '❌ No'}`,
|
||||
].join('\n'),
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
name: 'Account Created',
|
||||
value: user.createdAt.toLocaleString(),
|
||||
name: '🛡️ Moderation History',
|
||||
value: [
|
||||
`**Total Warnings:** ${numberOfWarnings || '0'} ${numberOfWarnings ? '⚠️' : ''}`,
|
||||
`**Total Mutes:** ${numberOfMutes || '0'} ${numberOfMutes ? '🔇' : ''}`,
|
||||
`**Total Bans:** ${numberOfBans || '0'} ${numberOfBans ? '🔨' : ''}`,
|
||||
`**Currently Muted:** ${memberData?.currentlyMuted ? '🔇 Yes' : '✅ No'}`,
|
||||
`**Currently Banned:** ${memberData?.currentlyBanned ? '🚫 Yes' : '✅ No'}`,
|
||||
].join('\n'),
|
||||
inline: false,
|
||||
},
|
||||
{
|
||||
name: 'Number of Warnings',
|
||||
value: memberData?.numberOfWarnings.toString() || '0',
|
||||
},
|
||||
{
|
||||
name: 'Number of Bans',
|
||||
value: memberData?.numberOfBans.toString() || '0',
|
||||
},
|
||||
);
|
||||
|
||||
if (recentWarnings && recentWarnings.length > 0) {
|
||||
embed.addFields({
|
||||
name: '⚠️ Recent Warnings',
|
||||
value: recentWarnings
|
||||
.map(
|
||||
(warning, index) =>
|
||||
`${index + 1}. \`${warning.createdAt?.toLocaleDateString() || 'Unknown'}\` - ` +
|
||||
`By <@${warning.moderatorDiscordId}>\n` +
|
||||
`└ Reason: ${warning.reason || 'No reason provided'}`,
|
||||
)
|
||||
.join('\n\n'),
|
||||
inline: false,
|
||||
});
|
||||
}
|
||||
if (memberData?.currentlyMuted && currentMute) {
|
||||
embed.addFields({
|
||||
name: '🔇 Current Mute Details',
|
||||
value: [
|
||||
`**Reason:** ${currentMute.reason || 'No reason provided'}`,
|
||||
`**Duration:** ${currentMute.duration || 'Indefinite'}`,
|
||||
`**Muted At:** ${currentMute.createdAt?.toLocaleString() || 'Unknown'}`,
|
||||
`**Muted By:** <@${currentMute.moderatorDiscordId}>`,
|
||||
].join('\n'),
|
||||
inline: false,
|
||||
});
|
||||
}
|
||||
if (memberData?.currentlyBanned && currentBan) {
|
||||
embed.addFields({
|
||||
name: '📌 Current Ban Details',
|
||||
value: [
|
||||
`**Reason:** ${currentBan.reason || 'No reason provided'}`,
|
||||
`**Duration:** ${currentBan.duration || 'Permanent'}`,
|
||||
`**Banned At:** ${currentBan.createdAt?.toLocaleString() || 'Unknown'}`,
|
||||
].join('\n'),
|
||||
inline: false,
|
||||
});
|
||||
}
|
||||
|
||||
embed.setFooter({
|
||||
text: `Requested by ${interaction.user.username}`,
|
||||
iconURL: interaction.user.displayAvatarURL(),
|
||||
});
|
||||
|
||||
await interaction.reply({ embeds: [embed] });
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue