diff --git a/backend/src/plugins/WelcomeMessage/events/SendWelcomeMessageEvt.ts b/backend/src/plugins/WelcomeMessage/events/SendWelcomeMessageEvt.ts index df983f0e1..f0f14a01e 100644 --- a/backend/src/plugins/WelcomeMessage/events/SendWelcomeMessageEvt.ts +++ b/backend/src/plugins/WelcomeMessage/events/SendWelcomeMessageEvt.ts @@ -1,6 +1,13 @@ -import { Snowflake, TextChannel } from "discord.js"; +import { PermissionsBitField, Snowflake, TextChannel } from "discord.js"; import { TemplateParseError, TemplateSafeValueContainer, renderTemplate } from "../../../templateFormatter.js"; -import { createChunkedMessage, verboseChannelMention, verboseUserMention } from "../../../utils.js"; +import { + createChunkedMessage, + renderRecursively, + verboseChannelMention, + verboseUserMention +} from "../../../utils.js"; +import { MessageContent } from "../../../utils.js"; +import { hasDiscordPermissions } from "../../../utils/hasDiscordPermissions.js"; import { sendDM } from "../../../utils/sendDM.js"; import { guildToTemplateSafeGuild, @@ -28,17 +35,20 @@ export const SendWelcomeMessageEvt = welcomeMessageEvt({ pluginData.state.sentWelcomeMessages.add(member.id); - let formatted; + const templateValues = new TemplateSafeValueContainer({ + member: memberToTemplateSafeMember(member), + user: userToTemplateSafeUser(member.user), + guild: guildToTemplateSafeGuild(member.guild), + }); + + const renderMessageText = (str: string) => renderTemplate(str, templateValues); + + let formatted: MessageContent; try { - formatted = await renderTemplate( - config.message, - new TemplateSafeValueContainer({ - member: memberToTemplateSafeMember(member), - user: userToTemplateSafeUser(member.user), - guild: guildToTemplateSafeGuild(member.guild), - }), - ); + formatted = typeof config.message === "string" + ? await renderMessageText(config.message) + : ((await renderRecursively(config.message, renderMessageText)) as MessageContent); } catch (e) { if (e instanceof TemplateParseError) { pluginData.getPlugin(LogsPlugin).logBotAlert({ @@ -46,7 +56,6 @@ export const SendWelcomeMessageEvt = welcomeMessageEvt({ }); return; } - throw e; } @@ -65,17 +74,49 @@ export const SendWelcomeMessageEvt = welcomeMessageEvt({ const channel = meta.args.member.guild.channels.cache.get(config.send_to_channel as Snowflake); if (!channel || !(channel instanceof TextChannel)) return; - try { - await createChunkedMessage(channel, formatted, { - parse: ["users"], + if ( + !hasDiscordPermissions( + channel.permissionsFor(pluginData.client.user!.id), + PermissionsBitField.Flags.SendMessages | PermissionsBitField.Flags.ViewChannel, + ) + ) { + pluginData.getPlugin(LogsPlugin).logBotAlert({ + body: `Missing permissions to send welcome message in ${verboseChannelMention(channel)}`, }); + return; + } + + if ( + typeof formatted === "object" && formatted.embeds && formatted.embeds.length > 0 && + !hasDiscordPermissions( + channel.permissionsFor(pluginData.client.user!.id), + PermissionsBitField.Flags.EmbedLinks, + ) + ) { + pluginData.getPlugin(LogsPlugin).logBotAlert({ + body: `Missing permissions to send welcome message **with embeds** in ${verboseChannelMention(channel)}`, + }); + return; + } + + try { + if (typeof formatted === "string") { + await createChunkedMessage(channel, formatted, { + parse: ["users"], + }); + } else { + await channel.send({ + ...formatted, + allowedMentions: { + parse: ["users"], + }, + }); + } } catch { pluginData.getPlugin(LogsPlugin).logBotAlert({ - body: `Failed send a welcome message for ${verboseUserMention(member.user)} to ${verboseChannelMention( - channel, - )}`, + body: `Failed to send welcome message for ${verboseUserMention(member.user)} to ${verboseChannelMention(channel)}`, }); } } }, -}); +}); \ No newline at end of file diff --git a/backend/src/plugins/WelcomeMessage/types.ts b/backend/src/plugins/WelcomeMessage/types.ts index 47ee0f681..61f1424c7 100644 --- a/backend/src/plugins/WelcomeMessage/types.ts +++ b/backend/src/plugins/WelcomeMessage/types.ts @@ -1,11 +1,12 @@ import { BasePluginType, guildPluginEventListener } from "vety"; import { z } from "zod"; import { GuildLogs } from "../../data/GuildLogs.js"; +import { zMessageContent } from "../../utils.js"; export const zWelcomeMessageConfig = z.strictObject({ send_dm: z.boolean().default(false), send_to_channel: z.string().nullable().default(null), - message: z.string().nullable().default(null), + message: zMessageContent.nullable().default(null), }); export interface WelcomeMessagePluginType extends BasePluginType { diff --git a/backend/src/utils/sendDM.ts b/backend/src/utils/sendDM.ts index f1ca6fc61..c4c5d2ff8 100644 --- a/backend/src/utils/sendDM.ts +++ b/backend/src/utils/sendDM.ts @@ -1,6 +1,7 @@ -import { MessagePayload, User } from "discord.js"; +import { User } from "discord.js"; import { logger } from "../logger.js"; import { HOURS, createChunkedMessage, isDiscordAPIError } from "../utils.js"; +import { MessageContent } from "../utils.js"; import Timeout = NodeJS.Timeout; let dmsDisabled = false; @@ -16,7 +17,11 @@ export class DMError extends Error {} const error20026 = "The bot cannot currently send DMs"; -export async function sendDM(user: User, content: string | MessagePayload, source: string) { +export async function sendDM( + user: User, + content: MessageContent, + source: string, +) { if (dmsDisabled) { throw new DMError(error20026); } @@ -36,7 +41,6 @@ export async function sendDM(user: User, content: string | MessagePayload, sourc disableDMs(1 * HOURS); throw new DMError(error20026); } - throw e; } -} +} \ No newline at end of file