Skip to content

Commit 05184d4

Browse files
#552: Switch server config from taking image urls to file uploads (#629)
* Switch server config from taking image urls to file uploads - Added two new commands, since there are enough thumbnail configurations to need to be split across two modals Fixes #552 * Switch server config from taking image urls to file uploads - Added missing changelog entry * #552 Code review changes: - Fixed new command descriptions - Re-added permission bits to new commands - Updated for looping pattern
1 parent be046f1 commit 05184d4

5 files changed

Lines changed: 127 additions & 119 deletions

File tree

ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
### Premium Features
77
- Added premium commands `/festival start-gp` and `/festival close-gp` for toggling times where gp contributions are multiplied (old festival commands renamed to `/festival start-xp` and `/festival close-xp`)
88
- Added the `nickname` option to `/config-premium` to allow bot managers to set a nickname for BountyBot that won't get cleared by toggling festivals on or off
9+
- Split premium configuration of thumbnails used for certain actions out of `/config-premium` into two new commands: `/config-server-thumbnails-premium` and `/config-user-thumbnails-premium`. These new commands use the newer image upload UI in modals for easier file selection.
910
### Other Changes
1011
- The completing a bounty via the select in the bounty board can now add turn-ins like the slash command
1112
- Fixed Goal Point contributions not showing up in reward messages

source/frontend/commands/_commandDictionary.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ exports.commandFiles = [
66
"bounty",
77
"commands.js",
88
"config-premium.js",
9+
"config-server-thumbnails-premium.js",
10+
"config-user-thumbnails-premium.js",
911
"config-server.js",
1012
"create-default",
1113
"data-policy.js",

source/frontend/commands/config-premium.js

Lines changed: 0 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -44,83 +44,6 @@ module.exports = new CommandWrapper(mainId, "Configure premium BountyBot setting
4444
}
4545
}
4646

47-
const toastThumbnailURL = interaction.options.getString("toast-thumbnail-url");
48-
if (toastThumbnailURL) {
49-
try {
50-
new URL(toastThumbnailURL);
51-
updatePayload.toastThumbnailURL = toastThumbnailURL;
52-
content += `\n- The toast thumbnail was set to <${toastThumbnailURL}>.`;
53-
} catch (error) {
54-
errors.push(error.message);
55-
}
56-
}
57-
58-
const openBountyThumbnailURL = interaction.options.getString("open-bounty-thumbnail-url");
59-
if (openBountyThumbnailURL) {
60-
try {
61-
new URL(openBountyThumbnailURL);
62-
updatePayload.openBountyThumbnailURL = openBountyThumbnailURL;
63-
content += `\n- The open bounty thumbnail was set to <${openBountyThumbnailURL}>.`;
64-
} catch (error) {
65-
errors.push(error.message);
66-
}
67-
}
68-
69-
const completedBountyThumbnailURL = interaction.options.getString("completed-bounty-thumbnail-url");
70-
if (completedBountyThumbnailURL) {
71-
try {
72-
new URL(completedBountyThumbnailURL);
73-
updatePayload.completedBountyThumbnailURL = completedBountyThumbnailURL;
74-
content += `\n- The completed bounty thumbnail was set to <${completedBountyThumbnailURL}>.`;
75-
} catch (error) {
76-
errors.push(error.message);
77-
}
78-
}
79-
80-
const deletedBountyThumbnailURL = interaction.options.getString("deleted-bounty-thumbnail-url");
81-
if (deletedBountyThumbnailURL) {
82-
try {
83-
new URL(deletedBountyThumbnailURL);
84-
updatePayload.deletedBountyThumbnailURL = deletedBountyThumbnailURL;
85-
content += `\n- The deleted bounty thumbnail was set to <${deletedBountyThumbnailURL}>.`;
86-
} catch (error) {
87-
errors.push(error.message);
88-
}
89-
}
90-
91-
const scoreboardThumbnailURL = interaction.options.getString("scoreboard-thumbnail-url");
92-
if (scoreboardThumbnailURL) {
93-
try {
94-
new URL(scoreboardThumbnailURL);
95-
updatePayload.scoreboardThumbnailURL = scoreboardThumbnailURL;
96-
content += `\n- The scoreboard thumbnail was set to <${scoreboardThumbnailURL}>.`;
97-
} catch (error) {
98-
errors.push(error.message);
99-
}
100-
}
101-
102-
const goalCompletionThumbnailURL = interaction.options.getString("goal-completion-thumbnail-url");
103-
if (goalCompletionThumbnailURL) {
104-
try {
105-
new URL(goalCompletionThumbnailURL);
106-
updatePayload.goalCompletionThumbnailURL = goalCompletionThumbnailURL;
107-
content += `\n- The server bonuses thumbnail was set to <${goalCompletionThumbnailURL}>.`;
108-
} catch (error) {
109-
errors.push(error.message);
110-
}
111-
}
112-
113-
const raffleThumbnailURL = interaction.options.getString("raffle-thumbnail-url");
114-
if (raffleThumbnailURL) {
115-
try {
116-
new URL(raffleThumbnailURL);
117-
updatePayload.raffleThumbnailURL = raffleThumbnailURL;
118-
content += `\n- The server bonuses thumbnail was set to <${raffleThumbnailURL}>.`;
119-
} catch (error) {
120-
errors.push(error.message);
121-
}
122-
}
123-
12447
origin.company.update(updatePayload);
12548
if (errors.length > 0) {
12649
content += `\n\nThe following errors were encountered:\n${unorderedList(errors)}`;
@@ -145,47 +68,5 @@ module.exports = new CommandWrapper(mainId, "Configure premium BountyBot setting
14568
name: "bounty-slots",
14669
description: `Configure the max number (between 1 and ${GLOBAL_MAX_BOUNTY_SLOTS}) of bounty slots hunters can have (default 5)`,
14770
required: false
148-
},
149-
{
150-
type: "String",
151-
name: "toast-thumbnail-url",
152-
description: "Set a url pointing to an image to use as thumbnail on toasts",
153-
required: false
154-
},
155-
{
156-
type: "String",
157-
name: "open-bounty-thumbnail-url",
158-
description: "Set a url pointing to an image to use as thumbnail on open bounties",
159-
required: false
160-
},
161-
{
162-
type: "String",
163-
name: "completed-bounty-thumbnail-url",
164-
description: "Set a url pointing to an image to use as thumbnail on completed bounties",
165-
required: false
166-
},
167-
{
168-
type: "String",
169-
name: "deleted-bounty-thumbnail-url",
170-
description: "Set a url pointing to an image to use as thumbnail on deleted bounties",
171-
required: false
172-
},
173-
{
174-
type: "String",
175-
name: "scoreboard-thumbnail-url",
176-
description: "Set a url pointing to an image to use as thumbnail on the scoreboard",
177-
required: false
178-
},
179-
{
180-
type: "String",
181-
name: "goal-completion-thumbnail-url",
182-
description: "Set a url pointing to an image to use as thumbnail in server goal completion messages",
183-
required: false
184-
},
185-
{
186-
type: "String",
187-
name: "raffle-thumbnail-url",
188-
description: "Set a url pointing to an image to use as thumbnail in raffle winner messages",
189-
required: false
19071
}
19172
);
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
const { PermissionFlagsBits, InteractionContextType, MessageFlags, ModalBuilder, LabelBuilder, FileUploadBuilder } = require("discord.js");
2+
const { SKIP_INTERACTION_HANDLING } = require('../../constants');
3+
const { CommandWrapper } = require("../classes");
4+
const { timeConversion } = require('../../shared');
5+
6+
const mainId = "config-server-thumbnails-premium";
7+
const imageFieldToPayloadPropertyAndMessage = new Map(Object.entries({
8+
"scoreboard-thumbnail": {
9+
payloadProperty: "scoreboardThumbnailURL",
10+
messageStub: "scoreboard thumbnail",
11+
description: "Set an image to use as thumbnail on the scoreboard",
12+
modalLabel: "Scoreboard Thumbnail"
13+
},
14+
"goal-completion-thumbnail": {
15+
payloadProperty: "goalCompletionThumbnailURL",
16+
messageStub: "goal completion thumbnail",
17+
description: "Set an image to use as thumbnail in server goal completion messages",
18+
modalLabel: "Goal Completion Thumbnail"
19+
},
20+
"raffle-thumbnail": {
21+
payloadProperty: "raffleThumbnailURL",
22+
messageStub: "raffle thumbnail",
23+
description: "Set an image to use as thumbnail in raffle winner messages",
24+
modalLabel: "Raffle Thumbnail"
25+
}
26+
}));
27+
module.exports = new CommandWrapper(mainId, "Configure thumbnails for server messages (Premium)", PermissionFlagsBits.ManageGuild, true, [InteractionContextType.Guild], 3000,
28+
async (interaction, origin, runMode) => {
29+
const modalLabelComponents = [];
30+
for (const [ imageField, { description, modalLabel } ] of imageFieldToPayloadPropertyAndMessage.keys()) {
31+
modalLabelComponents.push(new LabelBuilder().setLabel(modalLabel).setDescription(description)
32+
.setFileUploadComponent(
33+
new FileUploadBuilder().setCustomId(imageField)
34+
.setRequired(false)
35+
))
36+
}
37+
const modal = new ModalBuilder().setCustomId(`${SKIP_INTERACTION_HANDLING}${interaction.id}`).setTitle("Configure Server Thumbnails").addLabelComponents(modalLabelComponents);
38+
39+
interaction.showModal(modal);
40+
const modalInteraction = await interaction.awaitModalSubmit({ filter: incoming => incoming.customId === modal.data.custom_id, time: timeConversion(5, "m", "ms") });
41+
const updatePayload = {};
42+
43+
let replyContent = "The following thumbnails have been configured:";
44+
45+
for (const [ imageField, { payloadProperty, messageStub } ] of imageFieldToPayloadPropertyAndMessage.keys()) {
46+
const thumbnailCollection = modalInteraction.fields.getUploadedFiles(imageField);
47+
if (thumbnailCollection) {
48+
const firstAttachment = thumbnailCollection.first();
49+
if (firstAttachment) {
50+
updatePayload[payloadProperty] = firstAttachment.url;
51+
replyContent += `\n- The ${messageStub} was set to <${firstAttachment.url}>.`;
52+
}
53+
}
54+
}
55+
56+
origin.company.update(updatePayload);
57+
modalInteraction.reply({ content: replyContent, flags: MessageFlags.Ephemeral });
58+
}
59+
);
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
const { PermissionFlagsBits, InteractionContextType, MessageFlags, ModalBuilder, LabelBuilder, FileUploadBuilder } = require("discord.js");
2+
const { SKIP_INTERACTION_HANDLING } = require('../../constants');
3+
const { CommandWrapper } = require("../classes");
4+
const { timeConversion } = require('../../shared');
5+
6+
const mainId = "config-user-thumbnails-premium";
7+
const imageFieldToPayloadPropertyAndMessage = new Map(Object.entries({
8+
"toast-thumbnail": {
9+
payloadProperty: "toastThumbnailURL",
10+
messageStub: "toast thumbnail",
11+
description: "Set an image to use as thumbnail on toasts",
12+
modalLabel: "Toast Thumbnail"
13+
},
14+
"open-bounty-thumbnail": {
15+
payloadProperty: "openBountyThumbnailURL",
16+
messageStub: "open bounty thumbnail",
17+
description: "Set an image to use as thumbnail on open bounties",
18+
modalLabel: "Open Bounty Thumbnail"
19+
},
20+
"completed-bounty-thumbnail": {
21+
payloadProperty: "completedBountyThumbnailURL",
22+
messageStub: "completed bounty thumbnail",
23+
description: "Set an image to use as thumbnail on completed bounties",
24+
modalLabel: "Completed Bounty Thumbnail"
25+
},
26+
"deleted-bounty-thumbnail": {
27+
payloadProperty: "deletedBountyThumbnailURL",
28+
messageStub: "deleted bounty thumbnail",
29+
description: "Set an image to use as thumbnail on deleted bounties",
30+
modalLabel: "Deleted Bounty Thumbnail"
31+
}
32+
}));
33+
module.exports = new CommandWrapper(mainId, "Configure thumbnails shown for Toasts and Bounties (Premium)", PermissionFlagsBits.ManageGuild, true, [InteractionContextType.Guild], 3000,
34+
async (interaction, origin, runMode) => {
35+
const modalLabelComponents = [];
36+
for (const [ imageField, { description, modalLabel } ] of imageFieldToPayloadPropertyAndMessage) {
37+
modalLabelComponents.push(new LabelBuilder().setLabel(modalLabel).setDescription(description)
38+
.setFileUploadComponent(
39+
new FileUploadBuilder().setCustomId(imageField)
40+
.setRequired(false)
41+
))
42+
}
43+
const modal = new ModalBuilder().setCustomId(`${SKIP_INTERACTION_HANDLING}${interaction.id}`).setTitle("Configure User Thumnbails").addLabelComponents(modalLabelComponents);
44+
45+
interaction.showModal(modal);
46+
const modalInteraction = await interaction.awaitModalSubmit({ filter: incoming => incoming.customId === modal.data.custom_id, time: timeConversion(5, "m", "ms") });
47+
const updatePayload = {};
48+
49+
let replyContent = "The following thumbnails have been configured:";
50+
51+
for (const [ imageField, { payloadProperty, messageStub } ] of imageFieldToPayloadPropertyAndMessage) {
52+
const thumbnailCollection = modalInteraction.fields.getUploadedFiles(imageField);
53+
if (thumbnailCollection) {
54+
const firstAttachment = thumbnailCollection.first();
55+
if (firstAttachment) {
56+
updatePayload[payloadProperty] = firstAttachment.url;
57+
replyContent += `\n- The ${messageStub} was set to <${firstAttachment.url}>.`;
58+
}
59+
}
60+
}
61+
62+
origin.company.update(updatePayload);
63+
modalInteraction.reply({ content: replyContent, flags: MessageFlags.Ephemeral });
64+
}
65+
);

0 commit comments

Comments
 (0)