diff --git a/src/lib/discord/SkyraEmbed.ts b/src/lib/discord/SkyraEmbed.ts index 309ff2f5462..35ca40e1c89 100644 --- a/src/lib/discord/SkyraEmbed.ts +++ b/src/lib/discord/SkyraEmbed.ts @@ -1,9 +1,10 @@ import { ZeroWidthSpace } from '#utils/constants'; import { EmbedBuilder } from '@discordjs/builders'; +import { isNullishOrEmpty } from '@sapphire/utilities'; export class SkyraEmbed extends EmbedBuilder { public splitFields(contentOrTitle: string | string[], rawContent?: string | string[]) { - if (typeof contentOrTitle === 'undefined') return this; + if (isNullishOrEmpty(contentOrTitle)) return this; let title: string; let content: string | string[]; diff --git a/src/listeners/guilds/members/notMutedMemberAddInitialRole.ts b/src/listeners/guilds/members/notMutedMemberAddInitialRole.ts index ba368b7fc4d..8f727d97db9 100644 --- a/src/listeners/guilds/members/notMutedMemberAddInitialRole.ts +++ b/src/listeners/guilds/members/notMutedMemberAddInitialRole.ts @@ -2,11 +2,15 @@ import { GuildSettings, readSettings, writeSettings } from '#lib/database'; import { Events } from '#lib/types'; import { ApplyOptions } from '@sapphire/decorators'; import { Listener } from '@sapphire/framework'; -import type { GuildMember } from 'discord.js'; +import { isNullish } from '@sapphire/utilities'; +import { PermissionFlagsBits, type GuildMember } from 'discord.js'; @ApplyOptions({ event: Events.NotMutedMemberAdd }) export class UserListener extends Listener { public async run(member: GuildMember) { + // If the bot cannot manage roles, do not proceed: + if (!this.canGiveRoles(member)) return; + const [initial, initialHumans, initialBots] = await readSettings(member, [ GuildSettings.Roles.Initial, GuildSettings.Roles.InitialHumans, @@ -22,6 +26,16 @@ export class UserListener extends Listener { return member.roles.add(role); } - return writeSettings(member, [[GuildSettings.Roles.Initial, null]]); + const key = role // + ? GuildSettings.Roles.Initial + : member.user.bot + ? GuildSettings.Roles.InitialBots + : GuildSettings.Roles.InitialHumans; + return writeSettings(member, [[key, null]]); + } + + private canGiveRoles(member: GuildMember) { + const permissions = member.guild.members.me?.permissions; + return !isNullish(permissions) && permissions.has(PermissionFlagsBits.ManageRoles); } } diff --git a/src/listeners/messages/guildMessageDeleteNotify.ts b/src/listeners/messages/guildMessageDeleteNotify.ts index 1e5a217fbdb..cccdfea0d51 100644 --- a/src/listeners/messages/guildMessageDeleteNotify.ts +++ b/src/listeners/messages/guildMessageDeleteNotify.ts @@ -7,7 +7,7 @@ import { EmbedBuilder } from '@discordjs/builders'; import { ApplyOptions } from '@sapphire/decorators'; import { isNsfwChannel } from '@sapphire/discord.js-utilities'; import { Listener } from '@sapphire/framework'; -import { cutText, isNullish } from '@sapphire/utilities'; +import { cutText, isNullish, isNullishOrEmpty } from '@sapphire/utilities'; @ApplyOptions({ event: Events.GuildMessageDelete }) export class UserListener extends Listener { @@ -26,14 +26,19 @@ export class UserListener extends Listener { if (ignoredDeletes.some((id) => id === message.channel.id && message.channel.parentId === id)) return; if (ignoredAll.some((id) => id === message.channel.id || message.channel.parentId === id)) return; - this.container.client.emit(Events.GuildMessageLog, message.guild, logChannelId, key, () => - new EmbedBuilder() + this.container.client.emit(Events.GuildMessageLog, message.guild, logChannelId, key, () => { + const embed = new EmbedBuilder() .setColor(Colors.Red) .setAuthor(getFullEmbedAuthor(message.author, message.url)) - .setDescription(cutText(getContent(message) || '', 1900)) .setFooter({ text: t(LanguageKeys.Events.Messages.MessageDelete, { channel: `#${message.channel.name}` }) }) - .setImage(getImage(message)!) - .setTimestamp() - ); + .setTimestamp(); + + const content = getContent(message); + if (!isNullishOrEmpty(content)) embed.setDescription(cutText(content, 1900)); + const image = getImage(message); + if (!isNullish(image)) embed.setImage(image); + + return embed; + }); } } diff --git a/src/listeners/reactions/rawReactionAddNotify.ts b/src/listeners/reactions/rawReactionAddNotify.ts index 6e1f6896b44..5d7badb9640 100644 --- a/src/listeners/reactions/rawReactionAddNotify.ts +++ b/src/listeners/reactions/rawReactionAddNotify.ts @@ -8,9 +8,10 @@ import { getEmojiId, getEmojiReactionFormat, getEncodedTwemoji, getTwemojiUrl, t import { getFullEmbedAuthor } from '#utils/util'; import { EmbedBuilder } from '@discordjs/builders'; import { ApplyOptions } from '@sapphire/decorators'; +import type { GuildTextBasedChannelTypes } from '@sapphire/discord.js-utilities'; import { Listener } from '@sapphire/framework'; import { isNullish } from '@sapphire/utilities'; -import { Collection } from 'discord.js'; +import { Collection, PermissionFlagsBits } from 'discord.js'; @ApplyOptions({ event: Events.RawReactionAdd }) export class UserListener extends Listener { @@ -19,6 +20,9 @@ export class UserListener extends Listener { private kTimerSweeper: NodeJS.Timeout | null = null; public async run(data: LLRCData, emoji: SerializedEmoji) { + // If the bot cannot fetch messages, do not proceed: + if (!this.canFetchMessages(data.channel)) return; + const key = GuildSettings.Channels.Logs.Reaction; const [allowedEmojis, logChannelId, twemojiEnabled, ignoreChannels, ignoreReactionAdd, ignoreAllEvents, t] = await readSettings( data.guild, @@ -76,7 +80,12 @@ export class UserListener extends Listener { if (this.kTimerSweeper) clearInterval(this.kTimerSweeper); } - protected async retrieveCount(data: LLRCData, emoji: SerializedEmoji) { + private canFetchMessages(channel: GuildTextBasedChannelTypes) { + const permissions = channel.permissionsFor(this.container.client.id!); + return !isNullish(permissions) && permissions.has(PermissionFlagsBits.ReadMessageHistory); + } + + private async retrieveCount(data: LLRCData, emoji: SerializedEmoji) { const id = `${data.messageId}.${getEmojiId(emoji)}`; // Pull from sync queue, and if it exists, await