From 701005e68f027217ed5c07b0255149aa6d0a246a Mon Sep 17 00:00:00 2001 From: Ashley Date: Sun, 12 Oct 2025 18:17:02 -0500 Subject: [PATCH 1/8] update to 6.0.0 --- .../foxgenesis/customjail/CommonMessages.java | 40 +++ .../CustomJailAutoConfiguration.java | 5 +- .../foxgenesis/customjail/JailFrontend.java | 300 +++++++----------- .../foxgenesis/customjail/WarningPage.java | 2 +- .../customjail/WarningPageContainer.java | 116 +++++++ .../customjail/event/CustomJailEvent.java | 2 +- .../customjail/event/JailEvent.java | 2 +- .../event/warning/WarningEvent.java | 2 +- .../warning/WarningLevelDecreasedEvent.java | 2 +- .../event/warning/WarningsClearedEvent.java | 2 +- .../customjail/jail/JailDetails.java | 91 ++++-- .../jail/exception/LocalizedException.java | 12 +- .../customjail/jail/impl/JailSystemImpl.java | 159 ++++++---- .../customjail/lang/messages.properties | 10 + 14 files changed, 454 insertions(+), 291 deletions(-) create mode 100644 src/main/java/net/foxgenesis/customjail/CommonMessages.java create mode 100644 src/main/java/net/foxgenesis/customjail/WarningPageContainer.java diff --git a/src/main/java/net/foxgenesis/customjail/CommonMessages.java b/src/main/java/net/foxgenesis/customjail/CommonMessages.java new file mode 100644 index 0000000..1a83dbf --- /dev/null +++ b/src/main/java/net/foxgenesis/customjail/CommonMessages.java @@ -0,0 +1,40 @@ +package net.foxgenesis.customjail; + +import java.util.Objects; + +import org.springframework.context.MessageSourceResolvable; + +public enum CommonMessages implements MessageSourceResolvable { + MEMBER("customjail.embed.member"), + MODERATOR("customjail.embed.moderator"), + ANONYMOUS("customjail.anonymous"), + ACCEPT("customjail.embed.accept"), + ACCEPTED("customjail.embed.accepted"), + YES("customjail.embed.yes"), + NO("customjail.embed.no"), + REASON("customjail.embed.reason"), + DEFAULT_REASON("customjail.embed.defaultReason"), + WARNING_LEVEL("customjail.embed.warning-level"), + TOTAL_WARNINGS("customjail.embed.total-warnings"), + WITH_WARNING("customjail.embed.with-warning"), + NA("customjail.embed.na"), + CASE_ID("customjail.embed.caseid"), + DURATION("customjail.embed.duration"), + TIME_LEFT("customjail.embed.time-left"), + JAIL_DETAILS("customjail.container.jaildetails"), + UNJAIL("customjail.embed.unjail"), + FORCESTART("customjail.embed.forcestart"), + MEMBER_JAILED("customjail.embed.jailed"); + + private final String[] codes; + + CommonMessages(String code) { + this.codes = new String[] { Objects.requireNonNull(code) }; + } + + @Override + public String[] getCodes() { + return codes; + } + +} diff --git a/src/main/java/net/foxgenesis/customjail/CustomJailAutoConfiguration.java b/src/main/java/net/foxgenesis/customjail/CustomJailAutoConfiguration.java index dd9546e..5b2e001 100644 --- a/src/main/java/net/foxgenesis/customjail/CustomJailAutoConfiguration.java +++ b/src/main/java/net/foxgenesis/customjail/CustomJailAutoConfiguration.java @@ -25,6 +25,7 @@ import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.interactions.DiscordLocale; +import net.dv8tion.jda.api.interactions.InteractionContextType; import net.dv8tion.jda.api.interactions.commands.Command; import net.dv8tion.jda.api.interactions.commands.Command.Choice; import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; @@ -187,7 +188,7 @@ private static CommandData user(String id, DefaultMemberPermissions permissions, LocalizationFunction localization) { return Commands.user(id) // Set guild only - .setGuildOnly(true) + .setContexts(InteractionContextType.GUILD) // Set default permissions .setDefaultPermissions(permissions) // Set localization @@ -198,7 +199,7 @@ private static SlashCommandData slash(String id, String description, DefaultMemb LocalizationFunction localization) { return Commands.slash(id, description) // Set guild only - .setGuildOnly(true) + .setContexts(InteractionContextType.GUILD) // Set default permissions .setDefaultPermissions(permissions) // Set localization diff --git a/src/main/java/net/foxgenesis/customjail/JailFrontend.java b/src/main/java/net/foxgenesis/customjail/JailFrontend.java index e35e8eb..4409f5c 100644 --- a/src/main/java/net/foxgenesis/customjail/JailFrontend.java +++ b/src/main/java/net/foxgenesis/customjail/JailFrontend.java @@ -3,51 +3,46 @@ import java.util.Arrays; import java.util.Locale; import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import java.util.function.Supplier; import org.springframework.beans.factory.annotation.Autowired; - +import org.springframework.context.MessageSourceResolvable; + +import net.dv8tion.jda.api.components.label.Label; +import net.dv8tion.jda.api.components.selections.SelectMenu; +import net.dv8tion.jda.api.components.selections.SelectOption; +import net.dv8tion.jda.api.components.selections.StringSelectMenu; +import net.dv8tion.jda.api.components.textdisplay.TextDisplay; +import net.dv8tion.jda.api.components.textinput.TextInput; +import net.dv8tion.jda.api.components.textinput.TextInputStyle; import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.User; -import net.dv8tion.jda.api.entities.emoji.Emoji; import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.GenericComponentInteractionCreateEvent; -import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback; import net.dv8tion.jda.api.interactions.commands.OptionMapping; -import net.dv8tion.jda.api.interactions.components.ActionRow; -import net.dv8tion.jda.api.interactions.components.buttons.Button; -import net.dv8tion.jda.api.interactions.components.selections.SelectMenu; -import net.dv8tion.jda.api.interactions.components.selections.SelectOption; -import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu; -import net.dv8tion.jda.api.interactions.components.text.TextInput; -import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; -import net.dv8tion.jda.api.interactions.modals.Modal; +import net.dv8tion.jda.api.modals.Modal; import net.dv8tion.jda.api.requests.RestAction; import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; +import net.dv8tion.jda.api.utils.MarkdownUtil; import net.foxgenesis.customjail.database.warning.Warning; import net.foxgenesis.customjail.jail.JailDetails; import net.foxgenesis.customjail.jail.JailSystem; import net.foxgenesis.customjail.jail.exception.LocalizedException; import net.foxgenesis.customjail.util.CustomTime; import net.foxgenesis.customjail.util.Utilities; -import net.foxgenesis.watame.util.discord.Colors; import net.foxgenesis.watame.util.discord.DiscordUtils; -import net.foxgenesis.watame.util.discord.InteractionListener; -import net.foxgenesis.watame.util.discord.Response; +import net.foxgenesis.watame.util.discord.components.Response; import net.foxgenesis.watame.util.lang.DiscordLocaleMessageSource; -import net.foxgenesis.watame.util.lang.LocalizedEmbedBuilder; +import net.foxgenesis.watame.util.lang.LocalizedContainerBuilder; +import net.foxgenesis.watame.util.lang.LocalizedModalBuilder; public class JailFrontend extends ListenerAdapter { @@ -69,7 +64,8 @@ public void onUserContextInteraction(UserContextInteractionEvent event) { if (jail.isJailed(event.getTargetMember())) error(event, "customjail.alreadyJailed").queue(); else - new JailUserListener(event); +// new JailUserListener(event); + displayJailModal(event); } case "Jail Details" -> { @@ -79,32 +75,22 @@ public void onUserContextInteraction(UserContextInteractionEvent event) { if (!jail.isJailed(member)) error(event, "customjail.notJailed").queue(); else if (isNonBotUser(event, member)) { - Optional jailEndTimestamp = jail.getJailEndTimestamp(member); - boolean isTimerRunning = jailEndTimestamp.isPresent(); - // Create embed JailDetails details = jail.getJailDetails(member); - LocalizedEmbedBuilder builder = new LocalizedEmbedBuilder(messages, locale); - details.applyToEmbedBuilder(builder, messages, locale, member, jail.getJailEndTimestamp(member)); - - MessageEmbed embed = builder.build(); - - // Create actions - ActionRow interactions = ActionRow.of( - Button.danger(Utilities.Interactions.wrapInteraction("forcestart", member), - messages.getMessage("customjail.embed.forcestart", locale)) - .withDisabled(isTimerRunning), - Button.danger(Utilities.Interactions.wrapInteraction("unjail", member), - messages.getMessage("customjail.embed.unjail", locale))); + LocalizedContainerBuilder cb = new LocalizedContainerBuilder(messages, locale); + details.applyToContainerBuilder(cb, member, jail.getJailEndTimestamp(member)); // Reply with embed and actions - event.replyEmbeds(embed).setComponents(interactions).setEphemeral(true).queue(); + event.replyComponents(cb.build()) + // This is required any time you are using Components V2 + .useComponentsV2().setEphemeral(true).queue(); } } case "View Warnings" -> { if (isValidUser(event, event.getTargetMember())) { - new WarningPage(event, jail, event.getTargetMember(), messages); + // new WarningPage(event, jail, event.getTargetMember(), messages); + new WarningPageContainer(event, jail, event.getTargetMember(), messages); } } } @@ -158,6 +144,28 @@ public void onModalInteraction(ModalInteractionEvent event) { if (Utilities.Interactions.unwrapInteraction(event, (id, unwrappedMember, variant) -> { unwrappedMember.ifPresentOrElse(member -> { switch (id) { + // Callback from jail user modal + case "jailuser" -> { + if (isValidUser(event, member)) + if (jail.isJailed(member)) { + error(event, "customjail.alreadyJailed").queue(); + return; + } + + CustomTime duration = event.getValue("time-selection").getAsStringList().stream() + .reduce((a, b) -> a + b).map(CustomTime::new).orElseThrow(); + boolean active = Boolean.valueOf(event.getValue("add-warning").getAsStringList().get(0)); + boolean anon = Boolean.valueOf(event.getValue("anon").getAsStringList().get(0)); + String reason = event.getValue("reason").getAsString(); + + attemptAction(event, (hook, locale) -> { + jail.jail(member, event.getMember(), duration, reason, active, anon); + + String response = messages.getMessage("customjail.embed.jailed-user", new Object[] { + member.getAsMention(), duration.getLocalizedDisplayString(messages, locale) }, locale); + return Response.success(response); + }).queue(); + } case "addreason" -> { // Ensure the callback is valid if (variant.isEmpty()) { @@ -404,183 +412,87 @@ private void displayReasonModal(GenericComponentInteractionCreateEvent event, Su wrappedMember != null ? wrappedMember.get() : event.getMember(), callback), messages.getMessage("customjail.modal.title", locale)); - TextInput body = TextInput - .create("reason", messages.getMessage("customjail.embed.reason", locale), TextInputStyle.PARAGRAPH) + TextInput body = TextInput.create("reason", TextInputStyle.PARAGRAPH) .setPlaceholder(messages.getMessage("customjail.modal.placeholder", locale)).setMinLength(3) .setMaxLength(500).setRequired(false).build(); - builder.addActionRow(body); + builder.addComponents(Label.of(messages.getMessage(CommonMessages.REASON, locale), body)); event.replyModal(builder.build()).queue(); } - private class JailUserListener extends InteractionListener { - private CompletableFuture expirationFuture; - - private final Member member; - private final int warningLevel; - private final int warnings; - - private CustomTime time; - - public JailUserListener(UserContextInteractionEvent event) { - super(event); - this.member = event.getTargetMember(); - this.warningLevel = jail.getWarningLevel(member); - this.warnings = jail.getTotalWarnings(member); - - ActionRow interactions = ActionRow.of(getAddWarningButton(true), addAnonButton(true), - Button.danger("jailuser", messages.getMessage("customjail.embed.jail-user", locale)).asDisabled()); - - InteractionHook hook = event.getHook(); - event.replyEmbeds(createJailEmbed()) - // Add time menu - .addActionRow(getTimeMenu()) - // Add buttons - .addComponents(interactions) - // Set user only - .setEphemeral(true) - // Send - .queue(v -> { - hook.getJDA().addEventListener(this); - expirationFuture = CompletableFuture.runAsync(() -> { - hook.getJDA().removeEventListener(this); - hook.editOriginalEmbeds( - Response.error(messages.getMessage("watame.interaction.expired", null, locale))) - .setReplace(true).queue(); - }, CompletableFuture.delayedExecutor(894, TimeUnit.SECONDS)); - }); - } + private void displayJailModal(UserContextInteractionEvent event) { + Locale locale = event.getUserLocale().toLocale(); + Member target = event.getTargetMember(); - @Override - public void onButtonInteraction(ButtonInteractionEvent event) { - if (!shouldRespond(event)) - return; - switch (event.getButton().getId()) { - case "with-warning" -> event.editButton(getAddWarningButton(false)).queue(); - case "without-warning" -> event.editButton(getAddWarningButton(true)).queue(); - case "anon" -> event.editButton(addAnonButton(false)).queue(); - case "non-anon" -> event.editButton(addAnonButton(true)).queue(); - case "jailuser" -> event.replyModal(createJailModal(member)).queue(); - } - } + LocalizedModalBuilder builder = new LocalizedModalBuilder(messages, locale, + Utilities.Interactions.wrapInteraction("jailuser", target, "jailuser"), "customjail.embed.jail-user"); - @Override - public void onStringSelectInteraction(StringSelectInteractionEvent event) { - if (!shouldRespond(event)) - return; - switch (event.getComponentId()) { - case "time-selection" -> { - time = event.getValues().stream().reduce((a, b) -> a + b).map(CustomTime::new).orElseThrow(); - - Message message = event.getMessage(); - Button withWarning = message.getButtonById("with-warning"), - withoutWarning = message.getButtonById("without-warning"), anon = message.getButtonById("anon"), - nonAnon = message.getButtonById("non-anon"), jailButton = message.getButtonById("jailuser"); - - event.editComponents( - ActionRow.of(event.getSelectMenu().createCopy().setDefaultValues(event.getValues()).build()), - ActionRow.of(withWarning != null ? withWarning : withoutWarning, anon != null ? anon : nonAnon, - jailButton.asEnabled())) - .queue(); - } - } - } + TextDisplay details = getJailModalDetails(target, locale); - @Override - public void onModalInteraction(ModalInteractionEvent event) { - Message.Interaction interactionContext = event.getMessage().getInteraction(); - if (interactionContext != null && interactionContext.getIdLong() == id) { - Message message = event.getMessage(); + TextInput body = TextInput.create("reason", TextInputStyle.PARAGRAPH) + .setPlaceholder(messages.getMessage("customjail.modal.placeholder", locale)).setMinLength(3) + .setMaxLength(500).setRequired(false).build(); - boolean withWarning = message.getButtonById("with-warning") != null; - boolean anon = message.getButtonById("anon") != null; - String reason = event.getValue("reason").getAsString(); + SelectOption[] yesNo = new SelectOption[] { + SelectOption.of(messages.getMessage(CommonMessages.YES, locale), Boolean.TRUE.toString()), + SelectOption.of(messages.getMessage(CommonMessages.NO, locale), Boolean.FALSE.toString()) }; + SelectMenu addWarning = StringSelectMenu.create("add-warning").addOptions(yesNo).setDefaultOptions(yesNo[0]) + .setRequired(true).build(); + SelectMenu anon = StringSelectMenu.create("anon").addOptions(yesNo).setDefaultOptions(yesNo[0]) + .setRequired(true).build(); - expirationFuture.cancel(true); - event.getJDA().removeEventListener(this); + SelectMenu timeMenu = getTimeMenu(locale); - event.deferEdit().flatMap(hook -> { - Locale locale = event.getUserLocale().toLocale(); + builder.addComponents(details); + builder.addLocalizedLabel(CommonMessages.DURATION, timeMenu); + builder.addLocalizedLabel(CommonMessages.WITH_WARNING, addWarning); + builder.addLocalizedLabel(CommonMessages.ANONYMOUS, anon); - MessageEmbed embed = null; - try { - jail.jail(member, event.getMember(), time, reason, withWarning, anon); + builder.addLocalizedLabel(CommonMessages.REASON, body); - embed = Response.success(messages.getMessage("customjail.embed.jailed-user", new Object[] { - member.getAsMention(), time.getLocalizedDisplayString(messages, locale) }, locale)); - } catch (LocalizedException e) { - embed = Response.error(messages.getMessage(e.getErrorMessage(), locale)); - } catch (Exception e) { - embed = Response.error(DiscordUtils.toString(e)); - } + event.replyModal(builder.build()).queue(); + } - return hook.editOriginalEmbeds(embed != null ? embed : Response.error("Uknown error while jailing")) - .setReplace(true); - }).queue(); - } - } + private TextDisplay getJailModalDetails(Member target, Locale locale) { + int warningLevel = jail.getWarningLevel(target); + int totalWarnings = jail.getTotalWarnings(target); - private MessageEmbed createJailEmbed() { - LocalizedEmbedBuilder builder = new LocalizedEmbedBuilder(messages, locale); - builder.setColor(Colors.INFO); - builder.setLocalizedTitle("customjail.embed.jail-user"); - builder.setThumbnail(member.getEffectiveAvatarUrl()); + StringBuilder builder = new StringBuilder(); - builder.addLocalizedField("customjail.embed.member", member.getAsMention(), true); - builder.addLocalizedField("customjail.embed.warning-level", "" + warningLevel, true); - builder.addLocalizedField("customjail.embed.total-warnings", "" + warnings, true); - return builder.build(); - } + appendBoldField(builder, locale, CommonMessages.MEMBER, target.getAsMention()); + appendBoldField(builder, locale, CommonMessages.WARNING_LEVEL, MarkdownUtil.monospace(warningLevel + "")); + appendBoldField(builder, locale, CommonMessages.TOTAL_WARNINGS, MarkdownUtil.monospace(totalWarnings + "")); - private Button addAnonButton(boolean anon) { - return anon - ? Button.primary("anon", messages.getMessage("customjail.embed.anon", locale)) - .withEmoji(Emoji.fromFormatted("U+1F92B")) - : Button.secondary("non-anon", messages.getMessage("customjail.embed.non-anon", locale)) - .withEmoji(Emoji.fromFormatted("U+1F4E3")); - } - - private Button getAddWarningButton(boolean addWarning) { - return addWarning - ? Button.primary("with-warning", messages.getMessage("customjail.embed.with-warning", locale)) - .withEmoji(Emoji.fromFormatted("U+2705")) - : Button.secondary("without-warning", - messages.getMessage("customjail.embed.without-warning", locale)) - .withEmoji(Emoji.fromFormatted("U+274C")); - } + return TextDisplay.of(builder.toString()); + } - private SelectMenu getTimeMenu() { - SelectOption[] options = Arrays - // Stream times - .stream(jail.getJailTimings()) - // Create select option - .map(time -> SelectOption.of(new CustomTime(time).getLocalizedDisplayString(messages, locale), - time)) - // To array - .toArray(SelectOption[]::new); - - return StringSelectMenu - // Set ID - .create("time-selection") - // Set placeholder - .setPlaceholder(messages.getMessage("customjail.embed.set-time", locale)) - // Add time options - .addOptions(options) - // Build - .build(); + private void appendBoldField(StringBuilder builder, Locale locale, Object... args) { + Object[] resolved = Arrays.copyOf(args, args.length); + for (int i = 0; i < resolved.length; i++) { + Object arg = resolved[i]; + if (arg instanceof MessageSourceResolvable resolvable) + resolved[i] = messages.getMessage(resolvable, locale); } + builder.append(String.format("**%s:** %s\n", args)); + } - private Modal createJailModal(Member member) { - Modal.Builder builder = Modal.create(Utilities.Interactions.wrapInteraction("jailuser", member), - messages.getMessage("customjail.modal.title", locale)); - - TextInput body = TextInput - .create("reason", messages.getMessage("customjail.embed.reason", locale), TextInputStyle.PARAGRAPH) - .setPlaceholder(messages.getMessage("customjail.modal.placeholder", locale)).setMinLength(3) - .setMaxLength(500).setRequired(false).build(); - - builder.addActionRow(body); - return builder.build(); - } + private SelectMenu getTimeMenu(Locale locale) { + SelectOption[] options = Arrays + // Stream times + .stream(jail.getJailTimings()) + // Create select option + .map(time -> SelectOption.of(new CustomTime(time).getLocalizedDisplayString(messages, locale), time)) + // To array + .toArray(SelectOption[]::new); + + return StringSelectMenu + // Set ID + .create("time-selection") + // Set placeholder + .setPlaceholder(messages.getMessage("customjail.embed.set-time", locale)) + // Add time options + .addOptions(options) + // Build + .build(); } } diff --git a/src/main/java/net/foxgenesis/customjail/WarningPage.java b/src/main/java/net/foxgenesis/customjail/WarningPage.java index e594083..9ff6c5d 100644 --- a/src/main/java/net/foxgenesis/customjail/WarningPage.java +++ b/src/main/java/net/foxgenesis/customjail/WarningPage.java @@ -21,7 +21,7 @@ import net.foxgenesis.customjail.jail.WarningSystem; import net.foxgenesis.watame.util.discord.Colors; import net.foxgenesis.watame.util.discord.DiscordUtils; -import net.foxgenesis.watame.util.discord.Response; +import net.foxgenesis.watame.util.discord.components.Response; import net.foxgenesis.watame.util.lang.LocalizedPageMenu; public class WarningPage extends LocalizedPageMenu { diff --git a/src/main/java/net/foxgenesis/customjail/WarningPageContainer.java b/src/main/java/net/foxgenesis/customjail/WarningPageContainer.java new file mode 100644 index 0000000..3ac9714 --- /dev/null +++ b/src/main/java/net/foxgenesis/customjail/WarningPageContainer.java @@ -0,0 +1,116 @@ +package net.foxgenesis.customjail; + +import java.util.Iterator; +import java.util.Objects; + +import org.springframework.context.MessageSource; +import org.springframework.context.MessageSourceResolvable; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; + +import net.dv8tion.jda.api.components.container.Container; +import net.dv8tion.jda.api.components.separator.Separator.Spacing; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.events.interaction.command.GenericCommandInteractionEvent; +import net.dv8tion.jda.api.utils.TimeFormat; +import net.foxgenesis.customjail.database.warning.Warning; +import net.foxgenesis.customjail.jail.WarningSystem; +import net.foxgenesis.watame.util.StringUtils; +import net.foxgenesis.watame.util.discord.Colors; +import net.foxgenesis.watame.util.discord.DiscordUtils; +import net.foxgenesis.watame.util.discord.components.ComponentUtils; +import net.foxgenesis.watame.util.lang.Localized; +import net.foxgenesis.watame.util.lang.LocalizedContainerBuilder; +import net.foxgenesis.watame.util.lang.LocalizedContainerPageMenu; +import net.foxgenesis.watame.util.lang.LocalizedSectionBuilder; + +public class WarningPageContainer extends LocalizedContainerPageMenu { + private static final MessageSourceResolvable TITLE = Localized.resolved("customjail.embed.warnings"); + + private final WarningSystem database; + private final Member target; + + public WarningPageContainer(GenericCommandInteractionEvent event, WarningSystem database, Member target, + MessageSource source) { + super(event, database.getWarningPage(target, PageRequest.of(0, 3, Sort.by("time").descending())), source); + this.database = Objects.requireNonNull(database); + this.target = Objects.requireNonNull(target); + this.sendInitalMessage(event); + } + + @Override + protected void populateContainer(LocalizedContainerBuilder builder, Page page) { + builder.setColor(Colors.INFO); + + LocalizedSectionBuilder sb = builder.getNewLocalizedSectionBuilder(); + + String headerFormat = """ + %s: `%s` + %s: `%s` + """; + sb.setThumbnailUrl(target.getEffectiveAvatarUrl()); + sb.addLocalizedFormattedTextDisplay("### %s - %s", TITLE, target.getAsMention()); + sb.addLocalizedFormattedTextDisplay(headerFormat, + // Warning Level + CommonMessages.WARNING_LEVEL, database.getWarningLevel(target), + // Total Warnings + CommonMessages.TOTAL_WARNINGS, database.getTotalWarnings(target)); + builder.addSectionAndClear(sb); + + builder.addDividingSeparator(Spacing.SMALL); + + Iterator iterator = page.iterator(); + while (iterator.hasNext()) { + Warning warning = iterator.next(); + buildWarningSection(builder, warning); + } + } + + private void buildWarningSection(LocalizedContainerBuilder builder, Warning warning) { + String format = """ + #%,d: %s - %s + >>> %s + """; + builder.addLocalizedFormattedTextDisplay(warning.isActive() ? "\u23F2 " + format : format, + // Case-ID + warning.getId(), + // Date + TimeFormat.RELATIVE.format(warning.getTime()), + // Moderator + Localized.resolved("customjail.embed.by", DiscordUtils.mentionUser(warning.getModerator())), + // Reason + StringUtils.nullIfBlank(warning.getReason()) == null ? CommonMessages.DEFAULT_REASON + : warning.getReason()); + } + +// private void buildWarningSection(LocalizedSectionBuilder builder, Warning warning) { +// builder.setButton(Button.of(ButtonStyle.DANGER, DELETE_PREFIX + warning.getId(), "\u2715")); +// String format = "%,d: %s - %s"; +// builder.addLocalizedFormattedTextDisplay(warning.isActive() ? "\u23F2 " + format : format, +// // Case-ID +// warning.getId(), +// // Date +// TimeFormat.RELATIVE.format(warning.getTime()), +// // Moderator +// Localized.resolved("customjail.embed.by", DiscordUtils.mentionUser(warning.getModerator()))); +// //builder.addLocalizedTextDisplay("customjail.embed.by", DiscordUtils.mentionUser(warning.getModerator())); +// +// if (StringUtils.nullIfBlank(warning.getReason()) == null) +// builder.addLocalizedFormattedTextDisplay(">>> %s", CommonMessages.DEFAULT_REASON); +// else +// builder.addTextDisplay(MarkdownUtil.quoteBlock(warning.getReason())); +// } + + @Override + protected Container createEmptyPage() { + return ComponentUtils.Response.error(messages.getMessage("customjail.warnings.empty", null, locale)); + } + + @Override + protected Page getNewPage(Pageable pagable) { + return database.getWarningPage(target, pagable); + } + +} diff --git a/src/main/java/net/foxgenesis/customjail/event/CustomJailEvent.java b/src/main/java/net/foxgenesis/customjail/event/CustomJailEvent.java index 7d853b8..634b71e 100644 --- a/src/main/java/net/foxgenesis/customjail/event/CustomJailEvent.java +++ b/src/main/java/net/foxgenesis/customjail/event/CustomJailEvent.java @@ -5,7 +5,7 @@ import org.springframework.context.MessageSource; -import net.foxgenesis.watame.util.discord.LoggableEvent; +import net.foxgenesis.watame.util.discord.components.LoggableEvent; public abstract class CustomJailEvent extends LoggableEvent { diff --git a/src/main/java/net/foxgenesis/customjail/event/JailEvent.java b/src/main/java/net/foxgenesis/customjail/event/JailEvent.java index 3a9249a..1290a7e 100644 --- a/src/main/java/net/foxgenesis/customjail/event/JailEvent.java +++ b/src/main/java/net/foxgenesis/customjail/event/JailEvent.java @@ -1,7 +1,7 @@ package net.foxgenesis.customjail.event; import net.dv8tion.jda.api.entities.Member; -import net.foxgenesis.watame.util.discord.ModeratorActionEvent; +import net.foxgenesis.watame.util.discord.components.ModeratorActionEvent; public abstract class JailEvent extends ModeratorActionEvent { diff --git a/src/main/java/net/foxgenesis/customjail/event/warning/WarningEvent.java b/src/main/java/net/foxgenesis/customjail/event/warning/WarningEvent.java index d6eb6d8..2ad2795 100644 --- a/src/main/java/net/foxgenesis/customjail/event/warning/WarningEvent.java +++ b/src/main/java/net/foxgenesis/customjail/event/warning/WarningEvent.java @@ -8,7 +8,7 @@ import net.dv8tion.jda.api.entities.Member; import net.foxgenesis.customjail.database.warning.Warning; -import net.foxgenesis.watame.util.discord.ModeratorActionEvent; +import net.foxgenesis.watame.util.discord.components.ModeratorActionEvent; public abstract class WarningEvent extends ModeratorActionEvent { diff --git a/src/main/java/net/foxgenesis/customjail/event/warning/WarningLevelDecreasedEvent.java b/src/main/java/net/foxgenesis/customjail/event/warning/WarningLevelDecreasedEvent.java index 2614ddb..e9e46e4 100644 --- a/src/main/java/net/foxgenesis/customjail/event/warning/WarningLevelDecreasedEvent.java +++ b/src/main/java/net/foxgenesis/customjail/event/warning/WarningLevelDecreasedEvent.java @@ -6,7 +6,7 @@ import net.dv8tion.jda.api.entities.Member; import net.foxgenesis.watame.util.discord.Colors; -import net.foxgenesis.watame.util.discord.ModeratorActionEvent; +import net.foxgenesis.watame.util.discord.components.ModeratorActionEvent; import net.foxgenesis.watame.util.lang.LocalizedEmbedBuilder; public class WarningLevelDecreasedEvent extends ModeratorActionEvent { diff --git a/src/main/java/net/foxgenesis/customjail/event/warning/WarningsClearedEvent.java b/src/main/java/net/foxgenesis/customjail/event/warning/WarningsClearedEvent.java index a640c3d..55aca62 100644 --- a/src/main/java/net/foxgenesis/customjail/event/warning/WarningsClearedEvent.java +++ b/src/main/java/net/foxgenesis/customjail/event/warning/WarningsClearedEvent.java @@ -6,7 +6,7 @@ import net.dv8tion.jda.api.entities.Member; import net.foxgenesis.watame.util.discord.Colors; -import net.foxgenesis.watame.util.discord.ModeratorActionEvent; +import net.foxgenesis.watame.util.discord.components.ModeratorActionEvent; import net.foxgenesis.watame.util.lang.LocalizedEmbedBuilder; public class WarningsClearedEvent extends ModeratorActionEvent { diff --git a/src/main/java/net/foxgenesis/customjail/jail/JailDetails.java b/src/main/java/net/foxgenesis/customjail/jail/JailDetails.java index 95ceb6e..bfa4c07 100644 --- a/src/main/java/net/foxgenesis/customjail/jail/JailDetails.java +++ b/src/main/java/net/foxgenesis/customjail/jail/JailDetails.java @@ -1,22 +1,29 @@ package net.foxgenesis.customjail.jail; -import java.time.Instant; -import java.util.Locale; import java.util.Objects; import java.util.Optional; import org.quartz.JobDataMap; -import org.springframework.context.MessageSource; +import org.springframework.context.MessageSourceResolvable; +import org.springframework.context.support.DefaultMessageSourceResolvable; +import net.dv8tion.jda.api.components.buttons.Button; +import net.dv8tion.jda.api.components.buttons.ButtonStyle; import net.dv8tion.jda.api.entities.Member; +import net.foxgenesis.customjail.CommonMessages; import net.foxgenesis.customjail.util.CustomTime; +import net.foxgenesis.customjail.util.Utilities; +import net.foxgenesis.watame.util.StringUtils; import net.foxgenesis.watame.util.discord.Colors; import net.foxgenesis.watame.util.discord.DiscordUtils; -import net.foxgenesis.watame.util.lang.LocalizedEmbedBuilder; +import net.foxgenesis.watame.util.lang.LocalizedContainerBuilder; +import net.foxgenesis.watame.util.lang.LocalizedSectionBuilder; public record JailDetails(long guild, long member, Long moderator, CustomTime duration, String reason, long caseid, long timestamp) { + private static final String BOLD_FIELD = "**%s:** %s"; + private static final String KEY_GUILD = "guild-id"; private static final String KEY_MEMBER = "member-id"; private static final String KEY_MODERATOR = "moderator-id"; @@ -76,34 +83,58 @@ public static JailDetails resolveFromDataMap(JobDataMap map) { return new JailDetails(guild, member, modId, duration, reason, caseid, timestamp); } - public void applyToEmbedBuilder(LocalizedEmbedBuilder builder, MessageSource source, Locale locale, Member member, + private static final MessageSourceResolvable JAILED_BY = new DefaultMessageSourceResolvable( + "customjail.container.jailed-by"); + private static final MessageSourceResolvable TIME_LEFT = new DefaultMessageSourceResolvable( + "customjail.embed.time-left"); + private static final MessageSourceResolvable NOT_ACCEPTED = new DefaultMessageSourceResolvable( + "customjail.embed.not-accepted"); + + public void applyToContainerBuilder(LocalizedContainerBuilder cb, Member member, Optional jailEndTimestamp) { + cb.setColor(Colors.INFO); + LocalizedSectionBuilder sb = cb.getNewLocalizedSectionBuilder(); boolean isTimerRunning = jailEndTimestamp.isPresent(); - builder.setColor(Colors.INFO); - builder.setThumbnail(member.getEffectiveAvatarUrl()); - builder.setLocalizedTitle("customjail.embed.jaildetails"); - builder.addLocalizedField("customjail.embed.member", DiscordUtils.mentionUser(this.member), true); - builder.addLocalizedField("customjail.embed.moderator", DiscordUtils.mentionUser(moderator), true); - - builder.addLocalizedField("customjail.embed.caseid", - caseid != -1 ? "" + caseid : source.getMessage("customjail.embed.na", null, locale), true); - - builder.addLocalizedFieldAndValue("customjail.embed.accepted", - isTimerRunning ? "customjail.embed.yes" : "customjail.embed.no", true, null); - - builder.addLocalizedField("customjail.embed.duration", duration.getLocalizedDisplayString(source, locale), - true); - - String endTime = isTimerRunning - ? jailEndTimestamp.orElseGet(() -> source.getMessage("customjail.embed.na", null, locale)) - : source.getMessage("customjail.embed.not-accepted", null, locale); - builder.addLocalizedField("customjail.embed.time-left", endTime, true); - - builder.addLocalizedField("customjail.embed.reason", Optional.ofNullable(reason) - .orElseGet(() -> source.getMessage("customjail.embed.defaultReason", null, locale)), false); - builder.setLocalizedFooter("customjail.footer"); - - builder.setTimestamp(Instant.ofEpochMilli(timestamp)); + Button unjail = sb.newLocalizedButton(ButtonStyle.DANGER, + Utilities.Interactions.wrapInteraction("unjail", member), CommonMessages.UNJAIL); + Button forcestart = sb.newLocalizedButton(ButtonStyle.DANGER, + Utilities.Interactions.wrapInteraction("forcestart", member), CommonMessages.FORCESTART) + .withDisabled(isTimerRunning); + + // Section 1 + sb.setThumbnailUrl(member.getEffectiveAvatarUrl()); + sb.addLocalizedTextDisplay(CommonMessages.JAIL_DETAILS); + sb.addLocalizedTextDisplay("customjail.container.jaildetails-for", DiscordUtils.mentionUser(this.member)); + cb.addSectionAndClear(sb); + + cb.addSmallDividingSeparator(); + + // Section 2 + sb.setButton(unjail); + sb.addLocalizedFormattedTextDisplay(BOLD_FIELD, JAILED_BY, DiscordUtils.mentionUser(moderator)); + sb.addLocalizedFormattedTextDisplay(BOLD_FIELD, CommonMessages.CASE_ID, + caseid != -1 ? caseid : CommonMessages.NA); + sb.addLocalizedFormattedTextDisplay(BOLD_FIELD, CommonMessages.DURATION, + duration.getLocalizedDisplayString(sb.getMessageSource(), sb.getLocale())); + cb.addSectionAndClear(sb); + + cb.addSmallDividingSeparator(); + + // Section 3 + sb.setButton(forcestart); + sb.addLocalizedFormattedTextDisplay(BOLD_FIELD, CommonMessages.ACCEPTED, + isTimerRunning ? CommonMessages.YES : CommonMessages.NO); + sb.addLocalizedFormattedTextDisplay(BOLD_FIELD, TIME_LEFT, + isTimerRunning ? jailEndTimestamp.get() : NOT_ACCEPTED); + cb.addSectionAndClear(sb); + + cb.addSmallDividingSeparator(); + + cb.addLocalizedFormattedTextDisplay("### %s", CommonMessages.REASON); + if (StringUtils.nullIfBlank(reason) == null) + cb.addLocalizedTextDisplay(CommonMessages.DEFAULT_REASON); + else + cb.addTextDisplay(reason); } } diff --git a/src/main/java/net/foxgenesis/customjail/jail/exception/LocalizedException.java b/src/main/java/net/foxgenesis/customjail/jail/exception/LocalizedException.java index d803535..4234679 100644 --- a/src/main/java/net/foxgenesis/customjail/jail/exception/LocalizedException.java +++ b/src/main/java/net/foxgenesis/customjail/jail/exception/LocalizedException.java @@ -6,8 +6,11 @@ import org.springframework.context.MessageSourceResolvable; import org.springframework.context.support.DefaultMessageSourceResolvable; +import net.dv8tion.jda.api.components.container.Container; import net.dv8tion.jda.api.entities.MessageEmbed; -import net.foxgenesis.watame.util.discord.Response; +import net.foxgenesis.watame.util.discord.Colors; +import net.foxgenesis.watame.util.discord.components.Response; +import net.foxgenesis.watame.util.lang.LocalizedContainerBuilder; public class LocalizedException extends RuntimeException { @@ -32,4 +35,11 @@ public MessageSourceResolvable getErrorMessage() { public MessageEmbed getErrorEmbed(MessageSource source, Locale locale) { return Response.error(source.getMessage(getErrorMessage(), locale)); } + + public Container getErrorContainer(MessageSource source, Locale locale) { + LocalizedContainerBuilder builder = new LocalizedContainerBuilder(source, locale); + builder.setColor(Colors.ERROR); + builder.addLocalizedTextDisplay(resolvable); + return builder.build(); + } } diff --git a/src/main/java/net/foxgenesis/customjail/jail/impl/JailSystemImpl.java b/src/main/java/net/foxgenesis/customjail/jail/impl/JailSystemImpl.java index 4774c21..5a9cba7 100644 --- a/src/main/java/net/foxgenesis/customjail/jail/impl/JailSystemImpl.java +++ b/src/main/java/net/foxgenesis/customjail/jail/impl/JailSystemImpl.java @@ -33,6 +33,12 @@ import org.springframework.lang.Nullable; import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.components.actionrow.ActionRow; +import net.dv8tion.jda.api.components.buttons.Button; +import net.dv8tion.jda.api.components.buttons.ButtonStyle; +import net.dv8tion.jda.api.components.container.Container; +import net.dv8tion.jda.api.components.replacer.ComponentReplacer; +import net.dv8tion.jda.api.components.textdisplay.TextDisplay; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.GuildVoiceState; import net.dv8tion.jda.api.entities.Member; @@ -49,13 +55,13 @@ import net.dv8tion.jda.api.exceptions.ErrorHandler; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback; -import net.dv8tion.jda.api.interactions.components.buttons.Button; -import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle; import net.dv8tion.jda.api.requests.ErrorResponse; import net.dv8tion.jda.api.requests.RestAction; import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; import net.dv8tion.jda.api.utils.MarkdownUtil; import net.dv8tion.jda.api.utils.TimeFormat; +import net.dv8tion.jda.api.utils.Timestamp; +import net.foxgenesis.customjail.CommonMessages; import net.foxgenesis.customjail.database.CustomJailConfiguration; import net.foxgenesis.customjail.database.CustomJailConfigurationService; import net.foxgenesis.customjail.database.warning.Warning; @@ -80,12 +86,16 @@ import net.foxgenesis.customjail.util.CustomTime; import net.foxgenesis.customjail.util.Utilities; import net.foxgenesis.springJDA.SpringJDA; +import net.foxgenesis.watame.util.StringUtils; import net.foxgenesis.watame.util.discord.Colors; import net.foxgenesis.watame.util.discord.DiscordLogger; import net.foxgenesis.watame.util.discord.DiscordUtils; -import net.foxgenesis.watame.util.discord.ModeratorActionEvent; -import net.foxgenesis.watame.util.discord.Response; +import net.foxgenesis.watame.util.discord.components.ComponentUtils; +import net.foxgenesis.watame.util.discord.components.ModeratorActionEvent; +import net.foxgenesis.watame.util.discord.components.Response; +import net.foxgenesis.watame.util.lang.LocalizedContainerBuilder; import net.foxgenesis.watame.util.lang.LocalizedEmbedBuilder; +import net.foxgenesis.watame.util.lang.LocalizedSectionBuilder; /** * Standard implementation of a {@link JailSystem}. @@ -187,17 +197,13 @@ public void jail(Member member, Member moderator, CustomTime time, String reason // Create the jail embed Locale locale = discordLogger.getEffectiveLocale(guild); - MessageEmbed embed = createJailEmbed(member, moderator, warning.map(Warning::getId), time, + Container container = createJailContainer(member, moderator, warning.map(Warning::getId), time, reason, anon, locale); - Button button = Button.primary(Utilities.Interactions.wrapInteraction("startjail", member), - messages.getMessage("customjail.embed.accept", null, locale)); // Send jail message - return jailChannel.sendMessage(member.getAsMention()) - // Set embeds - .setEmbeds(embed) - // Add accept button - .addActionRow(button) + return jailChannel.sendMessageComponents(container) + // Use V2 + .useComponentsV2() // Publish event on success .onSuccess(m -> { logger.info("{} jailed {} in {} for {} \"{}\"", moderator, member, guild, @@ -297,38 +303,48 @@ public Optional getJailEndTimestamp(Member member) { return scheduler.getJailEndTimestamp(member); } - private MessageEmbed createJailEmbed(Member member, Member moderator, Optional caseId, CustomTime time, + private Container createJailContainer(Member member, Member moderator, Optional caseId, CustomTime time, String reason, boolean anon, Locale locale) { - // Create the jail embed - LocalizedEmbedBuilder jailEmbedBuilder = new LocalizedEmbedBuilder(messages, locale); - jailEmbedBuilder.setColor(Colors.ERROR); - jailEmbedBuilder.setLocalizedTitle("customjail.embed.jailed"); - jailEmbedBuilder.setThumbnail(member.getEffectiveAvatarUrl()); - - // Row 1 - jailEmbedBuilder.addLocalizedField("customjail.embed.member", member.getAsMention(), true); - if(anon) - jailEmbedBuilder.addLocalizedFieldAndValue("customjail.embed.moderator", "customjail.anonymous", true, null); - else - jailEmbedBuilder.addLocalizedField("customjail.embed.moderator", moderator.getAsMention(), true); - jailEmbedBuilder.addLocalizedField("customjail.embed.caseid", - caseId.map(id -> "" + id).orElseGet(() -> messages.getMessage("customjail.embed.na", null, locale)), - true); - - // Row 2 - jailEmbedBuilder.addLocalizedField("customjail.embed.duration", - time.getLocalizedDisplayString(messages, locale), true); - - // Row 3 - jailEmbedBuilder.addLocalizedField("customjail.embed.reason", - Optional.ofNullable(reason).filter(r -> !r.isBlank()) - .orElseGet(() -> messages.getMessage("customjail.embed.defaultReason", null, locale)), - false); - - jailEmbedBuilder.setTimestamp(Instant.now()); - jailEmbedBuilder.setLocalizedFooter("customjail.footer"); - - return jailEmbedBuilder.build(); + LocalizedContainerBuilder cb = new LocalizedContainerBuilder(messages, locale); + LocalizedSectionBuilder sb = cb.getNewLocalizedSectionBuilder(); + cb.setUniqueId(100); + cb.setColor(Colors.ERROR); + + Button acceptButton = sb.newLocalizedButton(ButtonStyle.PRIMARY, + Utilities.Interactions.wrapInteraction("startjail", member), CommonMessages.ACCEPT); + + sb.setThumbnailUrl(member.getEffectiveAvatarUrl()); + sb.addLocalizedFormattedTextDisplay("## %s\n%s", CommonMessages.MEMBER_JAILED, + "Welcome to City 17 " + member.getAsMention() + ".\nPlease hit \"Accept\" to start the timer."); + cb.addSectionAndClear(sb); + + cb.addSmallDividingSeparator(); + + String detailsFormat = """ + **%s:** %s + **%s:** %s + **%s:** %s + **%s:** %s + """; + cb.addLocalizedFormattedTextDisplay(detailsFormat, + // Member + CommonMessages.MEMBER, member.getAsMention(), + // Moderator + CommonMessages.MODERATOR, anon ? CommonMessages.ANONYMOUS : moderator.getAsMention(), + // Case-ID + CommonMessages.CASE_ID, caseId.isPresent() ? caseId.get() : CommonMessages.NA, + // Duration + CommonMessages.DURATION, time.getLocalizedDisplayString(messages, locale)); + cb.addSmallDividingSeparator(); + + cb.addLocalizedFormattedTextDisplay("**%s**\n%s", CommonMessages.REASON, + StringUtils.nullIfBlank(reason) == null ? CommonMessages.DEFAULT_REASON : reason); + + cb.addSmallDividingSeparator(); + + cb.addActionRow(ActionRow.of(acceptButton)); + + return cb.build(); } // =========================================================================================================== @@ -543,33 +559,60 @@ public void onButtonInteraction(ButtonInteractionEvent event) { return; } + // Create replacer for jail container + Function containerReplacer = endTimestamp -> ComponentReplacer.of( + // Of container + Container.class, + // Container with ID 100 + container -> container.getUniqueId() == 100, + // Update container + container -> { + Locale locale = event.getUserLocale().toLocale(); + + // Add time left to new component + Container newContainer = ComponentUtils.addComponentsToContainer(container, + TextDisplay.ofFormat("-# %s: %s", + messages.getMessage(CommonMessages.TIME_LEFT, locale), endTimestamp)); + + // Replace accept button with accepted + return newContainer.replace(ComponentReplacer.of(Button.class, + button -> button.getCustomId().startsWith("startjail"), + button -> Button + .success("jailAccepted", + messages.getMessage("customjail.embed.accepted", null, locale)) + .asDisabled())); + }); + + // If timer is already running, update jail container if (scheduler.isJailTimerRunning(member)) { - error(event, "customjail.timerAlreadyStarted").queue(); + error(event, "customjail.timer-already-started").queue(); // Set accepted if not already if (event.getButton().getStyle() != ButtonStyle.SUCCESS) - event.editButton( - Button.success("jailAccepted", messages.getMessage("customjail.embed.accepted", - null, event.getUserLocale().toLocale())).asDisabled()) - .queue(); + event.getMessage() + .editMessageComponents(event.getMessage().getComponentTree() + .replace(containerReplacer.apply(getJailEndTimestamp(member).get()))) + .useComponentsV2().queue(); return; } // Display working event.deferReply(true).flatMap(hook -> { - + // Attempt to start timer Date date = startJailTimer(member, null, null); Locale locale = event.getUserLocale().toLocale(); - return hook - .editOriginalEmbeds( - Response.success(messages.getMessage("customjail.embed.time-remaining", - new Object[] { TimeFormat.RELATIVE.atInstant(date.toInstant()) }, - event.getUserLocale().toLocale()))) - .and(event.editButton(Button - .success("jailAccepted", - messages.getMessage("customjail.embed.accepted", null, locale)) - .asDisabled())); + Timestamp endTimestamp = TimeFormat.RELATIVE.atInstant(date.toInstant()); + + // Create response embed + MessageEmbed successEmbed = Response.success(messages + .getMessage("customjail.embed.time-remaining", new Object[] { endTimestamp }, locale)); + + // Update jail container + RestAction updateContainer = event.getMessage().editMessageComponents( + event.getMessage().getComponentTree().replace(containerReplacer.apply(endTimestamp))) + .useComponentsV2(); + return hook.editOriginalEmbeds(successEmbed).and(updateContainer); }).queue(); } } diff --git a/src/main/resources/plugins/customjail/lang/messages.properties b/src/main/resources/plugins/customjail/lang/messages.properties index 9cd4d29..d05c667 100644 --- a/src/main/resources/plugins/customjail/lang/messages.properties +++ b/src/main/resources/plugins/customjail/lang/messages.properties @@ -3,6 +3,11 @@ customjail.name = Custom Jail customjail.description = A custom timeout system built for the Max0r discord server customjail.footer = CustomJail customjail.anonymous = Anonymous +customjail.h1 = # {0} +customjail.h2 = ## {0} +customjail.h3 = ### {0} +customjail.small-text = -# {0} +customjail.bold-field = **{0}:** {1} # WEB customjail.enabled = Enabled @@ -20,6 +25,11 @@ customjail.notify.jailed = You have been jailed customjail.notify.unjailed = You have been un-jailed customjail.notify.timer-started = Jail Timer Started +# CONTAINER +customjail.container.jaildetails = ## Jail Details +customjail.container.jaildetails-for = -# For: {0} +customjail.container.jailed-by = Jailed By + # EMBED customjail.embed.warnings = Warnings customjail.embed.warning-level = Warning Level From 423827cadf7492b81184a8daf457203bf639b5fd Mon Sep 17 00:00:00 2001 From: Ashley Date: Wed, 15 Oct 2025 13:36:49 -0500 Subject: [PATCH 2/8] Update JailFrontend.java --- .../foxgenesis/customjail/JailFrontend.java | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/main/java/net/foxgenesis/customjail/JailFrontend.java b/src/main/java/net/foxgenesis/customjail/JailFrontend.java index 4409f5c..ea4dd40 100644 --- a/src/main/java/net/foxgenesis/customjail/JailFrontend.java +++ b/src/main/java/net/foxgenesis/customjail/JailFrontend.java @@ -72,20 +72,21 @@ public void onUserContextInteraction(UserContextInteractionEvent event) { Locale locale = event.getUserLocale().toLocale(); Member member = event.getTargetMember(); - if (!jail.isJailed(member)) - error(event, "customjail.notJailed").queue(); - else if (isNonBotUser(event, member)) { - // Create embed - JailDetails details = jail.getJailDetails(member); - - LocalizedContainerBuilder cb = new LocalizedContainerBuilder(messages, locale); - details.applyToContainerBuilder(cb, member, jail.getJailEndTimestamp(member)); - - // Reply with embed and actions - event.replyComponents(cb.build()) - // This is required any time you are using Components V2 - .useComponentsV2().setEphemeral(true).queue(); - } + if (isValidUser(event, member)) + if (!jail.isJailed(member)) + error(event, "customjail.notJailed").queue(); + else if (isNonBotUser(event, member)) { + // Create embed + JailDetails details = jail.getJailDetails(member); + + LocalizedContainerBuilder cb = new LocalizedContainerBuilder(messages, locale); + details.applyToContainerBuilder(cb, member, jail.getJailEndTimestamp(member)); + + // Reply with embed and actions + event.replyComponents(cb.build()) + // This is required any time you are using Components V2 + .useComponentsV2().setEphemeral(true).queue(); + } } case "View Warnings" -> { if (isValidUser(event, event.getTargetMember())) { From ac0bb4ff548dd9329c0c1247cc9b1bec6112aa23 Mon Sep 17 00:00:00 2001 From: Ashley Date: Sat, 18 Oct 2025 15:02:06 -0500 Subject: [PATCH 3/8] warning page container finished --- .../foxgenesis/customjail/CommonMessages.java | 1 + .../foxgenesis/customjail/JailFrontend.java | 17 ++- .../foxgenesis/customjail/WarningPage.java | 118 ------------------ .../customjail/WarningPageContainer.java | 20 ++- .../customjail/lang/messages.properties | 5 + 5 files changed, 36 insertions(+), 125 deletions(-) delete mode 100644 src/main/java/net/foxgenesis/customjail/WarningPage.java diff --git a/src/main/java/net/foxgenesis/customjail/CommonMessages.java b/src/main/java/net/foxgenesis/customjail/CommonMessages.java index 1a83dbf..6091e88 100644 --- a/src/main/java/net/foxgenesis/customjail/CommonMessages.java +++ b/src/main/java/net/foxgenesis/customjail/CommonMessages.java @@ -16,6 +16,7 @@ public enum CommonMessages implements MessageSourceResolvable { DEFAULT_REASON("customjail.embed.defaultReason"), WARNING_LEVEL("customjail.embed.warning-level"), TOTAL_WARNINGS("customjail.embed.total-warnings"), + WARNING_EXPIRES("customjail.embed.warning-expires"), WITH_WARNING("customjail.embed.with-warning"), NA("customjail.embed.na"), CASE_ID("customjail.embed.caseid"), diff --git a/src/main/java/net/foxgenesis/customjail/JailFrontend.java b/src/main/java/net/foxgenesis/customjail/JailFrontend.java index a7a02fd..867310c 100644 --- a/src/main/java/net/foxgenesis/customjail/JailFrontend.java +++ b/src/main/java/net/foxgenesis/customjail/JailFrontend.java @@ -42,6 +42,7 @@ import net.foxgenesis.watame.util.discord.DiscordUtils; import net.foxgenesis.watame.util.discord.components.Response; import net.foxgenesis.watame.util.lang.DiscordLocaleMessageSource; +import net.foxgenesis.watame.util.lang.Localized; import net.foxgenesis.watame.util.lang.LocalizedContainerBuilder; import net.foxgenesis.watame.util.lang.LocalizedModalBuilder; @@ -282,7 +283,7 @@ private void handleWarningCommands(SlashCommandInteractionEvent event) { case "list" -> { Member member = event.getOption("user", OptionMapping::getAsMember); if (isNonBotUser(event, member)) - new WarningPage(event, jail, member, messages); + new WarningPageContainer(event, jail, member, messages); } // Add warning case "add" -> { @@ -461,11 +462,15 @@ private void displayJailModal(UserContextInteractionEvent event) { SelectMenu timeMenu = getTimeMenu(locale); builder.addComponents(details); - builder.addLocalizedLabel(CommonMessages.DURATION, timeMenu); - builder.addLocalizedLabel(CommonMessages.WITH_WARNING, addWarning); - builder.addLocalizedLabel(CommonMessages.ANONYMOUS, anon); + builder.addLocalizedLabelWithDescription(CommonMessages.DURATION, + Localized.resolved("customjail.embed.duration-description"), timeMenu); + builder.addLocalizedLabelWithDescription(CommonMessages.WITH_WARNING, + Localized.resolved("customjail.embed.with-warning-description"), addWarning); + builder.addLocalizedLabelWithDescription(CommonMessages.ANONYMOUS, + Localized.resolved("customjail.embed.anonymous-description"), anon); - builder.addLocalizedLabel(CommonMessages.REASON, body); + builder.addLocalizedLabelWithDescription(CommonMessages.REASON, + Localized.resolved("customjail.embed.reason-description"), body); event.replyModal(builder.build()).queue(); } @@ -473,12 +478,14 @@ private void displayJailModal(UserContextInteractionEvent event) { private TextDisplay getJailModalDetails(Member target, Locale locale) { int warningLevel = jail.getWarningLevel(target); int totalWarnings = jail.getTotalWarnings(target); + Object expires = jail.getWarningEndTimestamp(target).map(Object.class::cast).orElse(CommonMessages.NA); StringBuilder builder = new StringBuilder(); appendBoldField(builder, locale, CommonMessages.MEMBER, target.getAsMention()); appendBoldField(builder, locale, CommonMessages.WARNING_LEVEL, MarkdownUtil.monospace(warningLevel + "")); appendBoldField(builder, locale, CommonMessages.TOTAL_WARNINGS, MarkdownUtil.monospace(totalWarnings + "")); + appendBoldField(builder, locale, CommonMessages.WARNING_EXPIRES, expires); return TextDisplay.of(builder.toString()); } diff --git a/src/main/java/net/foxgenesis/customjail/WarningPage.java b/src/main/java/net/foxgenesis/customjail/WarningPage.java deleted file mode 100644 index c149039..0000000 --- a/src/main/java/net/foxgenesis/customjail/WarningPage.java +++ /dev/null @@ -1,118 +0,0 @@ -package net.foxgenesis.customjail; - -import java.util.Iterator; -import java.util.Locale; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -import org.springframework.context.MessageSource; -import org.springframework.context.MessageSourceResolvable; -import org.springframework.context.support.DefaultMessageSourceResolvable; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; - -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.events.interaction.command.GenericCommandInteractionEvent; -import net.dv8tion.jda.api.utils.MarkdownUtil; -import net.dv8tion.jda.api.utils.TimeFormat; -import net.foxgenesis.customjail.database.warning.Warning; -import net.foxgenesis.customjail.jail.WarningSystem; -import net.foxgenesis.customjail.util.CachedObject; -import net.foxgenesis.watame.util.discord.Colors; -import net.foxgenesis.watame.util.discord.DiscordUtils; -import net.foxgenesis.watame.util.discord.components.Response; -import net.foxgenesis.watame.util.lang.LocalizedPageMenu; - -public class WarningPage extends LocalizedPageMenu { - private static final MessageSourceResolvable NA = new DefaultMessageSourceResolvable("customjail.embed.na"); - - private final WarningSystem database; - private final Member target; - - private final CachedObject warningLevelCache; - private final CachedObject expiresCache; - - public WarningPage(GenericCommandInteractionEvent event, WarningSystem database, Member target, - MessageSource source) { - super(event, database.getWarningPage(target, PageRequest.of(0, 3, Sort.by("time").descending())), source); - this.database = Objects.requireNonNull(database); - this.target = Objects.requireNonNull(target); - - // Cache Database calls - this.warningLevelCache = new CachedObject<>(() -> database.getWarningLevel(target), 15, TimeUnit.SECONDS); - this.expiresCache = new CachedObject<>( - () -> database.getWarningEndTimestamp(target).orElseGet(() -> messages.getMessage(NA, locale)), 15, - TimeUnit.SECONDS); - - this.sendInitalMessage(event); - } - - @Override - protected MessageEmbed createEmbed(Page page, Locale locale) { - LocalizedEmbedBuilder builder = new LocalizedEmbedBuilder(messages, locale); - builder.setColor(Colors.INFO); - builder.setThumbnail(target.getEffectiveAvatarUrl()); - - builder.appendLocalizedDescription("customjail.embed.warning.title", target.getAsMention()); - builder.newLine(); - - builder.appendLocalizedDescription("customjail.embed.warning.level", warningLevelCache.get()); - builder.newLine(); - - builder.appendLocalizedDescription("customjail.embed.warning.expires", expiresCache.get()); - builder.newLine(); - builder.newLine(); - - // Write notes - writeNotes(builder, page, locale); - - Object[] args = { page.getTotalElements(), page.getNumber() + 1, page.getTotalPages() }; - builder.setLocalizedFooter("customjail.warnings.footer", args); - - return builder.build(); - } - - private void writeNotes(LocalizedEmbedBuilder builder, Page page, Locale locale) { - StringBuilder sb = builder.getDescriptionBuilder(); - sb.append(">>> "); - - Iterator iterator = page.iterator(); - while (iterator.hasNext()) { - Warning warning = iterator.next(); - // FIXME: clean up and make readable - // "%1$s#%caseid%:% messages.getMessage("customjail.embed.defaultReason", null, locale))); - - if (iterator.hasNext()) - sb.append("\n\n"); - } - } - - @Override - protected MessageEmbed createEmptyPageEmbed(Locale locale) { - return Response - .error(messages.getMessage("customjail.warnings.empty", null, "customjail.warnings.empty", locale)); - } - - @Override - protected Page getNewPage(Pageable pagable) { - return database.getWarningPage(target, pagable); - } - - @Override - protected MessageEmbed createExpiredEmbed(Locale locale) { - return Response.error(messages.getMessage(INTERACTION_EXPIRED, locale)); - } -} diff --git a/src/main/java/net/foxgenesis/customjail/WarningPageContainer.java b/src/main/java/net/foxgenesis/customjail/WarningPageContainer.java index 3ac9714..92d3fc4 100644 --- a/src/main/java/net/foxgenesis/customjail/WarningPageContainer.java +++ b/src/main/java/net/foxgenesis/customjail/WarningPageContainer.java @@ -2,6 +2,7 @@ import java.util.Iterator; import java.util.Objects; +import java.util.concurrent.TimeUnit; import org.springframework.context.MessageSource; import org.springframework.context.MessageSourceResolvable; @@ -17,6 +18,7 @@ import net.dv8tion.jda.api.utils.TimeFormat; import net.foxgenesis.customjail.database.warning.Warning; import net.foxgenesis.customjail.jail.WarningSystem; +import net.foxgenesis.customjail.util.CachedObject; import net.foxgenesis.watame.util.StringUtils; import net.foxgenesis.watame.util.discord.Colors; import net.foxgenesis.watame.util.discord.DiscordUtils; @@ -32,11 +34,22 @@ public class WarningPageContainer extends LocalizedContainerPageMenu { private final WarningSystem database; private final Member target; + private final CachedObject warningLevelCache; + private final CachedObject totalWarningsCache; + private final CachedObject expiresCache; + public WarningPageContainer(GenericCommandInteractionEvent event, WarningSystem database, Member target, MessageSource source) { super(event, database.getWarningPage(target, PageRequest.of(0, 3, Sort.by("time").descending())), source); this.database = Objects.requireNonNull(database); this.target = Objects.requireNonNull(target); + + warningLevelCache = new CachedObject<>(() -> database.getWarningLevel(target), 15, TimeUnit.SECONDS); + totalWarningsCache = new CachedObject<>(() -> database.getTotalWarnings(target), 15, TimeUnit.SECONDS); + expiresCache = new CachedObject<>( + () -> database.getWarningEndTimestamp(target).map(Object.class::cast).orElse(CommonMessages.NA), 15, + TimeUnit.SECONDS); + this.sendInitalMessage(event); } @@ -49,14 +62,17 @@ protected void populateContainer(LocalizedContainerBuilder builder, Page