diff options
-rw-r--r-- | src/commands/moderation/ban.ts | 2 | ||||
-rw-r--r-- | src/lib/common/AutoMod.ts | 18 | ||||
-rw-r--r-- | src/lib/extensions/discord.js/BushGuild.ts | 2 | ||||
-rw-r--r-- | src/lib/extensions/discord.js/BushGuildMember.ts | 5 | ||||
-rw-r--r-- | src/listeners/guild/guildMemberAdd.ts | 80 | ||||
-rw-r--r-- | src/listeners/guild/guildMemberRemove.ts | 4 | ||||
-rw-r--r-- | src/listeners/guild/joinRoles.ts | 119 |
7 files changed, 141 insertions, 89 deletions
diff --git a/src/commands/moderation/ban.ts b/src/commands/moderation/ban.ts index 7f0b91f..aeb03f0 100644 --- a/src/commands/moderation/ban.ts +++ b/src/commands/moderation/ban.ts @@ -127,6 +127,8 @@ export default class BanCommand extends BushCommand { const responseMessage = (): string => { const victim = util.format.input(user.tag); switch (responseCode) { + case banResponse.ALREADY_BANNED: + return `${util.emojis.error} ${victim} is already banned.`; case banResponse.MISSING_PERMISSIONS: return `${util.emojis.error} Could not ban ${victim} because I am missing the **Ban Members** permission.`; case banResponse.ACTION_ERROR: diff --git a/src/lib/common/AutoMod.ts b/src/lib/common/AutoMod.ts index 50f5d3e..7a30820 100644 --- a/src/lib/common/AutoMod.ts +++ b/src/lib/common/AutoMod.ts @@ -1,4 +1,4 @@ -import { Moderation, type BushButtonInteraction, type BushMessage } from '#lib'; +import { banResponse, Moderation, type BushButtonInteraction, type BushMessage } from '#lib'; import { GuildMember, MessageActionRow, MessageButton, MessageEmbed, type TextChannel } from 'discord.js'; /** @@ -308,18 +308,20 @@ export class AutoMod { evidence: (interaction.message as BushMessage).url ?? undefined }); - if (result === 'success') + const victimUserFormatted = (await util.resolveNonCachedUser(userId))?.tag ?? userId; + if (result === banResponse.SUCCESS) return interaction.reply({ - content: `${util.emojis.success} Successfully banned **${ - interaction.guild?.members.cache.get(userId)?.user.tag ?? userId - }**.`, + content: `${util.emojis.success} Successfully banned **${victimUserFormatted}**.`, + ephemeral: true + }); + else if (result === banResponse.DM_ERROR) + return interaction.reply({ + content: `${util.emojis.warn} Banned ${victimUserFormatted} however I could not send them a dm.`, ephemeral: true }); else return interaction.reply({ - content: `${util.emojis.error} Could not ban **${ - interaction.guild?.members.cache.get(userId)?.user.tag ?? userId - }**: \`${result}\` .`, + content: `${util.emojis.error} Could not ban **${victimUserFormatted}**: \`${result}\` .`, ephemeral: true }); } diff --git a/src/lib/extensions/discord.js/BushGuild.ts b/src/lib/extensions/discord.js/BushGuild.ts index 0011ce6..33ee3fc 100644 --- a/src/lib/extensions/discord.js/BushGuild.ts +++ b/src/lib/extensions/discord.js/BushGuild.ts @@ -163,6 +163,8 @@ export class BushGuild extends Guild { const user = (await util.resolveNonCachedUser(options.user))!; const moderator = client.users.resolve(options.moderator ?? client.user!)!; + if ((await this.bans.fetch()).has(user.id)) return banResponse.ALREADY_BANNED; + const ret = await (async () => { // dm user dmSuccessEvent = await Moderation.punishDM({ diff --git a/src/lib/extensions/discord.js/BushGuildMember.ts b/src/lib/extensions/discord.js/BushGuildMember.ts index 6a95ebf..54fb3f0 100644 --- a/src/lib/extensions/discord.js/BushGuildMember.ts +++ b/src/lib/extensions/discord.js/BushGuildMember.ts @@ -465,7 +465,7 @@ export class BushGuildMember extends GuildMember { * @returns A status message for banning the user. * @emits {@link BushClientEvents.bushBan} */ - public async bushBan(options: BushBanOptions): Promise<BanResponse> { + public async bushBan(options: BushBanOptions): Promise<Exclude<BanResponse, typeof banResponse['ALREADY_BANNED']>> { // checks if (!this.guild.me!.permissions.has('BAN_MEMBERS') || !this.bannable) return banResponse.MISSING_PERMISSIONS; @@ -1025,7 +1025,8 @@ export const kickResponse = Object.freeze({ export const banResponse = Object.freeze({ ...dmResponse, ...permissionsResponse, - ...punishmentEntryAdd + ...punishmentEntryAdd, + ALREADY_BANNED: 'already banned' } as const); export const blockResponse = Object.freeze({ diff --git a/src/listeners/guild/guildMemberAdd.ts b/src/listeners/guild/guildMemberAdd.ts index 4c7d498..22961e9 100644 --- a/src/listeners/guild/guildMemberAdd.ts +++ b/src/listeners/guild/guildMemberAdd.ts @@ -1,5 +1,5 @@ -import { BushListener, StickyRole, type BushClientEvents, type BushGuildMember, type BushTextChannel } from '#lib'; -import { MessageEmbed, type Snowflake } from 'discord.js'; +import { BushListener, type BushClientEvents, type BushGuildMember, type BushTextChannel } from '#lib'; +import { MessageEmbed } from 'discord.js'; export default class GuildMemberAddListener extends BushListener { public constructor() { @@ -12,10 +12,9 @@ export default class GuildMemberAddListener extends BushListener { public override async exec(...[member]: BushClientEvents['guildMemberAdd']) { void this.sendWelcomeMessage(member); - void this.joinAndStickyRoles(member); } - public async sendWelcomeMessage(member: BushGuildMember) { + private async sendWelcomeMessage(member: BushGuildMember) { if (client.config.isDevelopment) return; const welcomeChannel = await member.guild.getSetting('welcomeChannel'); if (!welcomeChannel) return; @@ -44,77 +43,4 @@ export default class GuildMemberAddListener extends BushListener { ) ); } - - public async joinAndStickyRoles(member: BushGuildMember) { - if (client.config.isDevelopment) return; - if (await member.guild.hasFeature('stickyRoles')) { - const hadRoles = await StickyRole.findOne({ where: { guild: member.guild.id, user: member.id } }); - if (hadRoles?.roles?.length) { - const rolesArray = hadRoles.roles - .map((roleID: Snowflake) => { - const role = member.guild.roles.cache.get(roleID); - if (role && !member.roles.cache.has(roleID)) { - if (role.name !== '@everyone' || !role.managed) return role.id; - } - }) - .filter((role) => role) as Snowflake[]; - if (hadRoles.nickname && member.manageable) { - void member.setNickname(hadRoles.nickname).catch(() => {}); - } - if (rolesArray?.length) { - const addedRoles = await member.roles.add(rolesArray, "Returning member's previous roles.").catch(() => - member.guild.sendLogChannel('error', { - embeds: [ - { - title: 'Sticky Roles Error', - description: `There was an error returning ${util.format.input(member.user.tag)}'s roles.`, - color: util.colors.error - } - ] - }) - ); - if (addedRoles) { - void client.console.info( - 'guildMemberAdd', - `Assigned sticky roles to ${util.format.inputLog(member.user.tag)} in ${util.format.inputLog(member.guild.name)}.` - ); - } else if (!addedRoles) { - const failedRoles: string[] = []; - for (let i = 0; i < rolesArray.length; i++) { - await member.roles - .add(rolesArray[i], "[Fallback] Returning member's previous roles.") - .catch(() => failedRoles.push(rolesArray[i])); - } - if (failedRoles.length) { - void client.console.warn('guildMemberAdd', `Failed assigning the following roles on Fallback: ${failedRoles}`); - } else { - void client.console.info( - 'guildMemberAdd', - `[Fallback] Assigned sticky roles to ${util.format.inputLog(member.user.tag)} in ${util.format.inputLog( - member.guild.name - )}.` - ); - } - } - } - } else { - const joinRoles = await member.guild.getSetting('joinRoles'); - if (!joinRoles || !joinRoles.length) return; - await member.roles - .add(joinRoles, 'Join roles.') - .then(() => - client.console.info( - 'guildMemberAdd', - `Assigned join roles to ${util.format.inputLog(member.user.tag)} in ${util.format.inputLog(member.guild.name)}.` - ) - ) - .catch(() => - member.guild.error( - 'Join Roles Error', - `Failed to assign join roles to ${util.format.input(member.user.tag)}, in ${util.format.input(member.guild.name)}.` - ) - ); - } - } - } } diff --git a/src/listeners/guild/guildMemberRemove.ts b/src/listeners/guild/guildMemberRemove.ts index ae4c4df..323bd24 100644 --- a/src/listeners/guild/guildMemberRemove.ts +++ b/src/listeners/guild/guildMemberRemove.ts @@ -22,7 +22,7 @@ export default class GuildMemberRemoveListener extends BushListener { void this.stickyRoles(member); } - public async sendWelcomeMessage(member: BushGuildMember | PartialBushGuildMember) { + private async sendWelcomeMessage(member: BushGuildMember | PartialBushGuildMember) { if (client.config.isDevelopment) return; const user = member.partial ? await client.users.fetch(member.id) : member.user; await util.sleep(0.05); // ban usually triggers after member leave @@ -54,7 +54,7 @@ export default class GuildMemberRemoveListener extends BushListener { ); } - public async stickyRoles(member: BushGuildMember | PartialBushGuildMember) { + private async stickyRoles(member: BushGuildMember | PartialBushGuildMember) { if (!(await member.guild.hasFeature('stickyRoles'))) return; if (member.partial) { await member.guild.members.fetch(); // try to prevent in the future diff --git a/src/listeners/guild/joinRoles.ts b/src/listeners/guild/joinRoles.ts new file mode 100644 index 0000000..a50f7ed --- /dev/null +++ b/src/listeners/guild/joinRoles.ts @@ -0,0 +1,119 @@ +import { BushListener, StickyRole, type BushClientEvents, type BushGuildMember } from '#lib'; +import { type Snowflake } from 'discord.js'; + +export default class JoinRolesListener extends BushListener { + public constructor() { + super('joinRoles', { + emitter: 'client', + event: 'guildMemberUpdate', // listens to guildMemberUpdate so that the role's aren't given before the member accepts the welcome screen + category: 'guild' + }); + } + + public override async exec(...[oldMember, newMember]: BushClientEvents['guildMemberUpdate']) { + if (client.config.isDevelopment) return; + if (oldMember.pending === false && newMember.pending === true) { + const feat = { + stickyRoles: await newMember.guild.hasFeature('stickyRoles'), + joinRoles: (await newMember.guild.getSetting('joinRoles')).length > 0 + }; + + if (!feat.stickyRoles && !feat.joinRoles) return; + + let addJoinRoles = true; + if (feat.stickyRoles) { + const addedStickyRoles = await this.stickyRoles(newMember); + if (addedStickyRoles) addJoinRoles = false; + } + + if (feat.joinRoles && addJoinRoles) { + void this.joinRoles(newMember); + } + } + } + + /** + * Adds sticky roles to a user. + * @param member The member to add sticky roles to. + * @returns Whether or not sticky roles were added. + */ + private async stickyRoles(member: BushGuildMember): Promise<boolean> { + const hadRoles = await StickyRole.findOne({ where: { guild: member.guild.id, user: member.id } }); + + if (hadRoles?.roles?.length) { + const rolesArray = hadRoles.roles + .map((roleID: Snowflake) => { + const role = member.guild.roles.cache.get(roleID); + if (role && !member.roles.cache.has(roleID)) { + if (role.name !== '@everyone' || !role.managed) return role.id; + } + }) + .filter((role) => role) as Snowflake[]; + if (hadRoles.nickname && member.manageable) { + void member.setNickname(hadRoles.nickname).catch(() => {}); + } + if (rolesArray?.length) { + const addedRoles = await member.roles.add(rolesArray, "Returning member's previous roles.").catch(() => { + void member.guild.sendLogChannel('error', { + embeds: [ + { + title: 'Sticky Roles Error', + description: `There was an error returning ${util.format.input(member.user.tag)}'s roles.`, + color: util.colors.error + } + ] + }); + return false as const; + }); + if (addedRoles) { + void client.console.info( + 'guildMemberAdd', + `Assigned sticky roles to ${util.format.inputLog(member.user.tag)} in ${util.format.inputLog(member.guild.name)}.` + ); + } else if (!addedRoles) { + const failedRoles: string[] = []; + for (let i = 0; i < rolesArray.length; i++) { + await member.roles + .add(rolesArray[i], "[Fallback] Returning member's previous roles.") + .catch(() => failedRoles.push(rolesArray[i])); + } + if (failedRoles.length) { + void client.console.warn('guildMemberAdd', `Failed assigning the following roles on Fallback: ${failedRoles}`); + } else { + void client.console.info( + 'guildMemberAdd', + `[Fallback] Assigned sticky roles to ${util.format.inputLog(member.user.tag)} in ${util.format.inputLog( + member.guild.name + )}.` + ); + } + } + return true; + } + } + return false; + } + + /** + * Add the guild's join roles to the member. + * @param member The member to add the join roles to. + */ + private async joinRoles(member: BushGuildMember): Promise<void> { + const joinRoles = await member.guild.getSetting('joinRoles'); + if (!joinRoles || !joinRoles.length) return; + await member.roles + .add(joinRoles, 'Join roles.') + .then(() => + client.console.info( + 'guildMemberAdd', + `Assigned join roles to ${util.format.inputLog(member.user.tag)} in ${util.format.inputLog(member.guild.name)}.` + ) + ) + .catch(() => + member.guild.error( + 'Join Roles Error', + `Failed to assign join roles to ${util.format.input(member.user.tag)}, in ${util.format.input(member.guild.name)}.` + ) + ); + } +} |