Skip to content

Commit

Permalink
Update quickstart, clear by name and cleanup with less threads (#624)
Browse files Browse the repository at this point in the history
* Update quickstart, clear by name and cleanup with less threads
  • Loading branch information
twonirwana authored Nov 20, 2024
1 parent 0bdbb6f commit 3d3380b
Show file tree
Hide file tree
Showing 29 changed files with 1,175 additions and 191 deletions.
6 changes: 2 additions & 4 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ The quickstart will show a user also all named commands other user have created
|`Rêve de Dragon`
|`Salvage Union BDR-V1.0`
|`Savage Worlds`
|`Shadowdark`
|`Shadowrun`
|`Shadowrun without Dice Images`
|`Star Wars - West End Games D6 Rules, 2nd Edition REUP`
Expand Down Expand Up @@ -487,10 +488,6 @@ The alias must be created before the custom_parameter command is used and removi

==== Examples

===== Vampire 5ed

`/custom_parameter start expression: val('$r',{regular dice:1\<\=>16}d10 col 'blue') val('$h',{hunger dice:0\<\=>5}d10 col 'purple_dark') val('$s',('$r' + '$h')>=6c) val('$rt','$r'==10c) val('$ht','$h'==10c) val('$ho','$h'==1c) val('$2s',( ( ('$rt' + '$ht'=) ) /2)*2) val('$ts',('$s' + '$2s'=)) concat('successes: ', '$ts', ifE('$ts',0,ifG('$ho',1,' bestial failure' , ''),''), ifE('$rt' mod 2, 1, ifE('$ht' mod 2, 1, ' messy critical', ''), '')) answer_format: without_expression dice_image_style: polyhedral_knots dice_image_color: blue`

===== nWod / Chronicles of Darkness

`/custom_parameter start expression: {Number of Dice}d!10>=8c`
Expand Down Expand Up @@ -676,6 +673,7 @@ The state of the button message will be lost and reset as if new created.
=== Clear

The clear command removes all button configuration in a channel from the bot and deletes the button messages.
The optional `name` option can be used to only delete a named command in a channel.

=== Validation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import de.janno.discord.bot.BaseCommandUtils;
import de.janno.discord.bot.persistance.MessageConfigDTO;
import de.janno.discord.bot.persistance.MessageDataDTO;
import de.janno.discord.bot.persistance.PersistenceManager;
Expand Down Expand Up @@ -75,11 +74,6 @@ protected boolean shouldKeepExistingButtonMessage(@NonNull ButtonEventAdaptor ev
public @NonNull Optional<String> getCurrentMessageContentChange(C config, State<S> state, boolean keepExistingButtonMessage) {
return AbstractCommand.this.getCurrentMessageContentChange(config, state, keepExistingButtonMessage);
}

@Override
public @NonNull MessageDataDTO createEmptyMessageData(@NonNull UUID configUUID, @Nullable Long guildId, long channelId, long messageId) {
return AbstractCommand.this.createEmptyMessageData(configUUID, guildId, channelId, messageId);
}
};
slashCommand = new SlashCommandImpl<>(persistenceManager) {
@Override
Expand Down Expand Up @@ -151,11 +145,6 @@ protected Collection<CommandDefinitionOption> additionalCommandOptions() {
protected @NonNull Optional<String> getStartOptionsValidationMessage(@NonNull CommandInteractionOption options, long channelId, long userId, @NonNull Locale userLocale) {
return AbstractCommand.this.getStartOptionsValidationMessage(options, channelId, userId, userLocale);
}

@Override
public @NonNull MessageDataDTO createEmptyMessageData(@NonNull UUID configUUID, @Nullable Long guildId, long channelId, long messageId) {
return AbstractCommand.this.createEmptyMessageData(configUUID, guildId, channelId, messageId);
}
};
}

Expand Down Expand Up @@ -199,17 +188,6 @@ protected abstract ConfigAndState<C, S> getMessageDataAndUpdateWithButtonValue(@
@NonNull String invokingUserName);


/**
* On the creation of a message an empty state need to be saved so we know the message exists and we can remove it later, even on concurrent actions
*/
@VisibleForTesting
public @NonNull MessageDataDTO createEmptyMessageData(@NonNull UUID configUUID,
@Nullable Long guildId,
long channelId,
long messageId) {
return BaseCommandUtils.createCleanupAndSaveEmptyMessageData(configUUID, guildId, channelId, messageId, getCommandId(), persistenceManager);
}

//visible for welcome command
public abstract Optional<MessageConfigDTO> createMessageConfig(@NonNull UUID configUUID,
@Nullable Long guildId,
Expand Down
42 changes: 39 additions & 3 deletions bot/src/main/java/de/janno/discord/bot/command/ClearCommand.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
package de.janno.discord.bot.command;

import com.google.common.base.Strings;
import de.janno.discord.bot.BotMetrics;
import de.janno.discord.bot.I18n;
import de.janno.discord.bot.persistance.PersistenceManager;
import de.janno.discord.connector.api.AutoCompleteAnswer;
import de.janno.discord.connector.api.AutoCompleteRequest;
import de.janno.discord.connector.api.SlashCommand;
import de.janno.discord.connector.api.SlashEventAdaptor;
import de.janno.discord.connector.api.slash.CommandDefinition;
import de.janno.discord.connector.api.slash.CommandDefinitionOption;
import de.janno.discord.connector.api.slash.CommandInteractionOption;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.function.Supplier;

@Slf4j
public class ClearCommand implements SlashCommand {

private static final String NAME_OPTION = "name";
private final PersistenceManager persistenceManager;

public ClearCommand(PersistenceManager persistenceManager) {
Expand All @@ -37,23 +45,51 @@ public ClearCommand(PersistenceManager persistenceManager) {
.nameLocales(I18n.allNoneEnglishMessagesNames("clear.name"))
.description(I18n.getMessage("clear.description", Locale.ENGLISH))
.descriptionLocales(I18n.allNoneEnglishMessagesDescriptions("clear.description"))
.option(CommandDefinitionOption.builder()
.autoComplete(true)
.type(CommandDefinitionOption.Type.STRING)
.required(false)
.name(I18n.getMessage("clear.option.name.name", Locale.ENGLISH))
.description(I18n.getMessage("clear.option.name.description", Locale.ENGLISH))
.descriptionLocales(I18n.allNoneEnglishMessagesDescriptions("clear.option.name.description"))
.build())
.build();
}

@Override
public @NonNull List<AutoCompleteAnswer> getAutoCompleteAnswer(@NonNull AutoCompleteRequest autoCompleteRequest, @NonNull Locale userLocale, long channelId, Long guildId, long userId) {
if (!NAME_OPTION.equals(autoCompleteRequest.getFocusedOptionName())) {
return List.of();
}

return persistenceManager.getNamedCommandsChannel(channelId).stream()
.filter(nc -> Strings.isNullOrEmpty(autoCompleteRequest.getFocusedOptionValue()) || nc.toLowerCase().contains(autoCompleteRequest.getFocusedOptionValue().toLowerCase()))
.map(n -> new AutoCompleteAnswer(n, n))
.distinct()
.sorted(Comparator.comparing(AutoCompleteAnswer::getName))
.limit(5)
.toList();
}

@Override
public @NonNull Mono<Void> handleSlashCommandEvent(@NonNull SlashEventAdaptor event, @NonNull Supplier<UUID> uuidSupplier, @NonNull Locale userLocal) {
BotMetrics.incrementSlashStartMetricCounter(getCommandId());

final String name = event.getOption(NAME_OPTION).map(CommandInteractionOption::getStringValue).orElse(null);

return event.reply(I18n.getMessage("clear.reply", userLocal), false)
.then(Mono.just(persistenceManager.deleteMessageDataForChannel(event.getChannelId()))
.then(Mono.just(persistenceManager.deleteMessageDataForChannel(event.getChannelId(), name))
.flux()
.flatMap(Flux::fromIterable)
.delayElements(Duration.ofMillis(io.avaje.config.Config.getLong("command.clear.messageDeleteDelay", 1000)))
.flatMap(event::deleteMessageById)
.doOnTerminate(() -> log.info("Finish delete"))
.then())
.doOnSuccess(v -> {
persistenceManager.deleteAllChannelConfig(event.getChannelId());
persistenceManager.deleteAllMessageConfigForChannel(event.getChannelId());
if (Strings.isNullOrEmpty(name)) {
persistenceManager.deleteAllChannelConfig(event.getChannelId());
}
persistenceManager.deleteAllMessageConfigForChannel(event.getChannelId(), name);
});
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.janno.discord.bot.command;

import de.janno.discord.bot.BaseCommandUtils;
import de.janno.discord.bot.BotMetrics;
import de.janno.discord.bot.I18n;
import de.janno.discord.bot.command.customDice.CustomDiceCommand;
Expand Down Expand Up @@ -80,7 +81,7 @@ private <C extends RollConfig> Mono<Void> moveButtonMessage(C config, AbstractCo
List<Mono<Void>> actions = List.of(
Mono.defer(() -> event.reply(I18n.getMessage("fetch.reply", event.getRequester().getUserLocal()), true)),
Mono.defer(() -> event.sendMessage(buttonMessage)
.doOnNext(messageId -> command.createEmptyMessageData(configUUID, event.getGuildId(), event.getChannelId(), messageId)))
.doOnNext(messageId -> BaseCommandUtils.createCleanupAndSaveEmptyMessageData(configUUID, event.getGuildId(), event.getChannelId(), messageId, getCommandId(), persistenceManager)))
.flatMap(newMessageId -> MessageDeletionHelper.deleteOldMessageAndData(persistenceManager, newMessageId, null, configUUID, event.getChannelId(), event))
.then());
return Flux.merge(1, actions.toArray(new Mono<?>[0]))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.janno.discord.bot.command;

import com.google.common.base.Stopwatch;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import de.janno.discord.bot.BaseCommandUtils;
Expand All @@ -21,7 +22,9 @@
import reactor.core.publisher.Mono;

import javax.annotation.Nullable;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -116,19 +119,9 @@ protected Collection<CommandDefinitionOption> additionalCommandOptions() {
return BaseCommandOptions.autoCompleteColorOption(autoCompleteRequest, userLocale);
}

/**
* On the creation of a message an empty state need to be saved so we know the message exists and we can remove it later, even on concurrent actions
*/
protected @NonNull MessageDataDTO createEmptyMessageData(@NonNull UUID configUUID,
@Nullable Long guildId,
long channelId,
long messageId) {
return BaseCommandUtils.createCleanupAndSaveEmptyMessageData(configUUID, guildId, channelId, messageId, getCommandId(), persistenceManager);
}


@Override
public final @NonNull Mono<Void> handleSlashCommandEvent(@NonNull SlashEventAdaptor event, @NonNull Supplier<UUID> uuidSupplier, @NonNull Locale userLocale) {
Stopwatch stopwatch = Stopwatch.createStarted();
Optional<String> checkPermissions = event.checkPermissions(userLocale);
if (checkPermissions.isPresent()) {
return event.reply(checkPermissions.get(), false);
Expand Down Expand Up @@ -168,22 +161,25 @@ protected Collection<CommandDefinitionOption> additionalCommandOptions() {
if (guildId == null) {
BotMetrics.outsideGuildCounter("slash");
}
log.info("{}: '{}'",
event.getRequester().toLogString(),
commandString.replace("`", "").replace("\n", " "));

String replayMessage = Stream.of(commandString, getConfigWarnMessage(config, userLocale).orElse(null))
.filter(s -> !Strings.isNullOrEmpty(s))
.collect(Collectors.joining("\n"));

Duration untilAck = stopwatch.elapsed();
return event.reply(replayMessage, false)
.then(Mono.defer(() -> {
final Optional<MessageConfigDTO> newMessageConfig = createMessageConfig(configUUID, guildId, channelId, event.getUserId(), config);
newMessageConfig.ifPresent(persistenceManager::saveMessageConfig);
return event.sendMessage(createSlashResponseMessage(configUUID, config, channelId))
.doOnNext(messageId -> createEmptyMessageData(configUUID, guildId, channelId, messageId))
.doOnNext(messageId -> BaseCommandUtils.createCleanupAndSaveEmptyMessageData(configUUID, guildId, channelId, messageId, getCommandId(), persistenceManager))
.then();
}));

}))
.doAfterTerminate(() -> log.info("{}: {} in start={}ms reply={}ms",
event.getRequester().toLogString(),
commandString.replace("`", "").replace("\n", " "),
untilAck.toMillis(),
stopwatch.elapsed(TimeUnit.MILLISECONDS)
));
} else if (event.getOption(HELP_OPTION_NAME).isPresent()) {
BotMetrics.incrementSlashHelpMetricCounter(getCommandId());
return event.replyWithEmbedOrMessageDefinition(getHelpMessage(event.getRequester().getUserLocal()), true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ static List<Parameter> createParameterListFromBaseExpression(String expression)
.filter(s -> !Strings.isNullOrEmpty(s))
.map(s -> {
//if a label exists
if (s.contains(DiceEvaluatorAdapter.LABEL_DELIMITER)) {
if (s.contains(DiceEvaluatorAdapter.LABEL_DELIMITER) && s.split(DiceEvaluatorAdapter.LABEL_DELIMITER).length == 2) {
String[] split = s.split(DiceEvaluatorAdapter.LABEL_DELIMITER);
final String parameterOptionExpressionWithPath = split[0];
final String nextPathId = getPathId(parameterOptionExpressionWithPath);
Expand All @@ -245,7 +245,7 @@ static List<Parameter> createParameterListFromBaseExpression(String expression)
cleanLable = label;
directRoll = false;
}
if (split.length == 2 && !Strings.isNullOrEmpty(parameterOptionExpression) && !Strings.isNullOrEmpty(split[1])) {
if (!Strings.isNullOrEmpty(parameterOptionExpression) && !Strings.isNullOrEmpty(split[1])) {
return new Parameter.ParameterOption(parameterOptionExpression, cleanLable, createParameterOptionIdFromIndex(counter.getAndIncrement()), directRoll, nextPathId);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.google.common.base.Stopwatch;
import com.google.common.base.Strings;
import de.janno.discord.bot.BaseCommandUtils;
import de.janno.discord.bot.BotMetrics;
import de.janno.discord.bot.I18n;
import de.janno.discord.bot.command.Config;
Expand Down Expand Up @@ -58,7 +59,7 @@ public class QuickstartCommand implements SlashCommand {
return List.of();
}

final List<AutoCompleteAnswer> savedNamedAnswers = persistenceManager.getNamedCommandsForChannel(userId, guildId).stream()
final List<AutoCompleteAnswer> savedNamedAnswers = persistenceManager.getLastUsedNamedCommandsOfUserAndGuild(userId, guildId).stream()
.filter(nc -> Strings.isNullOrEmpty(autoCompleteRequest.getFocusedOptionValue()) || nc.name().toLowerCase().contains(autoCompleteRequest.getFocusedOptionValue().toLowerCase()))
.filter(nc -> SUPPORTED_COMMANDS.contains(nc.commandId()))
.map(n -> new AutoCompleteAnswer(n.name(), n.name()))
Expand Down Expand Up @@ -172,6 +173,7 @@ private Optional<EmbedOrMessageDefinition> getMessage(Config genericConfig, UUID
}
if (commandAndMessageDefinition.isPresent()) {
return Mono.defer(() -> event.sendMessage(commandAndMessageDefinition.get()))
.doOnNext(messageId -> BaseCommandUtils.createCleanupAndSaveEmptyMessageData(newConfigUUID, guildId, channelId, messageId, getCommandId(), persistenceManager))
.doOnSuccess(v -> BotMetrics.timerNewButtonMessageMetricCounter(getCommandId(), stopwatch.elapsed()))
.then(event.reply(commandString, false))
.doOnSuccess(v ->
Expand Down
Loading

0 comments on commit 3d3380b

Please sign in to comment.