-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
26 changed files
with
610 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
93 changes: 93 additions & 0 deletions
93
bot/src/main/java/de/janno/discord/bot/command/FetchCommand.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package de.janno.discord.bot.command; | ||
|
||
import de.janno.discord.bot.BotMetrics; | ||
import de.janno.discord.bot.I18n; | ||
import de.janno.discord.bot.command.customDice.CustomDiceCommand; | ||
import de.janno.discord.bot.command.customDice.CustomDiceConfig; | ||
import de.janno.discord.bot.command.customParameter.CustomParameterCommand; | ||
import de.janno.discord.bot.command.customParameter.CustomParameterConfig; | ||
import de.janno.discord.bot.command.sumCustomSet.SumCustomSetCommand; | ||
import de.janno.discord.bot.command.sumCustomSet.SumCustomSetConfig; | ||
import de.janno.discord.bot.persistance.MessageConfigDTO; | ||
import de.janno.discord.bot.persistance.PersistenceManager; | ||
import de.janno.discord.connector.api.SlashCommand; | ||
import de.janno.discord.connector.api.SlashEventAdaptor; | ||
import de.janno.discord.connector.api.message.EmbedOrMessageDefinition; | ||
import de.janno.discord.connector.api.slash.CommandDefinition; | ||
import lombok.NonNull; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import reactor.core.publisher.Flux; | ||
import reactor.core.publisher.Mono; | ||
|
||
import java.time.LocalDateTime; | ||
import java.time.temporal.ChronoUnit; | ||
import java.util.*; | ||
import java.util.function.Function; | ||
import java.util.function.Supplier; | ||
|
||
@Slf4j | ||
@RequiredArgsConstructor | ||
public class FetchCommand implements SlashCommand { | ||
|
||
private final PersistenceManager persistenceManager; | ||
private final CustomParameterCommand customParameterCommand; | ||
private final CustomDiceCommand customDiceCommand; | ||
private final SumCustomSetCommand sumCustomSetCommand; | ||
|
||
@Override | ||
public @NonNull String getCommandId() { | ||
return "fetch"; | ||
} | ||
|
||
@Override | ||
public @NonNull CommandDefinition getCommandDefinition() { | ||
return CommandDefinition.builder() | ||
.name(getCommandId()) | ||
.nameLocales(I18n.allNoneEnglishMessagesNames("fetch.name")) | ||
.description(I18n.getMessage("fetch.description", Locale.ENGLISH)) | ||
.descriptionLocales(I18n.allNoneEnglishMessagesDescriptions("fetch.description")) | ||
.build(); | ||
} | ||
|
||
@Override | ||
public @NonNull Mono<Void> handleSlashCommandEvent(@NonNull SlashEventAdaptor event, @NonNull Supplier<UUID> uuidSupplier, @NonNull Locale userLocal) { | ||
BotMetrics.incrementSlashStartMetricCounter(getCommandId(), "[]"); | ||
long fetchDelayMs = io.avaje.config.Config.getLong("command.fetch.delayMs", 60_000); | ||
Long oldestMessageIdWaitingToDeleted = MessageDeletionHelper.getMessageWaitingToBeDeleted(event.getChannelId()).stream() | ||
.min(Comparator.comparing(Function.identity())) | ||
.orElse(null); | ||
Optional<MessageConfigDTO> messageConfigDTOOptional = persistenceManager.getLastMessageDataInChannel(event.getChannelId(), | ||
LocalDateTime.now().minus(fetchDelayMs, ChronoUnit.MILLIS), | ||
oldestMessageIdWaitingToDeleted); | ||
if (messageConfigDTOOptional.isPresent()) { | ||
final MessageConfigDTO messageConfigDTO = messageConfigDTOOptional.get(); | ||
final UUID configUUID = messageConfigDTO.getConfigUUID(); | ||
if (customDiceCommand.getCommandId().equals(messageConfigDTO.getCommandId())) { | ||
CustomDiceConfig config = CustomDiceCommand.deserializeConfig(messageConfigDTO); | ||
return moveButtonMessage(config, customDiceCommand, configUUID, event); | ||
} else if (customParameterCommand.getCommandId().equals(messageConfigDTO.getCommandId())) { | ||
CustomParameterConfig config = CustomParameterCommand.deserializeConfig(messageConfigDTO); | ||
return moveButtonMessage(config, customParameterCommand, configUUID, event); | ||
} else if (sumCustomSetCommand.getCommandId().equals(messageConfigDTO.getCommandId())) { | ||
SumCustomSetConfig config = SumCustomSetCommand.deserializeConfig(messageConfigDTO); | ||
return moveButtonMessage(config, sumCustomSetCommand, configUUID, event); | ||
} | ||
} | ||
return event.reply(I18n.getMessage("fetch.no.message.found", userLocal), true); | ||
} | ||
|
||
private <C extends Config> Mono<Void> moveButtonMessage(C config, AbstractCommand<C, ?> command, UUID configUUID, SlashEventAdaptor event) { | ||
EmbedOrMessageDefinition buttonMessage = command.createNewButtonMessage(configUUID, config); | ||
List<Mono<Void>> actions = List.of(Mono.defer(event::acknowledgeAndRemoveSlash), | ||
Mono.defer(() -> event.createMessageWithoutReference(buttonMessage) | ||
.doOnNext(messageId -> command.createEmptyMessageData(configUUID, event.getGuildId(), event.getChannelId(), messageId))) | ||
.flatMap(newMessageId -> MessageDeletionHelper.deleteOldMessageAndData(persistenceManager, newMessageId, null, configUUID, event.getChannelId(), event)) | ||
.then()); | ||
return Flux.merge(1, actions.toArray(new Mono<?>[0])) | ||
.parallel() | ||
.then(); | ||
} | ||
|
||
|
||
} |
87 changes: 87 additions & 0 deletions
87
bot/src/main/java/de/janno/discord/bot/command/MessageDeletionHelper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package de.janno.discord.bot.command; | ||
|
||
import de.janno.discord.bot.persistance.PersistenceManager; | ||
import de.janno.discord.connector.api.DiscordAdapter; | ||
import lombok.NonNull; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.jetbrains.annotations.Nullable; | ||
import reactor.core.publisher.Mono; | ||
|
||
import java.time.Duration; | ||
import java.util.List; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
import java.util.UUID; | ||
import java.util.concurrent.ConcurrentSkipListSet; | ||
import java.util.stream.Collectors; | ||
|
||
@Slf4j | ||
public class MessageDeletionHelper { | ||
|
||
private final static ConcurrentSkipListSet<MessageIdAndChannelId> MESSAGE_STATE_IDS_TO_DELETE = new ConcurrentSkipListSet<>(); | ||
private final static Duration DELAY_MESSAGE_DATA_DELETION = Duration.ofMillis(io.avaje.config.Config.getLong("command.delayMessageDataDeletionMs", 10000)); | ||
|
||
public static Mono<Void> deleteOldMessageAndData( | ||
PersistenceManager persistenceManager, | ||
long newMessageId, | ||
@Nullable Long currentMessageId, | ||
@NonNull UUID configUUID, | ||
long channelId, | ||
@NonNull DiscordAdapter discordAdapter) { | ||
|
||
Set<Long> ids = persistenceManager.getAllMessageIdsForConfig(configUUID).stream() | ||
//this will already delete directly | ||
.filter(id -> !Objects.equals(id, currentMessageId)) | ||
//we don't want to delete the new message | ||
.filter(id -> id != newMessageId) | ||
//we don't want to check the state of messages where the data is already scheduled to be deleted | ||
.filter(id -> !MESSAGE_STATE_IDS_TO_DELETE.contains(new MessageIdAndChannelId(id, channelId))) | ||
.collect(Collectors.toSet()); | ||
|
||
if (ids.size() > 5) { //there should be not many old message data | ||
log.warn(String.format("ConfigUUID %s had %d to many messageData persisted", configUUID, ids.size())); | ||
} | ||
|
||
if (ids.isEmpty()) { | ||
return Mono.empty(); | ||
} | ||
|
||
return discordAdapter.getMessagesState(ids) | ||
.flatMap(ms -> { | ||
if (ms.isCanBeDeleted() && !ms.isPinned() && ms.isExists() && ms.getCreationTime() != null) { | ||
return discordAdapter.deleteMessageById(ms.getMessageId()) | ||
.then(deleteMessageDataWithDelay(persistenceManager, channelId, ms.getMessageId())); | ||
} else if (!ms.isExists()) { | ||
return deleteMessageDataWithDelay(persistenceManager, channelId, ms.getMessageId()); | ||
} else { | ||
return Mono.empty(); | ||
} | ||
}).then(); | ||
} | ||
|
||
public static Mono<Void> deleteMessageDataWithDelay(PersistenceManager persistenceManager, long channelId, long messageId) { | ||
MessageIdAndChannelId messageIdAndChannelId = new MessageIdAndChannelId(messageId, channelId); | ||
MESSAGE_STATE_IDS_TO_DELETE.add(messageIdAndChannelId); | ||
return Mono.defer(() -> Mono.just(messageIdAndChannelId) | ||
.delayElement(DELAY_MESSAGE_DATA_DELETION) | ||
//add throttle? | ||
.doOnNext(mc -> { | ||
MESSAGE_STATE_IDS_TO_DELETE.remove(mc); | ||
persistenceManager.deleteStateForMessage(mc.channelId(), mc.messageId()); | ||
}).ofType(Void.class)); | ||
} | ||
|
||
public static List<Long> getMessageWaitingToBeDeleted(long channelId) { | ||
return MESSAGE_STATE_IDS_TO_DELETE.stream() | ||
.filter(mc -> mc.channelId() == channelId) | ||
.map(MessageIdAndChannelId::messageId) | ||
.toList(); | ||
} | ||
|
||
private record MessageIdAndChannelId(long messageId, long channelId) implements Comparable<MessageIdAndChannelId> { | ||
@Override | ||
public int compareTo(@NonNull MessageIdAndChannelId o) { | ||
return this.toString().compareTo(o.toString()); | ||
} | ||
} | ||
} |
Oops, something went wrong.