diff --git a/bot/build.gradle.kts b/bot/build.gradle.kts index bcc23dc3..8bb4b34b 100644 --- a/bot/build.gradle.kts +++ b/bot/build.gradle.kts @@ -21,7 +21,7 @@ dependencies { implementation(libs.guava) implementation(libs.micrometer.core) implementation(libs.commons.lang3) - implementation("org.apache.commons:commons-text:1.11.0") + implementation(libs.commons.text) implementation("io.micrometer:micrometer-registry-prometheus:1.12.1") implementation("com.h2database:h2:2.2.224") implementation("org.apache.derby:derby:10.17.1.0") diff --git a/bot/src/main/java/de/janno/discord/bot/command/AbstractCommand.java b/bot/src/main/java/de/janno/discord/bot/command/AbstractCommand.java index 5e2b7400..25ce5c2f 100644 --- a/bot/src/main/java/de/janno/discord/bot/command/AbstractCommand.java +++ b/bot/src/main/java/de/janno/discord/bot/command/AbstractCommand.java @@ -228,7 +228,7 @@ public Mono handleComponentInteractEvent(@NonNull ButtonEventAdaptor event state = configAndState.getState(); } final Long answerTargetChannelId = config.getAnswerTargetChannelId(); - Optional checkPermissions = event.checkPermissions(answerTargetChannelId); + Optional checkPermissions = event.checkPermissions(answerTargetChannelId, event.getRequester().getUserLocal()); if (checkPermissions.isPresent()) { return event.editMessage(checkPermissions.get(), null); } @@ -360,7 +360,7 @@ protected Mono deleteOldAndConcurrentMessageAndData( @Override public @NonNull Mono handleSlashCommandEvent(@NonNull SlashEventAdaptor event, @NonNull Supplier uuidSupplier, @NonNull Locale userLocale) { - Optional checkPermissions = event.checkPermissions(); + Optional checkPermissions = event.checkPermissions(userLocale); if (checkPermissions.isPresent()) { return event.reply(checkPermissions.get(), false); } diff --git a/bot/src/main/java/de/janno/discord/bot/command/RollAnswer.java b/bot/src/main/java/de/janno/discord/bot/command/RollAnswer.java index 1673e67d..dd62eb39 100644 --- a/bot/src/main/java/de/janno/discord/bot/command/RollAnswer.java +++ b/bot/src/main/java/de/janno/discord/bot/command/RollAnswer.java @@ -40,11 +40,7 @@ public String toShortString() { .toList().toString()) .orElse(null); return String.format("%s=%s", expression, - Joiner.on(",").skipNulls().join(result, rollDetails, errorMessage, warning, fieldStringList)) - .replace("▢", "0") - .replace("+", "+") - .replace("−", "-") //minus is not hyphen-minus - .replace("*", ""); + Joiner.on(",").skipNulls().join(result, rollDetails, errorMessage, warning, fieldStringList)); } diff --git a/bot/src/main/java/de/janno/discord/bot/command/directRoll/DirectRollCommand.java b/bot/src/main/java/de/janno/discord/bot/command/directRoll/DirectRollCommand.java index b8d429ad..efda31ce 100644 --- a/bot/src/main/java/de/janno/discord/bot/command/directRoll/DirectRollCommand.java +++ b/bot/src/main/java/de/janno/discord/bot/command/directRoll/DirectRollCommand.java @@ -94,7 +94,7 @@ DirectRollConfig deserializeConfig(ChannelConfigDTO channelConfigDTO) { public @NonNull Mono handleSlashCommandEvent(@NonNull SlashEventAdaptor event, @NonNull Supplier uuidSupplier, @NonNull Locale userLocale) { Stopwatch stopwatch = Stopwatch.createStarted(); - Optional checkPermissions = event.checkPermissions(); + Optional checkPermissions = event.checkPermissions(userLocale); if (checkPermissions.isPresent()) { return event.reply(checkPermissions.get(), false); } diff --git a/bot/src/main/java/de/janno/discord/bot/command/directRoll/ValidationCommand.java b/bot/src/main/java/de/janno/discord/bot/command/directRoll/ValidationCommand.java index 847df7b7..7af53b7f 100644 --- a/bot/src/main/java/de/janno/discord/bot/command/directRoll/ValidationCommand.java +++ b/bot/src/main/java/de/janno/discord/bot/command/directRoll/ValidationCommand.java @@ -49,8 +49,14 @@ public ValidationCommand(PersistenceManager persistenceManager, CachingDiceEvalu BotMetrics.incrementValidationCounter(validation.isEmpty()); return validation .map(s -> List.of(new AutoCompleteAnswer(s, option.getFocusedOptionValue()))) - //todo sometimes to long - .orElse(List.of(new AutoCompleteAnswer(option.getFocusedOptionValue(), option.getFocusedOptionValue()))); + .orElse(List.of(getValidAutoCompleteMessage(option.getFocusedOptionValue(), userLocale))); + } + + private AutoCompleteAnswer getValidAutoCompleteMessage(@NonNull String typedExpression, @NonNull Locale userLocale){ + if(typedExpression.length() <= 100){ + return new AutoCompleteAnswer(typedExpression, typedExpression); + } + return new AutoCompleteAnswer(I18n.getMessage("validation.autoComplete.tooLong", userLocale), I18n.getMessage("validation.autoComplete.tooLong", userLocale)); } @Override diff --git a/bot/src/main/java/de/janno/discord/bot/command/help/QuickstartCommand.java b/bot/src/main/java/de/janno/discord/bot/command/help/QuickstartCommand.java index b7b11520..eede49b2 100644 --- a/bot/src/main/java/de/janno/discord/bot/command/help/QuickstartCommand.java +++ b/bot/src/main/java/de/janno/discord/bot/command/help/QuickstartCommand.java @@ -117,7 +117,7 @@ private boolean matchRpgPreset(String typed, RpgSystemCommandPreset.PresetId pre public @NonNull Mono handleSlashCommandEvent(@NonNull SlashEventAdaptor event, @NonNull Supplier uuidSupplier, @NonNull Locale userLocal) { Stopwatch stopwatch = Stopwatch.createStarted(); - Optional checkPermissions = event.checkPermissions(); + Optional checkPermissions = event.checkPermissions(userLocal); if (checkPermissions.isPresent()) { return event.reply(checkPermissions.get(), false); } diff --git a/bot/src/main/java/de/janno/discord/bot/dice/DiceEvaluatorAdapter.java b/bot/src/main/java/de/janno/discord/bot/dice/DiceEvaluatorAdapter.java index 5ebb9faf..9ab04aac 100644 --- a/bot/src/main/java/de/janno/discord/bot/dice/DiceEvaluatorAdapter.java +++ b/bot/src/main/java/de/janno/discord/bot/dice/DiceEvaluatorAdapter.java @@ -16,6 +16,7 @@ import org.jetbrains.annotations.Nullable; import java.io.InputStream; +import java.math.BigDecimal; import java.util.List; import java.util.Locale; import java.util.Optional; @@ -53,14 +54,14 @@ private static Optional getLabelFromExpressionWithOptionalLabel(String e } private static String getResult(Roll roll, boolean sumUp) { - if (sumUp && allElementsAreIntegers(roll) && allElementsHaveNoColor(roll)) { - return String.valueOf(roll.getElements().stream().flatMap(r -> r.asInteger().stream()).mapToInt(i -> i).sum()); + if (sumUp && allElementsAreDecimal(roll) && allElementsHaveNoColor(roll)) { + return String.valueOf(roll.getElements().stream().flatMap(r -> r.asDecimal().stream()).reduce(BigDecimal::add).orElseThrow()); } return roll.getResultString(); } - private static boolean allElementsAreIntegers(Roll roll) { - return roll.getElements().stream().allMatch(r -> r.asInteger().isPresent()); + private static boolean allElementsAreDecimal(Roll roll) { + return roll.getElements().stream().allMatch(r -> r.asDecimal().isPresent()); } private static boolean allElementsHaveNoColor(Roll roll) { diff --git a/bot/src/main/resources/botMessages.properties b/bot/src/main/resources/botMessages.properties index 285b5166..50f7d737 100644 --- a/bot/src/main/resources/botMessages.properties +++ b/bot/src/main/resources/botMessages.properties @@ -192,6 +192,7 @@ quickstart.option.description=Start typing to filter and see more options #max 100 characters base.option.dice_image_style.autoComplete.missingStyle.name=Select the dice image style first validation.autoComplete.example=2d6= +validation.autoComplete.tooLong=Expression is valid but to long for auto complete base.option.dice.dice_image_style.autoComplete.missing.style.value=none base.option.dice_color.none.none=none base.option.dice_color.polyhedral_3d.red_and_white=red_and_white diff --git a/bot/src/main/resources/botMessages_de.properties b/bot/src/main/resources/botMessages_de.properties index 985a9e04..d00f22f9 100644 --- a/bot/src/main/resources/botMessages_de.properties +++ b/bot/src/main/resources/botMessages_de.properties @@ -191,6 +191,7 @@ quickstart.option.description=Tippe um zu filtern und mehr Optionen zu sehen #max 100 characters base.option.dice_image_style.autoComplete.missingStyle.name=Wähle zuerst einen Würfelbilderstil validation.autoComplete.example=2d6= +validation.autoComplete.tooLong=Würfelausdruck ist korrekt aber zu lang für AutoComplete base.option.dice.dice_image_style.autoComplete.missing.style.value=kein base.option.dice_color.none.none=kein base.option.dice_color.polyhedral_3d.red_and_white=rot_und_weiß diff --git a/bot/src/test/java/de/janno/discord/bot/ButtonEventAdaptorMock.java b/bot/src/test/java/de/janno/discord/bot/ButtonEventAdaptorMock.java index 95b18de1..d72cb07e 100644 --- a/bot/src/test/java/de/janno/discord/bot/ButtonEventAdaptorMock.java +++ b/bot/src/test/java/de/janno/discord/bot/ButtonEventAdaptorMock.java @@ -132,7 +132,7 @@ public Requester getRequester() { } @Override - public Optional checkPermissions(Long answerTargetChannelId) { + public Optional checkPermissions(Long answerTargetChannelId, @NonNull Locale userLocale) { return Optional.empty(); } diff --git a/bot/src/test/java/de/janno/discord/bot/SlashEventAdaptorMock.java b/bot/src/test/java/de/janno/discord/bot/SlashEventAdaptorMock.java index 30167a37..e6dd4300 100644 --- a/bot/src/test/java/de/janno/discord/bot/SlashEventAdaptorMock.java +++ b/bot/src/test/java/de/janno/discord/bot/SlashEventAdaptorMock.java @@ -52,7 +52,7 @@ public Long getGuildId() { } @Override - public Optional checkPermissions() { + public Optional checkPermissions(@NonNull Locale userLocal) { return Optional.empty(); } diff --git a/bot/src/test/java/de/janno/discord/bot/command/customDice/CustomDiceCommandTest.java b/bot/src/test/java/de/janno/discord/bot/command/customDice/CustomDiceCommandTest.java index 3e1966dd..ea076b83 100644 --- a/bot/src/test/java/de/janno/discord/bot/command/customDice/CustomDiceCommandTest.java +++ b/bot/src/test/java/de/janno/discord/bot/command/customDice/CustomDiceCommandTest.java @@ -237,7 +237,7 @@ void handleSlashCommandEvent() { StepVerifier.create(res).verifyComplete(); - verify(event).checkPermissions(); + verify(event).checkPermissions(Locale.ENGLISH); verify(event).getCommandString(); verify(event).getOption(any()); verify(event).reply(any(), anyBoolean()); @@ -306,7 +306,7 @@ void handleSlashCommandEvent_help() { assertThat(res).isNotNull(); - verify(event).checkPermissions(); + verify(event).checkPermissions(Locale.ENGLISH); verify(event).getCommandString(); verify(event, times(2)).getOption(any()); verify(event).replyWithEmbedOrMessageDefinition(EmbedOrMessageDefinition.builder() diff --git a/bot/src/test/java/de/janno/discord/bot/command/directRoll/DirectRollCommandTest.java b/bot/src/test/java/de/janno/discord/bot/command/directRoll/DirectRollCommandTest.java index 3fb06832..0213720f 100644 --- a/bot/src/test/java/de/janno/discord/bot/command/directRoll/DirectRollCommandTest.java +++ b/bot/src/test/java/de/janno/discord/bot/command/directRoll/DirectRollCommandTest.java @@ -62,7 +62,7 @@ void handleComponentInteractEvent() { StepVerifier.create(res) .verifyComplete(); - verify(slashEventAdaptor).checkPermissions(); + verify(slashEventAdaptor).checkPermissions(Locale.ENGLISH); verify(slashEventAdaptor, never()).reply(any(), anyBoolean()); verify(slashEventAdaptor).acknowledgeAndRemoveSlash(); verify(slashEventAdaptor).getOption("expression"); @@ -92,7 +92,7 @@ void handleComponentInteractEvent_validationFailed() { assertThat(res).isNotNull(); - verify(slashEventAdaptor).checkPermissions(); + verify(slashEventAdaptor).checkPermissions(Locale.ENGLISH); verify(slashEventAdaptor).getOption("expression"); verify(slashEventAdaptor, times(1)).getCommandString(); verify(slashEventAdaptor, never()).createMessageWithoutReference(any()); @@ -126,7 +126,7 @@ void handleComponentInteractEvent_help() { assertThat(res).isNotNull(); - verify(slashEventAdaptor).checkPermissions(); + verify(slashEventAdaptor).checkPermissions(Locale.ENGLISH); verify(slashEventAdaptor).getOption("expression"); verify(slashEventAdaptor, times(1)).getCommandString(); verify(slashEventAdaptor, never()).createMessageWithoutReference(any()); diff --git a/bot/src/test/java/de/janno/discord/bot/command/directRoll/HiddenRollCommandMockTest.java b/bot/src/test/java/de/janno/discord/bot/command/directRoll/HiddenRollCommandMockTest.java index f0ae26bb..daacd945 100644 --- a/bot/src/test/java/de/janno/discord/bot/command/directRoll/HiddenRollCommandMockTest.java +++ b/bot/src/test/java/de/janno/discord/bot/command/directRoll/HiddenRollCommandMockTest.java @@ -51,7 +51,7 @@ void roll_default() { List replyMessages = hiddenRollCommandEvent.getAllReplays(); assertThat(replyMessages).hasSize(1); - EmbedOrMessageDefinition replayMessage = replyMessages.get(0); + EmbedOrMessageDefinition replayMessage = replyMessages.getFirst(); ButtonEventAdaptorMock buttonEvent = new ButtonEventAdaptorMock("h", "reveal", replayMessage); underTest.handleComponentInteractEvent(buttonEvent).block(); @@ -81,7 +81,7 @@ void roll_NoWarn() { List replyMessages = hiddenRollCommandEvent.getAllReplays(); assertThat(replyMessages).hasSize(1); - EmbedOrMessageDefinition replayMessage = replyMessages.get(0); + EmbedOrMessageDefinition replayMessage = replyMessages.getFirst(); ButtonEventAdaptorMock buttonEvent = new ButtonEventAdaptorMock("h", "reveal", replayMessage); underTest.handleComponentInteractEvent(buttonEvent).block(); @@ -145,7 +145,7 @@ void roll_default_withLabel() { List replyMessages = hiddenRollCommandEvent.getAllReplays(); assertThat(replyMessages).hasSize(1); - EmbedOrMessageDefinition replayMessage = replyMessages.get(0); + EmbedOrMessageDefinition replayMessage = replyMessages.getFirst(); ButtonEventAdaptorMock buttonEvent = new ButtonEventAdaptorMock("h", "reveal", replayMessage); underTest.handleComponentInteractEvent(buttonEvent).block(); @@ -194,7 +194,7 @@ void roll_config_full_imageNone() { List replyMessages = hiddenRollCommandEvent.getAllReplays(); assertThat(replyMessages).hasSize(1); - EmbedOrMessageDefinition replayMessage = replyMessages.get(0); + EmbedOrMessageDefinition replayMessage = replyMessages.getFirst(); ButtonEventAdaptorMock buttonEvent = new ButtonEventAdaptorMock("h", "reveal", replayMessage); underTest.handleComponentInteractEvent(buttonEvent).block(); @@ -244,7 +244,7 @@ void roll_config_withoutExpression() { List replyMessages = hiddenRollCommandEvent.getAllReplays(); assertThat(replyMessages).hasSize(1); - EmbedOrMessageDefinition replayMessage = replyMessages.get(0); + EmbedOrMessageDefinition replayMessage = replyMessages.getFirst(); ButtonEventAdaptorMock buttonEvent = new ButtonEventAdaptorMock("h", "reveal", replayMessage); underTest.handleComponentInteractEvent(buttonEvent).block(); @@ -292,7 +292,7 @@ void roll_config_withoutExpression_withLabel() { List replyMessages = hiddenRollCommandEvent.getAllReplays(); assertThat(replyMessages).hasSize(1); - EmbedOrMessageDefinition replayMessage = replyMessages.get(0); + EmbedOrMessageDefinition replayMessage = replyMessages.getFirst(); ButtonEventAdaptorMock buttonEvent = new ButtonEventAdaptorMock("h", "reveal", replayMessage); underTest.handleComponentInteractEvent(buttonEvent).block(); @@ -341,7 +341,7 @@ void roll_config_compact() { List replyMessages = hiddenRollCommandEvent.getAllReplays(); assertThat(replyMessages).hasSize(1); - EmbedOrMessageDefinition replayMessage = replyMessages.get(0); + EmbedOrMessageDefinition replayMessage = replyMessages.getFirst(); ButtonEventAdaptorMock buttonEvent = new ButtonEventAdaptorMock("h", "reveal", replayMessage); underTest.handleComponentInteractEvent(buttonEvent).block(); @@ -390,7 +390,7 @@ void roll_config_minimal() { List replyMessages = hiddenRollCommandEvent.getAllReplays(); assertThat(replyMessages).hasSize(1); - EmbedOrMessageDefinition replayMessage = replyMessages.get(0); + EmbedOrMessageDefinition replayMessage = replyMessages.getFirst(); ButtonEventAdaptorMock buttonEvent = new ButtonEventAdaptorMock("h", "reveal", replayMessage); underTest.handleComponentInteractEvent(buttonEvent).block(); @@ -431,7 +431,7 @@ void channelAlias() { List replyMessages = hiddenRollCommandEvent.getAllReplays(); assertThat(replyMessages).hasSize(1); - EmbedOrMessageDefinition replayMessage = replyMessages.get(0); + EmbedOrMessageDefinition replayMessage = replyMessages.getFirst(); ButtonEventAdaptorMock buttonEvent = new ButtonEventAdaptorMock("h", "reveal", replayMessage); underTest.handleComponentInteractEvent(buttonEvent).block(); @@ -472,7 +472,7 @@ void userChannelAlias() { List replyMessages = hiddenRollCommandEvent.getAllReplays(); assertThat(replyMessages).hasSize(1); - EmbedOrMessageDefinition replayMessage = replyMessages.get(0); + EmbedOrMessageDefinition replayMessage = replyMessages.getFirst(); ButtonEventAdaptorMock buttonEvent = new ButtonEventAdaptorMock("h", "reveal", replayMessage); underTest.handleComponentInteractEvent(buttonEvent).block(); diff --git a/bot/src/test/java/de/janno/discord/bot/command/directRoll/ValidationCommandTest.java b/bot/src/test/java/de/janno/discord/bot/command/directRoll/ValidationCommandTest.java index 4075d406..4baad943 100644 --- a/bot/src/test/java/de/janno/discord/bot/command/directRoll/ValidationCommandTest.java +++ b/bot/src/test/java/de/janno/discord/bot/command/directRoll/ValidationCommandTest.java @@ -82,7 +82,7 @@ void handleComponentInteractEvent() { StepVerifier.create(res) .verifyComplete(); - verify(slashEventAdaptor).checkPermissions(); + verify(slashEventAdaptor).checkPermissions(Locale.ENGLISH); verify(slashEventAdaptor).reply("/validation expression:1d6", true); verify(slashEventAdaptor, never()).acknowledgeAndRemoveSlash(); verify(slashEventAdaptor).getOption("expression"); @@ -112,7 +112,7 @@ void handleComponentInteractEvent_validationFailed() { assertThat(res).isNotNull(); - verify(slashEventAdaptor).checkPermissions(); + verify(slashEventAdaptor).checkPermissions(Locale.ENGLISH); verify(slashEventAdaptor).getOption("expression"); verify(slashEventAdaptor, times(1)).getCommandString(); verify(slashEventAdaptor, never()).createMessageWithoutReference(any()); @@ -146,7 +146,7 @@ void handleComponentInteractEvent_help() { assertThat(res).isNotNull(); - verify(slashEventAdaptor).checkPermissions(); + verify(slashEventAdaptor).checkPermissions(Locale.ENGLISH); verify(slashEventAdaptor).getOption("expression"); verify(slashEventAdaptor, times(1)).getCommandString(); verify(slashEventAdaptor, never()).createMessageWithoutReference(any()); diff --git a/discord-connector/api/src/main/java/de/janno/discord/connector/api/ButtonEventAdaptor.java b/discord-connector/api/src/main/java/de/janno/discord/connector/api/ButtonEventAdaptor.java index fb5a0ac9..71ef9d90 100644 --- a/discord-connector/api/src/main/java/de/janno/discord/connector/api/ButtonEventAdaptor.java +++ b/discord-connector/api/src/main/java/de/janno/discord/connector/api/ButtonEventAdaptor.java @@ -10,6 +10,7 @@ import java.time.OffsetDateTime; import java.util.Collection; import java.util.List; +import java.util.Locale; import java.util.Optional; public interface ButtonEventAdaptor extends DiscordAdapter { @@ -30,7 +31,7 @@ public interface ButtonEventAdaptor extends DiscordAdapter { Requester getRequester(); - Optional checkPermissions(Long answerTargetChannelId); + Optional checkPermissions(Long answerTargetChannelId, @NonNull Locale userLocale); Mono createResultMessageWithReference(EmbedOrMessageDefinition answer, Long targetChannelId); diff --git a/discord-connector/api/src/main/java/de/janno/discord/connector/api/SlashEventAdaptor.java b/discord-connector/api/src/main/java/de/janno/discord/connector/api/SlashEventAdaptor.java index 7f535f4a..063d9278 100644 --- a/discord-connector/api/src/main/java/de/janno/discord/connector/api/SlashEventAdaptor.java +++ b/discord-connector/api/src/main/java/de/janno/discord/connector/api/SlashEventAdaptor.java @@ -6,10 +6,11 @@ import reactor.core.publisher.Mono; import java.util.List; +import java.util.Locale; import java.util.Optional; public interface SlashEventAdaptor extends DiscordAdapter { - Optional checkPermissions(); + Optional checkPermissions(@NonNull Locale userLocale); Optional getOption(@NonNull String optionName); diff --git a/discord-connector/jda/build.gradle.kts b/discord-connector/jda/build.gradle.kts index a19375d2..01f6f869 100644 --- a/discord-connector/jda/build.gradle.kts +++ b/discord-connector/jda/build.gradle.kts @@ -16,6 +16,7 @@ dependencies { implementation(libs.guava) implementation(libs.commons.lang3) implementation(libs.micrometer.core) + implementation(libs.commons.text) compileOnly(libs.lombok) annotationProcessor(libs.lombok) diff --git a/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/ButtonEventAdapterImpl.java b/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/ButtonEventAdapterImpl.java index 913485b1..773385e0 100644 --- a/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/ButtonEventAdapterImpl.java +++ b/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/ButtonEventAdapterImpl.java @@ -25,10 +25,7 @@ import java.io.InputStream; import java.time.OffsetDateTime; -import java.util.Collection; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.concurrent.ExecutionException; import java.util.function.Supplier; @@ -140,17 +137,17 @@ public Mono createResultMessageWithReference(EmbedOrMessageDefinition answ } @Override - public Optional checkPermissions(Long answerTargetChannelId) { - Optional primaryChannelPermissionCheck = checkPermission(event.getMessageChannel(), event.getGuild(), true); + public Optional checkPermissions(Long answerTargetChannelId, @NonNull Locale userLocale) { + Optional primaryChannelPermissionCheck = checkPermission(event.getMessageChannel(), event.getGuild(), true, userLocale); if (primaryChannelPermissionCheck.isPresent()) { return primaryChannelPermissionCheck; } if (answerTargetChannelId != null) { Optional answerChannel = Optional.ofNullable(event.getGuild()).map(g -> g.getChannelById(MessageChannel.class, answerTargetChannelId)); if (answerChannel.isEmpty()) { - return Optional.of("Configured answer target channel is not a valid message channel"); + return Optional.of(I18n.getMessage("permission.check.target.invalid", userLocale)); } - return checkPermission(answerChannel.get(), event.getGuild(), true); + return checkPermission(answerChannel.get(), event.getGuild(), true, userLocale); } return Optional.empty(); } @@ -246,7 +243,7 @@ public EmbedOrMessageDefinition getMessageDefinitionOfEventMessageWithoutButtons title = null; } else { type = EmbedOrMessageDefinition.Type.EMBED; - MessageEmbed embed = message.getEmbeds().get(0); + MessageEmbed embed = message.getEmbeds().getFirst(); descriptionOrContent = embed.getDescription(); title = embed.getTitle(); if (embed.getImage() == null) { diff --git a/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/DiscordAdapterImpl.java b/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/DiscordAdapterImpl.java index 3df53694..49d3e5c9 100644 --- a/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/DiscordAdapterImpl.java +++ b/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/DiscordAdapterImpl.java @@ -11,6 +11,7 @@ import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel; import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; @@ -31,6 +32,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Optional; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -184,18 +186,42 @@ case ErrorResponseException ignored when ((ErrorResponseException) throwable).ge * @param allowLegacyPermission if this is set to true only the old set of permissions is checked, so old button messages work as created. Should be removed in the future. * @return Optional message with the missing permissions */ - protected Optional checkPermission(@NonNull MessageChannel messageChannel, @Nullable Guild guild, boolean allowLegacyPermission) { + protected Optional checkPermission(@NonNull MessageChannel messageChannel, @Nullable Guild guild, boolean allowLegacyPermission, Locale userLocale) { List checks = new ArrayList<>(); - if (!messageChannel.canTalk()) { - checks.add("'SEND_MESSAGES'"); + boolean missingViewChannelPermission = Optional.of(messageChannel) + .filter(m -> m instanceof GuildMessageChannel) + .map(m -> (GuildMessageChannel) m) + .flatMap(g -> Optional.ofNullable(guild).map(Guild::getSelfMember).map(m -> !m.hasPermission(g, Permission.VIEW_CHANNEL))) + .orElse(true); + if (missingViewChannelPermission) { + checks.add(I18n.getMessage("permission.VIEW_CHANNEL", userLocale)); + } + boolean missingSendPermission = Optional.of(messageChannel) + .filter(m -> m instanceof GuildMessageChannel) + .map(m -> (GuildMessageChannel) m) + .flatMap(g -> Optional.ofNullable(guild).map(Guild::getSelfMember).map(m -> !m.hasPermission(g, Permission.MESSAGE_SEND))) + .orElse(true); + if (missingSendPermission) { + checks.add(I18n.getMessage("permission.MESSAGE_SEND", userLocale)); + } + + if (messageChannel instanceof ThreadChannel) { + boolean missingThreadSendPermission = Optional.of(messageChannel) + .map(m -> (ThreadChannel) m) + .flatMap(g -> Optional.ofNullable(guild).map(Guild::getSelfMember).map(m -> !m.hasPermission(g, Permission.MESSAGE_SEND_IN_THREADS))) + .orElse(true); + if (missingThreadSendPermission) { + checks.add(I18n.getMessage("permission.MESSAGE_SEND_IN_THREADS", userLocale)); + } } + boolean missingEmbedPermission = Optional.of(messageChannel) .filter(m -> m instanceof GuildMessageChannel) .map(m -> (GuildMessageChannel) m) .flatMap(g -> Optional.ofNullable(guild).map(Guild::getSelfMember).map(m -> !m.hasPermission(g, Permission.MESSAGE_EMBED_LINKS))) .orElse(true); if (missingEmbedPermission) { - checks.add("'EMBED_LINKS'"); + checks.add(I18n.getMessage("permission.EMBED_LINKS", userLocale)); } if (!allowLegacyPermission) { @@ -205,7 +231,7 @@ protected Optional checkPermission(@NonNull MessageChannel messageChanne .flatMap(g -> Optional.ofNullable(guild).map(Guild::getSelfMember).map(m -> !m.hasPermission(g, Permission.MESSAGE_HISTORY))) .orElse(true); if (missingMessageHistoryPermission) { - checks.add("'MESSAGE_HISTORY'"); + checks.add(I18n.getMessage("permission.MESSAGE_HISTORY", userLocale)); } boolean missingAddFilePermission = Optional.of(messageChannel) .filter(m -> m instanceof GuildMessageChannel) @@ -213,16 +239,17 @@ protected Optional checkPermission(@NonNull MessageChannel messageChanne .flatMap(g -> Optional.ofNullable(guild).map(Guild::getSelfMember).map(m -> !m.hasPermission(g, Permission.MESSAGE_ATTACH_FILES))) .orElse(true); if (missingAddFilePermission) { - checks.add("'ATTACH_FILES'"); + checks.add(I18n.getMessage("permission.ATTACH_FILES", userLocale)); } } if (checks.isEmpty()) { return Optional.empty(); } - String result = String.format("'%s'.'%s': The bot is missing the permission: %s. It will not work correctly without it. Please check the guild and channel permissions for the bot", + String result = String.format("'%s'.'%s': %s", Optional.ofNullable(guild).map(Guild::getName).orElse("-"), messageChannel.getName(), - String.join(" and ", checks)); + I18n.getMessage("permission.missing", userLocale, String.join(", ", checks)) + ); log.info(result); return Optional.of(result); } diff --git a/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/I18n.java b/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/I18n.java new file mode 100644 index 00000000..278780c9 --- /dev/null +++ b/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/I18n.java @@ -0,0 +1,25 @@ +package de.janno.discord.connector.jda; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.text.StringEscapeUtils; + +import java.text.MessageFormat; +import java.util.Locale; +import java.util.ResourceBundle; + +@Slf4j +public final class I18n { + private final static String MESSAGES_KEY = "discordConnector"; + + private I18n() { + } + + public static String getMessage(String key, Locale locale) { + return StringEscapeUtils.unescapeJava(ResourceBundle.getBundle(MESSAGES_KEY, locale).getString(key)); + } + + public static String getMessage(String key, Locale locale, Object... arguments) { + return MessageFormat.format(getMessage(key, locale), arguments); + } + +} diff --git a/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/JdaClient.java b/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/JdaClient.java index 88cdde49..d9656d16 100644 --- a/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/JdaClient.java +++ b/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/JdaClient.java @@ -218,7 +218,7 @@ public void onButtonInteraction(@NonNull ButtonInteractionEvent event) { SlashCommandRegistry.builder() .addSlashCommands(commands) - .registerSlashCommands(shardManager.getShards().get(0), disableCommandUpdate); + .registerSlashCommands(shardManager.getShards().getFirst(), disableCommandUpdate); } diff --git a/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/SlashEventAdapterImpl.java b/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/SlashEventAdapterImpl.java index 3b4593c3..d5da2e1f 100644 --- a/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/SlashEventAdapterImpl.java +++ b/discord-connector/jda/src/main/java/de/janno/discord/connector/jda/SlashEventAdapterImpl.java @@ -16,6 +16,7 @@ import reactor.core.publisher.Mono; import java.util.List; +import java.util.Locale; import java.util.Optional; import java.util.stream.Collectors; @@ -46,8 +47,8 @@ public Long getGuildId() { } @Override - public Optional checkPermissions() { - return checkPermission(event.getMessageChannel(), event.getGuild(), false); + public Optional checkPermissions(@NonNull Locale userLocale) { + return checkPermission(event.getMessageChannel(), event.getGuild(), false, userLocale); } @Override diff --git a/discord-connector/jda/src/main/resources/discordConnector.properties b/discord-connector/jda/src/main/resources/discordConnector.properties new file mode 100644 index 00000000..764af9a0 --- /dev/null +++ b/discord-connector/jda/src/main/resources/discordConnector.properties @@ -0,0 +1,8 @@ +permission.check.target.invalid=Configured answer target channel is not a valid message channel +permission.missing=The bot is missing the permissions: {0}. Please check the guild and channel permissions for the bot +permission.VIEW_CHANNEL=`View Channel` +permission.MESSAGE_SEND=`Send Messages` +permission.MESSAGE_SEND_IN_THREADS=`Send Messages in Threads` +permission.EMBED_LINKS=`Embed Links` +permission.ATTACH_FILES=`Attach Files` +permission.MESSAGE_HISTORY=`Read Message History` diff --git a/discord-connector/jda/src/main/resources/discordConnector_de.properties b/discord-connector/jda/src/main/resources/discordConnector_de.properties new file mode 100644 index 00000000..81460407 --- /dev/null +++ b/discord-connector/jda/src/main/resources/discordConnector_de.properties @@ -0,0 +1,8 @@ +permission.check.target.invalid=Der konfigurierte Antwortkanal ist kein valider Nachrichten Kanal +permission.missing=Dem Bot fehlen die Berechtigungen: {0}. Bitte überprüfe die Kanal und Server Berechtigungen für den Bot +permission.VIEW_CHANNEL=`Kanal anzeigen` +permission.MESSAGE_SEND=`Nachrichten senden` +permission.MESSAGE_SEND_IN_THREADS=`Nachrichten in Threads senden` +permission.EMBED_LINKS=`Links einbetten` +permission.ATTACH_FILES=`Datei anhängen` +permission.MESSAGE_HISTORY=`Nachrichtenverlauf anzeigen` diff --git a/discord-connector/jda/src/main/resources/discordConnector_pt_BR.properties b/discord-connector/jda/src/main/resources/discordConnector_pt_BR.properties new file mode 100644 index 00000000..41189c7a --- /dev/null +++ b/discord-connector/jda/src/main/resources/discordConnector_pt_BR.properties @@ -0,0 +1,8 @@ +#todo permission.check.target.invalid=Configured answer target channel is not a valid message channel +permission.missing=O bot está sem as permissões: {0}. Por favor, verifique as permissões de guilda e canal para o bot +permission.VIEW_CHANNEL=`Ver canal` +permission.MESSAGE_SEND=`Enviar mensagens` +permission.MESSAGE_SEND_IN_THREADS=`Enviar mensagens em tópicos` +permission.EMBED_LINKS=`Inserir links` +permission.ATTACH_FILES=`Anexar arquivos` +permission.MESSAGE_HISTORY=`Ver histórico de mensagens` \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 42491ac5..5514c81a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,6 +18,7 @@ dependencyResolutionManagement { library("logback-classic", "ch.qos.logback:logback-classic:1.4.14") library("log4j-to-slf4j", "org.apache.logging.log4j:log4j-to-slf4j:2.22.0") library("commons-lang3", "org.apache.commons:commons-lang3:3.14.0") + library("commons-text", "org.apache.commons:commons-text:1.11.0") } } }