Skip to content

Commit 6795bf7

Browse files
committed
Merge branch 'master' of github.com:ZeppelinBot/Zeppelin
2 parents 5227e6c + 77e356b commit 6795bf7

6 files changed

Lines changed: 109 additions & 37 deletions

File tree

backend/src/plugins/Logs/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
} from "../../utils/templateSafeObjects.js";
2424
import { TemplateSafeValueContainer } from "../../templateFormatter.js";
2525
import DefaultLogMessages from "../../data/DefaultLogMessages.json" with { type: "json" };
26+
import { TemplateSafeValueContainer } from "templateFormatter.js";
2627

2728
const DEFAULT_BATCH_TIME = 1000;
2829
const MIN_BATCH_TIME = 250;

backend/src/plugins/WelcomeMessage/events/SendWelcomeMessageEvt.ts

Lines changed: 60 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1-
import { Snowflake, TextChannel } from "discord.js";
1+
import { PermissionsBitField, Snowflake, TextChannel } from "discord.js";
22
import { TemplateParseError, TemplateSafeValueContainer, renderTemplate } from "../../../templateFormatter.js";
3-
import { createChunkedMessage, verboseChannelMention, verboseUserMention } from "../../../utils.js";
3+
import {
4+
createChunkedMessage,
5+
renderRecursively,
6+
verboseChannelMention,
7+
verboseUserMention
8+
} from "../../../utils.js";
9+
import { MessageContent } from "../../../utils.js";
10+
import { hasDiscordPermissions } from "../../../utils/hasDiscordPermissions.js";
411
import { sendDM } from "../../../utils/sendDM.js";
512
import {
613
guildToTemplateSafeGuild,
@@ -28,25 +35,27 @@ export const SendWelcomeMessageEvt = welcomeMessageEvt({
2835

2936
pluginData.state.sentWelcomeMessages.add(member.id);
3037

31-
let formatted;
38+
const templateValues = new TemplateSafeValueContainer({
39+
member: memberToTemplateSafeMember(member),
40+
user: userToTemplateSafeUser(member.user),
41+
guild: guildToTemplateSafeGuild(member.guild),
42+
});
43+
44+
const renderMessageText = (str: string) => renderTemplate(str, templateValues);
45+
46+
let formatted: MessageContent;
3247

3348
try {
34-
formatted = await renderTemplate(
35-
config.message,
36-
new TemplateSafeValueContainer({
37-
member: memberToTemplateSafeMember(member),
38-
user: userToTemplateSafeUser(member.user),
39-
guild: guildToTemplateSafeGuild(member.guild),
40-
}),
41-
);
49+
formatted = typeof config.message === "string"
50+
? await renderMessageText(config.message)
51+
: ((await renderRecursively(config.message, renderMessageText)) as MessageContent);
4252
} catch (e) {
4353
if (e instanceof TemplateParseError) {
4454
pluginData.getPlugin(LogsPlugin).logBotAlert({
4555
body: `Error formatting welcome message: ${e.message}`,
4656
});
4757
return;
4858
}
49-
5059
throw e;
5160
}
5261

@@ -65,17 +74,49 @@ export const SendWelcomeMessageEvt = welcomeMessageEvt({
6574
const channel = meta.args.member.guild.channels.cache.get(config.send_to_channel as Snowflake);
6675
if (!channel || !(channel instanceof TextChannel)) return;
6776

68-
try {
69-
await createChunkedMessage(channel, formatted, {
70-
parse: ["users"],
77+
if (
78+
!hasDiscordPermissions(
79+
channel.permissionsFor(pluginData.client.user!.id),
80+
PermissionsBitField.Flags.SendMessages | PermissionsBitField.Flags.ViewChannel,
81+
)
82+
) {
83+
pluginData.getPlugin(LogsPlugin).logBotAlert({
84+
body: `Missing permissions to send welcome message in ${verboseChannelMention(channel)}`,
7185
});
86+
return;
87+
}
88+
89+
if (
90+
typeof formatted === "object" && formatted.embeds && formatted.embeds.length > 0 &&
91+
!hasDiscordPermissions(
92+
channel.permissionsFor(pluginData.client.user!.id),
93+
PermissionsBitField.Flags.EmbedLinks,
94+
)
95+
) {
96+
pluginData.getPlugin(LogsPlugin).logBotAlert({
97+
body: `Missing permissions to send welcome message **with embeds** in ${verboseChannelMention(channel)}`,
98+
});
99+
return;
100+
}
101+
102+
try {
103+
if (typeof formatted === "string") {
104+
await createChunkedMessage(channel, formatted, {
105+
parse: ["users"],
106+
});
107+
} else {
108+
await channel.send({
109+
...formatted,
110+
allowedMentions: {
111+
parse: ["users"],
112+
},
113+
});
114+
}
72115
} catch {
73116
pluginData.getPlugin(LogsPlugin).logBotAlert({
74-
body: `Failed send a welcome message for ${verboseUserMention(member.user)} to ${verboseChannelMention(
75-
channel,
76-
)}`,
117+
body: `Failed to send welcome message for ${verboseUserMention(member.user)} to ${verboseChannelMention(channel)}`,
77118
});
78119
}
79120
}
80121
},
81-
});
122+
});

backend/src/plugins/WelcomeMessage/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { BasePluginType, guildPluginEventListener } from "vety";
22
import { z } from "zod";
33
import { GuildLogs } from "../../data/GuildLogs.js";
4+
import { zMessageContent } from "../../utils.js";
45

56
export const zWelcomeMessageConfig = z.strictObject({
67
send_dm: z.boolean().default(false),
78
send_to_channel: z.string().nullable().default(null),
8-
message: z.string().nullable().default(null),
9+
message: zMessageContent.nullable().default(null),
910
});
1011

1112
export interface WelcomeMessagePluginType extends BasePluginType {

backend/src/utils.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import {
2+
ActionRowBuilder,
23
APIEmbed,
4+
ButtonBuilder,
35
ChannelType,
46
Client,
57
DiscordAPIError,
@@ -18,6 +20,7 @@ import {
1820
InviteType,
1921
LimitedCollection,
2022
Message,
23+
MessageActionRowComponentBuilder,
2124
MessageCreateOptions,
2225
MessageMentionOptions,
2326
PartialGroupDMChannel,
@@ -1306,6 +1309,20 @@ export async function confirm(
13061309
return waitForButtonConfirm(context, content, { restrictToId: userId });
13071310
}
13081311

1312+
export function createDisabledButtonRow(
1313+
row: ActionRowBuilder<MessageActionRowComponentBuilder>
1314+
): ActionRowBuilder<MessageActionRowComponentBuilder> {
1315+
const newRow = new ActionRowBuilder<MessageActionRowComponentBuilder>();
1316+
for (const component of row.components) {
1317+
if (component instanceof ButtonBuilder) {
1318+
newRow.addComponents(
1319+
ButtonBuilder.from(component).setDisabled(true)
1320+
);
1321+
}
1322+
}
1323+
return newRow;
1324+
}
1325+
13091326
export function messageSummary(msg: SavedMessage) {
13101327
// Regular text content
13111328
let result = "```\n" + (msg.data.content ? escapeCodeBlock(msg.data.content) : "<no text content>") + "```";

backend/src/utils/sendDM.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { MessagePayload, User } from "discord.js";
1+
import { User } from "discord.js";
22
import { logger } from "../logger.js";
33
import { HOURS, createChunkedMessage, isDiscordAPIError } from "../utils.js";
4+
import { MessageContent } from "../utils.js";
45
import Timeout = NodeJS.Timeout;
56

67
let dmsDisabled = false;
@@ -16,7 +17,11 @@ export class DMError extends Error {}
1617

1718
const error20026 = "The bot cannot currently send DMs";
1819

19-
export async function sendDM(user: User, content: string | MessagePayload, source: string) {
20+
export async function sendDM(
21+
user: User,
22+
content: MessageContent,
23+
source: string,
24+
) {
2025
if (dmsDisabled) {
2126
throw new DMError(error20026);
2227
}
@@ -36,7 +41,6 @@ export async function sendDM(user: User, content: string | MessagePayload, sourc
3641
disableDMs(1 * HOURS);
3742
throw new DMError(error20026);
3843
}
39-
4044
throw e;
4145
}
42-
}
46+
}

backend/src/utils/waitForInteraction.ts

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
import moment from "moment-timezone";
1010
import { v4 as uuidv4 } from "uuid";
1111
import { GenericCommandSource, isContextInteraction, sendContextResponse } from "../pluginUtils.js";
12-
import { noop } from "../utils.js";
12+
import { noop, createDisabledButtonRow } from "../utils.js";
1313

1414
export async function waitForButtonConfirm(
1515
context: GenericCommandSource,
@@ -24,34 +24,42 @@ export async function waitForButtonConfirm(
2424
.setStyle(ButtonStyle.Success)
2525
.setLabel(options?.confirmText || "Confirm")
2626
.setCustomId(`confirmButton:${idMod}:${uuidv4()}`),
27-
2827
new ButtonBuilder()
2928
.setStyle(ButtonStyle.Danger)
3029
.setLabel(options?.cancelText || "Cancel")
3130
.setCustomId(`cancelButton:${idMod}:${uuidv4()}`),
3231
]);
3332
const message = await sendContextResponse(context, { ...toPost, components: [row] }, true);
34-
3533
const collector = message.createMessageComponentCollector({ time: 10000 });
3634

3735
collector.on("collect", (interaction: MessageComponentInteraction) => {
3836
if (options?.restrictToId && options.restrictToId !== interaction.user.id) {
3937
interaction
4038
.reply({ content: `You are not permitted to use these buttons.`, ephemeral: true })
41-
// tslint:disable-next-line no-console
42-
.catch((err) => console.trace(err.message));
43-
} else {
44-
if (interaction.customId.startsWith(`confirmButton:${idMod}:`)) {
45-
if (!contextIsInteraction) message.delete();
46-
resolve(true);
47-
} else if (interaction.customId.startsWith(`cancelButton:${idMod}:`)) {
48-
if (!contextIsInteraction) message.delete();
49-
resolve(false);
39+
.catch(noop);
40+
} else if (interaction.customId.startsWith(`confirmButton:${idMod}:`)) {
41+
if (!contextIsInteraction) {
42+
message.delete().catch(noop);
43+
} else {
44+
interaction.update({ components: [createDisabledButtonRow(row)] }).catch(noop);
45+
}
46+
resolve(true);
47+
} else if (interaction.customId.startsWith(`cancelButton:${idMod}:`)) {
48+
if (!contextIsInteraction) {
49+
message.delete().catch(noop);
50+
} else {
51+
interaction.update({ components: [createDisabledButtonRow(row)] }).catch(noop);
5052
}
53+
resolve(false);
5154
}
5255
});
56+
5357
collector.on("end", () => {
54-
if (!contextIsInteraction && message.deletable) message.delete().catch(noop);
58+
if (!contextIsInteraction) {
59+
if (message.deletable) message.delete().catch(noop);
60+
} else {
61+
message.edit({ components: [createDisabledButtonRow(row)] }).catch(noop);
62+
}
5563
resolve(false);
5664
});
5765
});

0 commit comments

Comments
 (0)