diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/command/economy/Slots.java b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/command/economy/Slots.java index 08b19d9a3..338612bed 100644 --- a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/command/economy/Slots.java +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/command/economy/Slots.java @@ -89,14 +89,14 @@ public class Slots extends AbstractConditionableCommand { .templated(true) .build(); - Parameter lowParameter = Parameter + Parameter bidParameter = Parameter .builder() .name(BID_PARAMETER) .type(Integer.class) .templated(true) .validators(Arrays.asList(MinIntegerValueValidator.min(0L))) .build(); - parameters.add(lowParameter); + parameters.add(bidParameter); SlashCommandConfig slashCommandConfig = SlashCommandConfig .builder() diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/command/games/Mines.java b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/command/games/Mines.java new file mode 100644 index 000000000..28a185f4f --- /dev/null +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/command/games/Mines.java @@ -0,0 +1,228 @@ +package dev.sheldan.abstracto.entertainment.command.games; + +import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand; +import dev.sheldan.abstracto.core.command.config.CommandConfiguration; +import dev.sheldan.abstracto.core.command.config.HelpInfo; +import dev.sheldan.abstracto.core.command.config.Parameter; +import dev.sheldan.abstracto.core.command.execution.CommandContext; +import dev.sheldan.abstracto.core.command.execution.CommandResult; +import dev.sheldan.abstracto.core.config.FeatureDefinition; +import dev.sheldan.abstracto.core.interaction.InteractionService; +import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig; +import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService; +import dev.sheldan.abstracto.core.models.ServerUser; +import dev.sheldan.abstracto.core.service.ChannelService; +import dev.sheldan.abstracto.core.service.FeatureFlagService; +import dev.sheldan.abstracto.core.templating.model.MessageToSend; +import dev.sheldan.abstracto.core.templating.service.TemplateService; +import dev.sheldan.abstracto.core.utils.FutureUtils; +import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureDefinition; +import dev.sheldan.abstracto.entertainment.config.EntertainmentModuleDefinition; +import dev.sheldan.abstracto.entertainment.config.EntertainmentSlashCommandNames; +import dev.sheldan.abstracto.entertainment.exception.NotEnoughCreditsException; +import dev.sheldan.abstracto.entertainment.model.command.games.MineBoard; +import dev.sheldan.abstracto.entertainment.model.database.EconomyUser; +import dev.sheldan.abstracto.entertainment.service.GameService; +import dev.sheldan.abstracto.entertainment.service.management.EconomyUserManagementService; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +@Component +public class Mines extends AbstractConditionableCommand { + + private static final String MINES_COMMAND_NAME = "mines"; + private static final String WIDTH_PARAMETER = "width"; + private static final String HEIGHT_PARAMETER = "height"; + private static final String MINES_PARAMETER = "mines"; + private static final String CREDITS_PARAMETER = "credits"; + public static final String MINE_BOARD_TEMPLATE_KEY = "mines_board_response"; + + @Autowired + private InteractionService interactionService; + + @Autowired + private SlashCommandParameterService slashCommandParameterService; + + @Autowired + private FeatureFlagService featureFlagService; + + @Autowired + private GameService gameService; + + @Autowired + private TemplateService templateService; + + @Autowired + private EconomyUserManagementService economyUserManagementService; + + @Autowired + private ChannelService channelService; + + @Override + public CompletableFuture executeSlash(SlashCommandInteractionEvent event) { + Integer width = 5; + if(slashCommandParameterService.hasCommandOption(WIDTH_PARAMETER, event)) { + width = slashCommandParameterService.getCommandOption(WIDTH_PARAMETER, event, Integer.class); + } + Integer height = 5; + if(slashCommandParameterService.hasCommandOption(HEIGHT_PARAMETER, event)) { + height = slashCommandParameterService.getCommandOption(HEIGHT_PARAMETER, event, Integer.class); + } + Integer mines = 5; + if(slashCommandParameterService.hasCommandOption(MINES_PARAMETER, event)) { + mines = slashCommandParameterService.getCommandOption(MINES_PARAMETER, event, Integer.class); + } + Integer credit = null; + boolean economyEnabled = featureFlagService.getFeatureFlagValue(EntertainmentFeatureDefinition.ECONOMY, event.getGuild().getIdLong()); + if(economyEnabled){ + credit = 50; + if(slashCommandParameterService.hasCommandOption(CREDITS_PARAMETER, event)) { + credit = slashCommandParameterService.getCommandOption(CREDITS_PARAMETER, event, Integer.class); + } + + Optional userOptional = economyUserManagementService.getUser(ServerUser.fromMember(event.getMember())); + if(!userOptional.isPresent()) { + throw new NotEnoughCreditsException(); + } + EconomyUser user = userOptional.get(); + if(user.getCredits() < credit) { + throw new NotEnoughCreditsException(); + } + } + MineBoard board = gameService.createBoard(width, height, mines); + board.setCreditsEnabled(economyEnabled); + board.setUserId(event.getMember().getIdLong()); + board.setServerId(event.getGuild().getIdLong()); + board.setCredits(credit); + MessageToSend messageToSend = templateService.renderEmbedTemplate(MINE_BOARD_TEMPLATE_KEY, board); + return interactionService.replyMessageToSend(messageToSend, event) + .thenCompose(interactionHook -> interactionHook.retrieveOriginal().submit()) + .thenApply(message -> { + gameService.persistMineBoardMessage(board, message); + return CommandResult.fromSuccess(); + }); + } + + + @Override + public CompletableFuture executeAsync(CommandContext commandContext) { + Integer width = 5; + List parameters = commandContext.getParameters().getParameters(); + if(!parameters.isEmpty()) { + width = (Integer) parameters.get(0); + } + Integer height = 5; + if(parameters.size() >= 2) { + height = (Integer) parameters.get(1); + } + Integer mines = 5; + if(parameters.size() >= 3) { + mines = (Integer) parameters.get(2); + } + Integer credit = null; + boolean economyEnabled = featureFlagService.getFeatureFlagValue(EntertainmentFeatureDefinition.ECONOMY, commandContext.getGuild().getIdLong()); + if(economyEnabled){ + credit = 50; + if(parameters.size() == 4) { + credit = (Integer) parameters.get(3); + } + + Optional userOptional = economyUserManagementService.getUser(ServerUser.fromMember(commandContext.getAuthor())); + if(!userOptional.isPresent()) { + throw new NotEnoughCreditsException(); + } + EconomyUser user = userOptional.get(); + if(user.getCredits() < credit) { + throw new NotEnoughCreditsException(); + } + } + MineBoard board = gameService.createBoard(width, height, mines); + board.setCreditsEnabled(economyEnabled); + board.setUserId(commandContext.getAuthor().getIdLong()); + board.setServerId(commandContext.getGuild().getIdLong()); + board.setCredits(credit); + MessageToSend messageToSend = templateService.renderEmbedTemplate(MINE_BOARD_TEMPLATE_KEY, board); + List> futures = channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()); + return FutureUtils.toSingleFutureGeneric(futures) + .thenAccept(unused -> gameService.persistMineBoardMessage(board, futures.get(0).join())) + .thenApply(unused -> CommandResult.fromSuccess()); + + } + + @Override + public CommandConfiguration getConfiguration() { + List parameters = new ArrayList<>(); + HelpInfo helpInfo = HelpInfo + .builder() + .templated(true) + .build(); + + Parameter widthParameter = Parameter + .builder() + .name(WIDTH_PARAMETER) + .type(Integer.class) + .optional(true) + .templated(true) + .build(); + parameters.add(widthParameter); + + Parameter heightParameter = Parameter + .builder() + .name(HEIGHT_PARAMETER) + .type(Integer.class) + .optional(true) + .templated(true) + .build(); + parameters.add(heightParameter); + + Parameter minesParameter = Parameter + .builder() + .name(MINES_PARAMETER) + .type(Integer.class) + .optional(true) + .templated(true) + .build(); + parameters.add(minesParameter); + + Parameter creditsParameter = Parameter + .builder() + .name(CREDITS_PARAMETER) + .type(Integer.class) + .optional(true) + .templated(true) + .dependentFeatures(Arrays.asList(EntertainmentFeatureDefinition.ECONOMY.getKey())) + .build(); + parameters.add(creditsParameter); + + SlashCommandConfig slashCommandConfig = SlashCommandConfig + .builder() + .enabled(true) + .rootCommandName(EntertainmentSlashCommandNames.GAME) + .commandName(MINES_COMMAND_NAME) + .build(); + + return CommandConfiguration.builder() + .name(MINES_COMMAND_NAME) + .slashCommandConfig(slashCommandConfig) + .async(true) + .module(EntertainmentModuleDefinition.ENTERTAINMENT) + .templated(true) + .supportsEmbedException(true) + .parameters(parameters) + .help(helpInfo) + .build(); + } + + @Override + public FeatureDefinition getFeature() { + return EntertainmentFeatureDefinition.GAMES; + } +} diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/listener/interaction/MinesButtonClickedListener.java b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/listener/interaction/MinesButtonClickedListener.java new file mode 100644 index 000000000..6807608a9 --- /dev/null +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/listener/interaction/MinesButtonClickedListener.java @@ -0,0 +1,76 @@ +package dev.sheldan.abstracto.entertainment.listener.interaction; + +import dev.sheldan.abstracto.core.config.FeatureDefinition; +import dev.sheldan.abstracto.core.config.ListenerPriority; +import dev.sheldan.abstracto.core.interaction.InteractionService; +import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListener; +import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListenerModel; +import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListenerResult; +import dev.sheldan.abstracto.core.service.FeatureFlagService; +import dev.sheldan.abstracto.core.templating.model.MessageToSend; +import dev.sheldan.abstracto.core.templating.service.TemplateService; +import dev.sheldan.abstracto.entertainment.command.games.Mines; +import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureDefinition; +import dev.sheldan.abstracto.entertainment.model.command.games.MineBoard; +import dev.sheldan.abstracto.entertainment.model.command.games.MineBoardPayload; +import dev.sheldan.abstracto.entertainment.service.GameService; +import dev.sheldan.abstracto.entertainment.service.GameServiceBean; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + + +@Component +@Slf4j +public class MinesButtonClickedListener implements ButtonClickedListener { + + @Autowired + private GameService gameService; + + @Autowired + private TemplateService templateService; + + @Autowired + private InteractionService interactionService; + + @Autowired + private FeatureFlagService featureFlagService; + + @Override + public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) { + MineBoardPayload payload = (MineBoardPayload) model.getDeserializedPayload(); + MineBoard mineBoard = payload.getMineBoard(); + if(!mineBoard.getState().equals(GameService.MineResult.CONTINUE)) { + return ButtonClickedListenerResult.IGNORED; + } + GameService.MineResult mineResult = gameService.uncoverField(mineBoard, payload.getX(), payload.getY()); + mineBoard.setState(mineResult); + if(mineBoard.getState() != GameService.MineResult.CONTINUE) { + if(featureFlagService.getFeatureFlagValue(EntertainmentFeatureDefinition.ECONOMY, model.getServerId())){ + gameService.evaluateCreditChanges(mineBoard); + } + gameService.uncoverBoard(mineBoard); + } + MessageToSend messageToSend = templateService.renderEmbedTemplate(Mines.MINE_BOARD_TEMPLATE_KEY, mineBoard); + interactionService.editOriginal(messageToSend, model.getEvent().getHook()).thenAccept(message -> { + gameService.updateMineBoard(mineBoard); + log.info("Updated original mineboard for board {}.", mineBoard.getBoardId()); + }); + return ButtonClickedListenerResult.ACKNOWLEDGED; + } + + @Override + public FeatureDefinition getFeature() { + return EntertainmentFeatureDefinition.GAMES; + } + + @Override + public Integer getPriority() { + return ListenerPriority.MEDIUM; + } + + @Override + public Boolean handlesEvent(ButtonClickedListenerModel model) { + return model.getOrigin().equals(GameServiceBean.MINES_BUTTON_ORIGIN); + } +} diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/repository/EconomyUserRepository.java b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/repository/EconomyUserRepository.java index 77694c9ab..328cbf62b 100644 --- a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/repository/EconomyUserRepository.java +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/repository/EconomyUserRepository.java @@ -16,6 +16,7 @@ import java.util.Optional; @Repository public interface EconomyUserRepository extends JpaRepository { Optional findByUser(AUserInAServer aUserInAServer); + Optional findByServer_IdAndUser_UserReference_Id(Long serverId, Long userId); @Query(value = "WITH economy_user_ranked AS" + "( " + diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/service/GameServiceBean.java b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/service/GameServiceBean.java new file mode 100644 index 000000000..97dfa2d3e --- /dev/null +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/service/GameServiceBean.java @@ -0,0 +1,306 @@ +package dev.sheldan.abstracto.entertainment.service; + +import dev.sheldan.abstracto.core.interaction.ComponentPayloadService; +import dev.sheldan.abstracto.core.interaction.ComponentService; +import dev.sheldan.abstracto.core.models.ServerUser; +import dev.sheldan.abstracto.core.models.database.AServer; +import dev.sheldan.abstracto.core.service.ConfigService; +import dev.sheldan.abstracto.core.service.management.ServerManagementService; +import dev.sheldan.abstracto.entertainment.exception.InvalidGameBoardException; +import dev.sheldan.abstracto.entertainment.model.command.games.MineBoard; +import dev.sheldan.abstracto.entertainment.model.command.games.MineBoardField; +import dev.sheldan.abstracto.entertainment.model.command.games.MineBoardPayload; +import dev.sheldan.abstracto.entertainment.model.command.games.MineBoardRow; +import dev.sheldan.abstracto.entertainment.model.database.EconomyUser; +import dev.sheldan.abstracto.entertainment.service.management.EconomyUserManagementService; +import net.dv8tion.jda.api.entities.Message; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.security.SecureRandom; +import java.util.*; +import java.util.stream.Collectors; + +import static dev.sheldan.abstracto.entertainment.config.GamesFeatureConfig.MINES_CREDITS_FACTOR; + +@Component +public class GameServiceBean implements GameService { + + public static final String MINES_BUTTON_ORIGIN = "MINES_BUTTON"; + @Autowired + private SecureRandom secureRandom; + + @Autowired + private ComponentService componentService; + + @Autowired + private ComponentPayloadService componentPayloadService; + + @Autowired + private ServerManagementService serverManagementService; + + @Autowired + private EconomyService economyService; + + @Autowired + private EconomyUserManagementService economyUserManagementService; + + @Autowired + private ConfigService configService; + + @Override + public MineBoard createBoard(Integer width, Integer height, Integer mines) { + if(mines >= width * height || width > 5 || height > 5 || mines <= 0 || height == 0 || width == 0) { + throw new InvalidGameBoardException(); + } + MineBoard mineBoard = generateEmptyBoard(width, height); + mineBoard.setMineCount(mines); + fillWithMines(mineBoard); + evaluateCounters(mineBoard); + + return mineBoard; + } + + @Override + @Transactional + public void persistMineBoardMessage(MineBoard mineBoard, Message message) { + mineBoard.setMessageId(message.getIdLong()); + mineBoard.setChannelId(message.getChannel().getIdLong()); + AServer server = serverManagementService.loadServer(message.getGuild()); + mineBoard.getFields().forEach(mineBoardField -> { + MineBoardPayload payload = MineBoardPayload + .builder() + .x(mineBoardField.getX()) + .y(mineBoardField.getY()) + .mineBoard(mineBoard) + .build(); + String componentId = mineBoard.getBoardId() + "_" + mineBoardField.getX() + "_" + mineBoardField.getY(); + componentPayloadService.createButtonPayload(componentId, payload, MINES_BUTTON_ORIGIN, server); + }); + } + + @Override + @Transactional + public void updateMineBoard(MineBoard mineBoard) { + mineBoard.getFields().forEach(mineBoardField -> { + MineBoardPayload newPayload = MineBoardPayload + .builder() + .x(mineBoardField.getX()) + .y(mineBoardField.getY()) + .mineBoard(mineBoard) + .build(); + String componentId = mineBoard.getBoardId() + "_" + mineBoardField.getX() + "_" + mineBoardField.getY(); + componentPayloadService.updateButtonPayload(componentId, newPayload); + }); + } + + @Override + public void uncoverBoard(MineBoard mineBoard) { + mineBoard.getFields().forEach(mineBoardField -> { + if(mineBoardField.getType().equals(MineBoardField.MineBoardFieldType.COVERED)) { + mineBoardField.setType(MineBoardField.MineBoardFieldType.UNCOVERED); + } + }); + } + + @Override + public void evaluateCreditChanges(MineBoard mineBoard) { + Long credits = mineBoard.getCredits().longValue(); + Integer mineCount = mineBoard.getMineCount(); + List allFields = mineBoard.getFields(); + Integer leftFields = (int) allFields + .stream() + .filter(mineBoardField -> mineBoardField.getType().equals(MineBoardField.MineBoardFieldType.COVERED) + || mineBoardField.getType().equals(MineBoardField.MineBoardFieldType.MINE)) + .count(); + Integer fieldCount = allFields.size(); + Integer uncoveredFields = fieldCount - leftFields; + Long creditChange = (long) (calculateCreditFactor(fieldCount, mineCount, uncoveredFields) * credits); + if(mineBoard.getState().equals(MineResult.WON)) { + Double factor = configService.getDoubleValueOrConfigDefault(MINES_CREDITS_FACTOR, mineBoard.getServerId()); + creditChange = (long) (creditChange * factor); + } + ServerUser serverUser = ServerUser + .builder() + .serverId(mineBoard.getServerId()) + .userId(mineBoard.getUserId()) + .build(); + Optional economyUserOptional = economyUserManagementService.getUser(serverUser); + if(economyUserOptional.isPresent()) { + economyService.addCredits(economyUserOptional.get(), creditChange); + } + mineBoard.setCreditChange(creditChange); + } + + private double calculateCreditFactor(int totalFields, int mines, int uncovered) { + return ((double) uncovered / (totalFields - mines)) + totalFields / 25.0 - ((totalFields - mines) / 25.0); + } + + @Override + public MineResult uncoverField(MineBoard board, Integer x, Integer y) { + return uncoverFieldOnBoard(board, x, y); + } + + public GameService.MineResult uncoverFieldOnBoard(MineBoard board, Integer x, Integer y) { + MineBoardField field = board.getField(x, y); + if(!MineBoardField.canInteract(field.getType())) { + return GameService.MineResult.CONTINUE; + } + if(field.getType().equals(MineBoardField.MineBoardFieldType.MINE)) { + field.setType(MineBoardField.MineBoardFieldType.EXPLODED); + return GameService.MineResult.LOST; + } + if(field.getType().equals(MineBoardField.MineBoardFieldType.COVERED)) { + if(field.getCounterValue() == 0) { + Set alreadyConsidered = new HashSet<>(); + Queue toUncover = new LinkedList<>(); + toUncover.add(field); + while(!toUncover.isEmpty()) { + MineBoardField fieldToHandle = toUncover.poll(); + fieldToHandle.setType(MineBoardField.MineBoardFieldType.UNCOVERED); + alreadyConsidered.add(fieldToHandle.getX() + "_" + fieldToHandle.getY()); + // only when we actually got a free field, we should add its neighbors to the next one to uncover + if(fieldToHandle.getCounterValue() == 0) { + List neighbors = getNeighbors(board, fieldToHandle.getX(), fieldToHandle.getY(), false); + List uncoverableNeighbors = neighbors + .stream().filter(mineBoardField -> mineBoardField.getType().equals(MineBoardField.MineBoardFieldType.COVERED)) + .collect(Collectors.toList()); + uncoverableNeighbors.forEach(mineBoardField -> { + if(!alreadyConsidered.contains(mineBoardField.getX() + "_" + mineBoardField.getY())) { + mineBoardField.setType(MineBoardField.MineBoardFieldType.UNCOVERED); + // only if t he newly found neighbor is a free field, we should discover its neighbors + if(mineBoardField.getCounterValue() == 0) { + toUncover.addAll(uncoverableNeighbors); + } + } + }); + } + } + } else { + field.setType(MineBoardField.MineBoardFieldType.UNCOVERED); + } + if(hasWon(board)) { + return GameService.MineResult.WON; + } + return GameService.MineResult.CONTINUE; + } + throw new IllegalStateException("Did not find correct type of field."); + } + + private List getNeighbors(MineBoard mineBoard, int xPosition, int yPosition) { + return getNeighbors(mineBoard, xPosition, yPosition, false); + } + + private List getNeighbors(MineBoard board, int xPosition, int yPosition, boolean directOnly) { + List neighbors = new ArrayList<>(); + boolean isFirstRow = yPosition == 0; + boolean isLastRow = yPosition == board.getRowCount() - 1; + boolean isFirstColumn = xPosition == 0; + boolean isLastColumn = xPosition == board.getColumnCount() - 1; + if(!isFirstColumn) { + if(!isFirstRow && !directOnly) { + neighbors.add(board.getField(xPosition - 1, yPosition - 1)); + } + neighbors.add(board.getField(xPosition - 1, yPosition)); + if(!isLastRow && !directOnly) { + neighbors.add(board.getField(xPosition - 1, yPosition + 1)); + } + } + if(!isFirstRow && !directOnly) { + neighbors.add(board.getField(xPosition, yPosition - 1)); + } + if(!isLastRow && !directOnly) { + neighbors.add(board.getField(xPosition, yPosition + 1)); + } + if(!isLastColumn) { + if(!isFirstRow && !directOnly) { + neighbors.add(board.getField(xPosition + 1, yPosition - 1)); + } + neighbors.add(board.getField(xPosition + 1, yPosition)); + if(!isLastRow && !directOnly) { + neighbors.add(board.getField(xPosition + 1, yPosition + 1)); + } + } + return neighbors; + } + + + public MineBoard generateEmptyBoard(Integer width, Integer height) { + List rows = new ArrayList<>(); + for (int y = 0; y < height; y++) { + List fields = new ArrayList<>(); + for (int x = 0; x < width; x++) { + MineBoardField field = MineBoardField + .builder() + .y(y) + .x(x) + .counterValue(0) + .type(MineBoardField.MineBoardFieldType.COVERED) + .build(); + fields.add(field); + } + MineBoardRow row = MineBoardRow + .builder() + .fields(fields) + .build(); + rows.add(row); + } + return MineBoard + .builder() + .rows(rows) + .state(MineResult.CONTINUE) + .columnCount(width) + .creditChange(0L) + .rowCount(height) + .boardId(UUID.randomUUID().toString()) + .build(); + } + + public void fillWithMines(MineBoard board) { + Set usedKeys = new HashSet<>(); + int maxIterations = 1_0000_000; + int iterations = 0; + List> foundPositions = new ArrayList<>(); + do { + int x = secureRandom.nextInt(board.getColumnCount()); + int y = secureRandom.nextInt(board.getRowCount()); + String positionKey = x + "_" + y; + if(!usedKeys.contains(positionKey)) { + foundPositions.add(Pair.of(x, y)); + usedKeys.add(positionKey); + iterations = 0; + } + iterations++; + } while(foundPositions.size() < board.getMineCount() && iterations < maxIterations); + foundPositions.forEach(xYPair -> board.getRows().get(xYPair.getRight()).getFields().get(xYPair.getLeft()).setType(MineBoardField.MineBoardFieldType.MINE)); + } + + public void evaluateCounters(MineBoard board) { + board.getRows().forEach(mineBoardRow -> mineBoardRow.getFields().forEach(mineBoardField -> { + if(!mineBoardField.getType().equals(MineBoardField.MineBoardFieldType.MINE)) { + long mineCounts = getMineCounts(board, mineBoardField.getX(), mineBoardField.getY()); + mineBoardField.setCounterValue((int) mineCounts); + } + })); + } + + private long getMineCounts(MineBoard board, int xPosition, int yPosition) { + List neighbors = getNeighbors(board, xPosition, yPosition); + return neighbors + .stream() + .filter(mineBoardField -> mineBoardField.getType().equals(MineBoardField.MineBoardFieldType.MINE)) + .count(); + } + + private boolean hasWon(MineBoard board) { + return board + .getFields() + .stream() + .noneMatch(mineBoardField -> + mineBoardField.getType().equals(MineBoardField.MineBoardFieldType.COVERED) + && !mineBoardField.getType().equals(MineBoardField.MineBoardFieldType.MINE)); + } + +} diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/service/management/EconomyUserManagementServiceBean.java b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/service/management/EconomyUserManagementServiceBean.java index ee6e9ecd5..95b8a903e 100644 --- a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/service/management/EconomyUserManagementServiceBean.java +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/java/dev/sheldan/abstracto/entertainment/service/management/EconomyUserManagementServiceBean.java @@ -1,5 +1,6 @@ package dev.sheldan.abstracto.entertainment.service.management; +import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.entertainment.model.database.EconomyLeaderboardResult; @@ -39,6 +40,11 @@ public class EconomyUserManagementServiceBean implements EconomyUserManagementSe return repository.findByUser(aUserInAServer); } + @Override + public Optional getUser(ServerUser serverUser) { + return repository.findByServer_IdAndUser_UserReference_Id(serverUser.getServerId(), serverUser.getUserId()); + } + @Override public EconomyLeaderboardResult getRankOfUserInServer(AUserInAServer aUserInAServer) { return repository.getRankOfUserInServer(aUserInAServer.getUserInServerId(), aUserInAServer.getServerReference().getId()); diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/entertainment-config.properties b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/entertainment-config.properties index 2db6d18a7..cf166d5a8 100644 --- a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/entertainment-config.properties +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/entertainment-config.properties @@ -20,6 +20,12 @@ abstracto.systemConfigs.slotsCooldown.longValue=30 abstracto.featureFlags.economy.featureName=economy abstracto.featureFlags.economy.enabled=false +abstracto.featureFlags.games.featureName=games +abstracto.featureFlags.games.enabled=false + +abstracto.systemConfigs.minesCreditsFactor.name=minesCreditsFactor +abstracto.systemConfigs.minesCreditsFactor.doubleValue=5 + # for now this is fine abstracto.systemConfigs.creditGambleJackpot.name=creditGambleJackpot abstracto.systemConfigs.creditGambleJackpot.longValue=1000 \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/migrations/1.4.11/collection.xml b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/migrations/1.4.11/collection.xml new file mode 100644 index 000000000..121b5aadf --- /dev/null +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/migrations/1.4.11/collection.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/migrations/1.4.11/seedData/command.xml b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/migrations/1.4.11/seedData/command.xml new file mode 100644 index 000000000..0bb85a893 --- /dev/null +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/migrations/1.4.11/seedData/command.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/migrations/1.4.11/seedData/data.xml b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/migrations/1.4.11/seedData/data.xml new file mode 100644 index 000000000..d492a359d --- /dev/null +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/migrations/1.4.11/seedData/data.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/migrations/1.4.11/seedData/feature.xml b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/migrations/1.4.11/seedData/feature.xml new file mode 100644 index 000000000..6d59be348 --- /dev/null +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/migrations/1.4.11/seedData/feature.xml @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/migrations/entertainment-changeLog.xml b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/migrations/entertainment-changeLog.xml index e0a4ff6f7..bc443ea07 100644 --- a/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/migrations/entertainment-changeLog.xml +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-impl/src/main/resources/migrations/entertainment-changeLog.xml @@ -11,4 +11,5 @@ + \ No newline at end of file diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/config/EntertainmentFeatureDefinition.java b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/config/EntertainmentFeatureDefinition.java index 03ef291f3..d324f5dfc 100644 --- a/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/config/EntertainmentFeatureDefinition.java +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/config/EntertainmentFeatureDefinition.java @@ -5,7 +5,7 @@ import lombok.Getter; @Getter public enum EntertainmentFeatureDefinition implements FeatureDefinition { - ENTERTAINMENT("entertainment"), ECONOMY("economy"); + ENTERTAINMENT("entertainment"), ECONOMY("economy"), GAMES("games"); private String key; diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/config/EntertainmentSlashCommandNames.java b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/config/EntertainmentSlashCommandNames.java index 36518233e..05718b1d1 100644 --- a/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/config/EntertainmentSlashCommandNames.java +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/config/EntertainmentSlashCommandNames.java @@ -4,4 +4,5 @@ public class EntertainmentSlashCommandNames { public static final String ENTERTAINMENT = "entertainment"; public static final String UTILITY = "utility"; public static final String ECONOMY = "economy"; + public static final String GAME = "game"; } diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/config/GamesFeatureConfig.java b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/config/GamesFeatureConfig.java new file mode 100644 index 000000000..31fb7d7cd --- /dev/null +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/config/GamesFeatureConfig.java @@ -0,0 +1,25 @@ +package dev.sheldan.abstracto.entertainment.config; + +import dev.sheldan.abstracto.core.config.FeatureConfig; +import dev.sheldan.abstracto.core.config.FeatureDefinition; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; + +@Component +public class GamesFeatureConfig implements FeatureConfig { + + public static final String MINES_CREDITS_FACTOR = "minesCreditsFactor"; + + @Override + public FeatureDefinition getFeature() { + return EntertainmentFeatureDefinition.GAMES; + } + + @Override + public List getRequiredSystemConfigKeys() { + return Arrays.asList(MINES_CREDITS_FACTOR); + } + +} diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/exception/InvalidGameBoardException.java b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/exception/InvalidGameBoardException.java new file mode 100644 index 000000000..aab3a46fa --- /dev/null +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/exception/InvalidGameBoardException.java @@ -0,0 +1,15 @@ +package dev.sheldan.abstracto.entertainment.exception; + +import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException; + +public class InvalidGameBoardException extends AbstractoTemplatableException { + @Override + public String getTemplateName() { + return "invalid_mine_board_config_exception"; + } + + @Override + public Object getTemplateModel() { + return new Object(); + } +} diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/model/command/games/MineBoard.java b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/model/command/games/MineBoard.java new file mode 100644 index 000000000..95c900cc0 --- /dev/null +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/model/command/games/MineBoard.java @@ -0,0 +1,44 @@ +package dev.sheldan.abstracto.entertainment.model.command.games; + +import dev.sheldan.abstracto.entertainment.service.GameService; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +@Builder +public class MineBoard { + private String boardId; + private Long userId; + private Long serverId; + private Long messageId; + private Long channelId; + private Integer rowCount; + private Integer columnCount; + private Integer credits; + private Long creditChange; + private boolean creditsEnabled; + private Integer mineCount; + private List rows; + private GameService.MineResult state; + + public MineBoardField getField(int x, int y) { + if(x > columnCount || y > rowCount) { + throw new IllegalArgumentException("Out of bounds access to board."); + } + MineBoardRow mineBoardRow = rows.get(y); + return mineBoardRow.getFields().get(x); + } + + public List getFields() { + List fields = new ArrayList<>(); + rows.forEach(mineBoardRow -> fields.addAll(mineBoardRow.getFields())); + return fields; + } + + +} diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/model/command/games/MineBoardField.java b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/model/command/games/MineBoardField.java new file mode 100644 index 000000000..d4957ffec --- /dev/null +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/model/command/games/MineBoardField.java @@ -0,0 +1,25 @@ +package dev.sheldan.abstracto.entertainment.model.command.games; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Builder +@Setter +public class MineBoardField { + private MineBoardField.MineBoardFieldType type; + private Integer x; + private Integer y; + private Integer counterValue; + + public enum MineBoardFieldType { + MINE, UNCOVERED, COVERED, EXPLODED + } + + public static boolean canInteract(MineBoardFieldType currentType) { + return currentType != MineBoardFieldType.UNCOVERED && currentType != MineBoardFieldType.EXPLODED; + } + +} + diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/model/command/games/MineBoardPayload.java b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/model/command/games/MineBoardPayload.java new file mode 100644 index 000000000..88798eb05 --- /dev/null +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/model/command/games/MineBoardPayload.java @@ -0,0 +1,15 @@ +package dev.sheldan.abstracto.entertainment.model.command.games; + +import dev.sheldan.abstracto.core.interaction.button.ButtonPayload; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Builder +@Setter +public class MineBoardPayload implements ButtonPayload { + private MineBoard mineBoard; + private Integer x; + private Integer y; +} diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/model/command/games/MineBoardRow.java b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/model/command/games/MineBoardRow.java new file mode 100644 index 000000000..9805fe047 --- /dev/null +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/model/command/games/MineBoardRow.java @@ -0,0 +1,12 @@ +package dev.sheldan.abstracto.entertainment.model.command.games; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +public class MineBoardRow { + private List fields; +} diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/service/GameService.java b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/service/GameService.java new file mode 100644 index 000000000..1bc1c5e95 --- /dev/null +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/service/GameService.java @@ -0,0 +1,18 @@ +package dev.sheldan.abstracto.entertainment.service; + +import dev.sheldan.abstracto.entertainment.model.command.games.MineBoard; +import net.dv8tion.jda.api.entities.Message; + +public interface GameService { + MineBoard createBoard(Integer width, Integer height, Integer mines); + void persistMineBoardMessage(MineBoard mineBoard, Message message); + void updateMineBoard(MineBoard mineBoard); + void uncoverBoard(MineBoard mineBoard); + void evaluateCreditChanges(MineBoard mineBoard); + MineResult uncoverField(MineBoard board, Integer x, Integer y); + + + enum MineResult { + WON, LOST, CONTINUE + } +} diff --git a/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/service/management/EconomyUserManagementService.java b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/service/management/EconomyUserManagementService.java index 37405792f..ccdd9d38b 100644 --- a/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/service/management/EconomyUserManagementService.java +++ b/abstracto-application/abstracto-modules/entertainment/entertainment-int/src/main/java/dev/sheldan/abstracto/entertainment/service/management/EconomyUserManagementService.java @@ -1,5 +1,6 @@ package dev.sheldan.abstracto.entertainment.service.management; +import dev.sheldan.abstracto.core.models.ServerUser; import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.entertainment.model.database.EconomyLeaderboardResult; @@ -11,6 +12,7 @@ import java.util.Optional; public interface EconomyUserManagementService { EconomyUser createUser(AUserInAServer aUserInAServer); Optional getUser(AUserInAServer aUserInAServer); + Optional getUser(ServerUser serverUser); EconomyLeaderboardResult getRankOfUserInServer(AUserInAServer aUserInAServer); List getRanksInServer(AServer server, Integer page, Integer size); } diff --git a/abstracto-application/abstracto-modules/suggestion/suggestion-impl/src/main/java/dev/sheldan/abstracto/suggestion/job/SuggestionEvaluationJob.java b/abstracto-application/abstracto-modules/suggestion/suggestion-impl/src/main/java/dev/sheldan/abstracto/suggestion/job/SuggestionEvaluationJob.java index 2fe3a7444..d58dc98d7 100644 --- a/abstracto-application/abstracto-modules/suggestion/suggestion-impl/src/main/java/dev/sheldan/abstracto/suggestion/job/SuggestionEvaluationJob.java +++ b/abstracto-application/abstracto-modules/suggestion/suggestion-impl/src/main/java/dev/sheldan/abstracto/suggestion/job/SuggestionEvaluationJob.java @@ -1,6 +1,8 @@ package dev.sheldan.abstracto.suggestion.job; import dev.sheldan.abstracto.suggestion.service.SuggestionService; +import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.quartz.DisallowConcurrentExecution; import org.quartz.JobExecutionContext; @@ -13,6 +15,8 @@ import org.springframework.stereotype.Component; @Slf4j @DisallowConcurrentExecution @Component +@Getter +@Setter @PersistJobDataAfterExecution public class SuggestionEvaluationJob extends QuartzJobBean { diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/CommandReceivedHandler.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/CommandReceivedHandler.java index d8e135286..6b9fc4031 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/CommandReceivedHandler.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/CommandReceivedHandler.java @@ -10,6 +10,7 @@ import dev.sheldan.abstracto.core.command.exception.InsufficientParametersExcept import dev.sheldan.abstracto.core.command.execution.*; import dev.sheldan.abstracto.core.command.handler.CommandParameterHandler; import dev.sheldan.abstracto.core.command.handler.CommandParameterIterators; +import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.interaction.button.CommandConfirmationModel; import dev.sheldan.abstracto.core.interaction.button.CommandConfirmationPayload; import dev.sheldan.abstracto.core.command.service.CommandManager; @@ -109,6 +110,12 @@ public class CommandReceivedHandler extends ListenerAdapter { @Autowired private MessageService messageService; + @Autowired + private FeatureConfigService featureConfigService; + + @Autowired + private FeatureFlagService featureFlagService; + public static final String COMMAND_CONFIRMATION_ORIGIN = "commandConfirmation"; public static final String COMMAND_CONFIRMATION_MESSAGE_TEMPLATE_KEY = "command_confirmation_message"; public static final String COMMAND_PROCESSED = "command.processed"; @@ -260,7 +267,7 @@ public class CommandReceivedHandler extends ListenerAdapter { .message(event.getMessage()) .jda(event.getJDA()) .userInitiatedContext(userInitiatedContext); - validateCommandParameters(parsedParameters, foundCommand); + validateCommandParameters(parsedParameters, foundCommand, event.getGuild().getIdLong()); CommandContext commandContext = commandContextBuilder.parameters(parsedParameters).build(); CompletableFuture conditionResultFuture = commandService.isCommandExecutable(foundCommand, commandContext); conditionResultFuture.thenAccept(conditionResult -> { @@ -347,7 +354,7 @@ public class CommandReceivedHandler extends ListenerAdapter { reportException(event.getMessage(), event.getChannel(), event.getMember(), foundCommand, throwable, s); } - private void validateCommandParameters(Parameters parameters, Command foundCommand) { + private void validateCommandParameters(Parameters parameters, Command foundCommand, Long serverId) { CommandConfiguration commandConfiguration = foundCommand.getConfiguration(); List parameterList = commandConfiguration.getParameters(); // we iterate only over the actually found parameters, that way we dont have to consider the optional parameters @@ -362,8 +369,9 @@ public class CommandReceivedHandler extends ListenerAdapter { } } } - if (commandConfiguration.getNecessaryParameterCount() > parameters.getParameters().size()) { - String nextParameterName = commandConfiguration.getParameters().get(commandConfiguration.getNecessaryParameterCount() - 1).getName(); + long necessaryParameters = commandManager.getParameterCountForCommandConfig(commandConfiguration, serverId); + if (necessaryParameters > parameters.getParameters().size()) { + String nextParameterName = commandConfiguration.getParameters().get((int)necessaryParameters - 1).getName(); throw new InsufficientParametersException(foundCommand, nextParameterName); } } @@ -438,6 +446,22 @@ public class CommandReceivedHandler extends ListenerAdapter { } else { break; } + if(!param.getDependentFeatures().isEmpty()) { + List featureDefinitions = param + .getDependentFeatures() + .stream() + .map(s -> featureConfigService.getFeatureEnum(s)) + .collect(Collectors.toList()); + boolean parameterActiveForFeatures = false; + for (FeatureDefinition featureDefinition : featureDefinitions) { + if(featureFlagService.getFeatureFlagValue(featureDefinition, message.getGuild().getIdLong())) { + parameterActiveForFeatures = true; + } + } + if(!parameterActiveForFeatures) { + break; + } + } UnparsedCommandParameterPiece value = unParsedCommandParameter.getParameters().get(i); // TODO might be able to do this without iterating, if we directly associated the handler required for each parameter for (CommandParameterHandler handler : usedParameterHandler) { diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/service/CommandManager.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/service/CommandManager.java index 9d39a0123..18795c25c 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/service/CommandManager.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/command/service/CommandManager.java @@ -4,13 +4,17 @@ import dev.sheldan.abstracto.core.command.Command; import dev.sheldan.abstracto.core.command.CommandReceivedHandler; import dev.sheldan.abstracto.core.command.config.CommandConfiguration; import dev.sheldan.abstracto.core.command.config.ModuleDefinition; +import dev.sheldan.abstracto.core.command.config.Parameter; import dev.sheldan.abstracto.core.command.exception.CommandNotFoundException; import dev.sheldan.abstracto.core.command.exception.InsufficientParametersException; import dev.sheldan.abstracto.core.command.execution.UnParsedCommandParameter; import dev.sheldan.abstracto.core.command.model.database.ACommandInAServer; import dev.sheldan.abstracto.core.command.model.database.ACommandInServerAlias; +import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.metric.service.MetricService; import dev.sheldan.abstracto.core.service.ConfigService; +import dev.sheldan.abstracto.core.service.FeatureConfigService; +import dev.sheldan.abstracto.core.service.FeatureFlagService; import dev.sheldan.abstracto.core.service.management.DefaultConfigManagementService; import net.dv8tion.jda.api.entities.Message; import org.springframework.beans.factory.annotation.Autowired; @@ -20,6 +24,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.function.Predicate; +import java.util.stream.Collectors; @Service public class CommandManager implements CommandRegistry { @@ -40,9 +45,15 @@ public class CommandManager implements CommandRegistry { @Autowired private CommandInServerAliasService commandInServerAliasService; + @Autowired + private FeatureFlagService featureFlagService; + + @Autowired + private FeatureConfigService featureConfigService; + @Override public Optional findCommandByParameters(String name, UnParsedCommandParameter unParsedCommandParameter, Long serverId) { - Optional commandOptional = commands.stream().filter(getCommandByNameAndParameterPredicate(name, unParsedCommandParameter)).findFirst(); + Optional commandOptional = commands.stream().filter(getCommandByNameAndParameterPredicate(name, unParsedCommandParameter, serverId)).findFirst(); if(!commandOptional.isPresent()) { commandOptional = getCommandViaAliasAndParameter(name, unParsedCommandParameter, serverId); } @@ -55,7 +66,7 @@ public class CommandManager implements CommandRegistry { // if its present, retrieve the original command ACommandInAServer command = aliasOptional.get().getCommandInAServer(); // and find the command based on the newly found name - return commands.stream().filter(getCommandByNameAndParameterPredicate(command.getCommandReference().getName(), unParsedCommandParameter)).findFirst(); + return commands.stream().filter(getCommandByNameAndParameterPredicate(command.getCommandReference().getName(), unParsedCommandParameter, serverId)).findFirst(); } return Optional.empty(); } @@ -71,7 +82,7 @@ public class CommandManager implements CommandRegistry { return Optional.empty(); } - private Predicate getCommandByNameAndParameterPredicate(String name, UnParsedCommandParameter unParsedCommandParameter) { + private Predicate getCommandByNameAndParameterPredicate(String name, UnParsedCommandParameter unParsedCommandParameter, Long serverId) { return (Command commandObj) -> { CommandConfiguration commandConfiguration = commandObj.getConfiguration(); if (commandConfiguration == null) { @@ -80,7 +91,7 @@ public class CommandManager implements CommandRegistry { if (!commandNameOrAliasMatches(name, commandConfiguration)) { return false; } - return verifyCommandConfiguration(unParsedCommandParameter, commandObj, commandConfiguration); + return verifyCommandConfiguration(unParsedCommandParameter, commandObj, commandConfiguration, serverId); }; } @@ -94,15 +105,47 @@ public class CommandManager implements CommandRegistry { }; } - private boolean verifyCommandConfiguration(UnParsedCommandParameter unParsedCommandParameter, Command command, CommandConfiguration commandConfiguration) { - if(commandConfiguration.getParameters() != null && commandConfiguration.getNecessaryParameterCount() > unParsedCommandParameter.getParameters().size()){ - String nextParameterName = commandConfiguration.getParameters().get(commandConfiguration.getNecessaryParameterCount() - 1).getName(); - metricService.incrementCounter(CommandReceivedHandler.COMMANDS_WRONG_PARAMETER_COUNTER); - throw new InsufficientParametersException(command, nextParameterName); + private boolean verifyCommandConfiguration(UnParsedCommandParameter unParsedCommandParameter, Command command, CommandConfiguration commandConfiguration, Long serverId) { + if(commandConfiguration.getParameters() != null) { + long necessaryParameterCount = getParameterCountForCommandConfig(commandConfiguration, serverId); + if(necessaryParameterCount > unParsedCommandParameter.getParameters().size()){ + String nextParameterName = commandConfiguration.getParameters().get((int) necessaryParameterCount - 1).getName(); + metricService.incrementCounter(CommandReceivedHandler.COMMANDS_WRONG_PARAMETER_COUNTER); + throw new InsufficientParametersException(command, nextParameterName); + } } return true; } + public long getParameterCountForCommandConfig(CommandConfiguration commandConfiguration, Long serverId) { + if(commandConfiguration.getParameters() == null || commandConfiguration.getParameters().isEmpty()) { + return 0; + } + return commandConfiguration + .getParameters() + .stream().filter(parameter -> isParameterRequired(parameter, serverId)) + .count(); + } + + private boolean isParameterRequired(Parameter parameter, Long serverId) { + if(parameter.getDependentFeatures().isEmpty() || parameter.isOptional()) { + return !parameter.isOptional(); + } else { + List featureDefinitions = parameter + .getDependentFeatures() + .stream() + .map(s -> featureConfigService.getFeatureEnum(s)) + .collect(Collectors.toList()); + boolean required = false; + for (FeatureDefinition featureDefinition : featureDefinitions) { + if(featureFlagService.getFeatureFlagValue(featureDefinition, serverId)) { + required = true; + } + } + return required; + } + } + private boolean commandNameOrAliasMatches(String name, CommandConfiguration commandConfiguration) { return commandConfiguration.getName().equalsIgnoreCase(name) || commandConfiguration.getAliases() != null && commandConfiguration.getAliases().stream().anyMatch(s -> s.equalsIgnoreCase(name)); diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/CommandLoaderListener.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/CommandLoaderListener.java index b3b296956..e074d3f9b 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/CommandLoaderListener.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/CommandLoaderListener.java @@ -92,7 +92,7 @@ public class CommandLoaderListener implements AsyncStartupListener { return; } log.info("Updating slash command {} in guild {}.", command.getConfiguration().getName(), guild.getId()); - slashCommandService.convertCommandConfigToCommandData(command.getConfiguration(), slashCommandsToUpdate); + slashCommandService.convertCommandConfigToCommandData(command.getConfiguration(), slashCommandsToUpdate, guild.getIdLong()); }); log.info("Updating context commands for guild {}.", guild.getIdLong()); diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/ComponentPayloadServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/ComponentPayloadServiceBean.java index 053252697..1e36e7d5b 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/ComponentPayloadServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/ComponentPayloadServiceBean.java @@ -25,6 +25,12 @@ public class ComponentPayloadServiceBean implements ComponentPayloadService { return componentPayloadManagementService.createPayload(componentId, json, payload.getClass(), origin, server, ComponentType.BUTTON); } + @Override + public void updateButtonPayload(String componentId, ButtonPayload payload) { + String json = gson.toJson(payload); + componentPayloadManagementService.updatePayload(componentId, json); + } + @Override public ComponentPayload createSelectionPayload(String componentId, ButtonPayload payload, String origin, AServer server) { String json = gson.toJson(payload); diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandServiceBean.java index 074a0cf34..ced6fa19a 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandServiceBean.java @@ -1,11 +1,15 @@ package dev.sheldan.abstracto.core.interaction.slash; import dev.sheldan.abstracto.core.command.config.CommandConfiguration; +import dev.sheldan.abstracto.core.command.config.Parameter; import dev.sheldan.abstracto.core.command.model.database.ACommand; import dev.sheldan.abstracto.core.command.model.database.ACommandInAServer; import dev.sheldan.abstracto.core.command.service.management.CommandInServerManagementService; import dev.sheldan.abstracto.core.command.service.management.CommandManagementService; +import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService; +import dev.sheldan.abstracto.core.service.FeatureConfigService; +import dev.sheldan.abstracto.core.service.FeatureFlagService; import dev.sheldan.abstracto.core.templating.service.TemplateService; import dev.sheldan.abstracto.core.utils.CompletableFutureList; import net.dv8tion.jda.api.entities.Guild; @@ -42,8 +46,14 @@ public class SlashCommandServiceBean implements SlashCommandService { @Autowired private SlashCommandServiceBean self; + @Autowired + private FeatureConfigService featureConfigService; + + @Autowired + private FeatureFlagService featureFlagService; + @Override - public void convertCommandConfigToCommandData(CommandConfiguration commandConfiguration, List, SlashCommandData>> existingCommands) { + public void convertCommandConfigToCommandData(CommandConfiguration commandConfiguration, List, SlashCommandData>> existingCommands, Long serverId) { boolean isTemplated = commandConfiguration.isTemplated(); SlashCommandConfig slashConfig = commandConfiguration.getSlashCommandConfig(); String description; @@ -76,10 +86,10 @@ public class SlashCommandServiceBean implements SlashCommandService { groupData.addSubcommands(slashCommand); rootCommand.addSubcommandGroups(groupData); } - List requiredParameters = getParameters(commandConfiguration, isTemplated, internalCommandName); + List requiredParameters = getParameters(commandConfiguration, isTemplated, internalCommandName, serverId); slashCommand.addOptions(requiredParameters); } else { - List requiredParameters = getParameters(commandConfiguration, isTemplated, internalCommandName); + List requiredParameters = getParameters(commandConfiguration, isTemplated, internalCommandName, serverId); rootCommand.addOptions(requiredParameters); } if(!existingRootCommand.isPresent()) { @@ -95,10 +105,18 @@ public class SlashCommandServiceBean implements SlashCommandService { } } - private List getParameters(CommandConfiguration commandConfiguration, boolean isTemplated, String internalCommandName) { + @Override + public void convertCommandConfigToCommandData(CommandConfiguration commandConfiguration, List, SlashCommandData>> existingCommands) { + convertCommandConfigToCommandData(commandConfiguration, existingCommands, null); + } + + private List getParameters(CommandConfiguration commandConfiguration, boolean isTemplated, String internalCommandName, Long serverId) { List requiredParameters = new ArrayList<>(); List optionalParameters = new ArrayList<>(); commandConfiguration.getParameters().forEach(parameter -> { + if(!shouldParameterBeCreated(parameter, serverId)) { + return; + } List types = slashCommandParameterService.getTypesFromParameter(parameter.getType()); if(types.size() > 1) { if(parameter.isListParam()) { @@ -132,6 +150,25 @@ public class SlashCommandServiceBean implements SlashCommandService { return requiredParameters; } + private boolean shouldParameterBeCreated(Parameter parameter, Long serverId) { + if(parameter.getDependentFeatures().isEmpty()) { + return true; + } else { + List featureDefinitions = parameter + .getDependentFeatures() + .stream() + .map(s -> featureConfigService.getFeatureEnum(s)) + .collect(Collectors.toList()); + + for (FeatureDefinition featureDefinition : featureDefinitions) { + if(!featureFlagService.getFeatureFlagValue(featureDefinition, serverId)) { + return false; + } + } + return true; + } + } + @Override public CompletableFuture> updateGuildSlashCommand(Guild guild, List, SlashCommandData>> commandData) { diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/listener/SlashCommandFeatureActivationListener.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/listener/SlashCommandFeatureActivationListener.java index 979b72ffa..10543b47a 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/listener/SlashCommandFeatureActivationListener.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/interaction/slash/listener/SlashCommandFeatureActivationListener.java @@ -55,7 +55,7 @@ public class SlashCommandFeatureActivationListener implements FeatureActivationL return; } log.info("Updating slash command {} in guild {}.", command.getConfiguration().getName(), guild.getId()); - slashCommandService.convertCommandConfigToCommandData(command.getConfiguration(), commandsToUpDate); + slashCommandService.convertCommandConfigToCommandData(command.getConfiguration(), commandsToUpDate, model.getServerId()); }); slashCommandService.addGuildSlashCommands(guild, commandsToUpDate) .thenAccept(commands1 -> log.info("Updating {} slash commands in guild {}.", commandsToUpDate.size(), guild.getIdLong())); diff --git a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ComponentPayloadManagementServiceBean.java b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ComponentPayloadManagementServiceBean.java index fa50f9d44..282ed6b1a 100644 --- a/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ComponentPayloadManagementServiceBean.java +++ b/abstracto-application/core/core-impl/src/main/java/dev/sheldan/abstracto/core/service/management/ComponentPayloadManagementServiceBean.java @@ -42,6 +42,11 @@ public class ComponentPayloadManagementServiceBean implements ComponentPayloadMa return repository.save(componentPayload); } + @Override + public void updatePayload(String id, String payload) { + findPayload(id).ifPresent(componentPayload -> componentPayload.setPayload(payload)); + } + @Override public ComponentPayload createButtonPayload(ButtonConfigModel buttonConfigModel, AServer server) { String payload = gson.toJson(buttonConfigModel.getButtonPayload()); diff --git a/abstracto-application/core/core-impl/src/test/java/dev/sheldan/abstracto/core/command/service/CommandManagerTest.java b/abstracto-application/core/core-impl/src/test/java/dev/sheldan/abstracto/core/command/service/CommandManagerTest.java index eae57d6ea..4a70563d9 100644 --- a/abstracto-application/core/core-impl/src/test/java/dev/sheldan/abstracto/core/command/service/CommandManagerTest.java +++ b/abstracto-application/core/core-impl/src/test/java/dev/sheldan/abstracto/core/command/service/CommandManagerTest.java @@ -100,7 +100,6 @@ public class CommandManagerTest { commands.add(firstCommand); when(commandConfiguration.getName()).thenReturn(COMMAND_NAME); when(firstCommand.getConfiguration()).thenReturn(commandConfiguration); - when(commandConfiguration.getNecessaryParameterCount()).thenReturn(0); when(parsedCommandParameter.getParameters()).thenReturn(new ArrayList<>()); Optional foundCommand = testUnit.findCommandByParameters(COMMAND_NAME, parsedCommandParameter, SERVER_ID); Assert.assertEquals(firstCommand, foundCommand.get()); @@ -132,7 +131,6 @@ public class CommandManagerTest { when(commandConfiguration.getName()).thenReturn(COMMAND_NAME_2); when(commandConfiguration.getAliases()).thenReturn(Arrays.asList(COMMAND_NAME)); when(firstCommand.getConfiguration()).thenReturn(commandConfiguration); - when(commandConfiguration.getNecessaryParameterCount()).thenReturn(0); when(parsedCommandParameter.getParameters()).thenReturn(new ArrayList<>()); Optional foundCommand = testUnit.findCommandByParameters(COMMAND_NAME, parsedCommandParameter, SERVER_ID); Assert.assertEquals(firstCommand, foundCommand.get()); @@ -144,7 +142,6 @@ public class CommandManagerTest { when(commandConfiguration.getParameters()).thenReturn(Arrays.asList(parameter)); when(firstCommand.getConfiguration()).thenReturn(commandConfiguration); when(commandConfiguration.getName()).thenReturn(COMMAND_NAME); - when(commandConfiguration.getNecessaryParameterCount()).thenReturn(1); when(parsedCommandParameter.getParameters()).thenReturn(new ArrayList<>()); testUnit.findCommandByParameters(COMMAND_NAME, parsedCommandParameter, SERVER_ID); } @@ -154,7 +151,6 @@ public class CommandManagerTest { commands.add(firstCommand); when(commandConfiguration.getName()).thenReturn(COMMAND_NAME); when(firstCommand.getConfiguration()).thenReturn(commandConfiguration); - when(commandConfiguration.getNecessaryParameterCount()).thenReturn(0); when(parsedCommandParameter.getParameters()).thenReturn(new ArrayList<>()); setupAliasTest(); Optional foundCommand = testUnit.findCommandByParameters(ALIAS_NAME, parsedCommandParameter, SERVER_ID); diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/config/CommandConfiguration.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/config/CommandConfiguration.java index d827d7433..fc3888d00 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/config/CommandConfiguration.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/config/CommandConfiguration.java @@ -50,7 +50,4 @@ public class CommandConfiguration { .enabled(false) .build(); - public int getNecessaryParameterCount(){ - return (int) parameters.stream().filter(parameter -> !parameter.isOptional()).count(); - } } diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/config/Parameter.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/config/Parameter.java index f88a3fe6e..98e22fe98 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/config/Parameter.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/config/Parameter.java @@ -34,6 +34,9 @@ public class Parameter implements Serializable { private List validators = new ArrayList<>(); @Builder.Default private Map additionalInfo = new HashMap<>(); + // these are the features which potentially require this parameter + @Builder.Default + private List dependentFeatures = new ArrayList<>(); public String getSlashCompatibleName() { return name.toLowerCase(Locale.ROOT); diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/config/validator/MaxIntegerValueValidator.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/config/validator/MaxIntegerValueValidator.java index fb1a6ef51..33131b8be 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/config/validator/MaxIntegerValueValidator.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/config/validator/MaxIntegerValueValidator.java @@ -38,6 +38,13 @@ public class MaxIntegerValueValidator implements ParameterValidator { return Arrays.asList(param); } + public static MaxIntegerValueValidator max(Long number) { + return MaxIntegerValueValidator + .builder() + .maxValue(number) + .build(); + } + @Override public String getExceptionTemplateName() { return "command_parameter_validation_value_too_large"; diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/service/CommandRegistry.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/service/CommandRegistry.java index 0c2f2b2a3..f33646bc9 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/service/CommandRegistry.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/command/service/CommandRegistry.java @@ -2,6 +2,7 @@ package dev.sheldan.abstracto.core.command.service; import dev.sheldan.abstracto.core.command.Command; +import dev.sheldan.abstracto.core.command.config.CommandConfiguration; import dev.sheldan.abstracto.core.command.config.ModuleDefinition; import dev.sheldan.abstracto.core.command.execution.UnParsedCommandParameter; import net.dv8tion.jda.api.entities.Message; @@ -19,4 +20,5 @@ public interface CommandRegistry { Command getCommandByName(String name, boolean searchAliases, Long serverId); Optional getCommandByNameOptional(String name, boolean searchAliases, Long serverId); String getCommandName(String input, Long serverId); + long getParameterCountForCommandConfig(CommandConfiguration commandConfiguration, Long serverId); } diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/interaction/ComponentPayloadManagementService.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/interaction/ComponentPayloadManagementService.java index cfc9aa242..f09f930df 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/interaction/ComponentPayloadManagementService.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/interaction/ComponentPayloadManagementService.java @@ -11,6 +11,7 @@ import java.util.Optional; public interface ComponentPayloadManagementService { ComponentPayload createPayload(String id, String payload, Class payloadType, String buttonOrigin, AServer server, ComponentType type); + void updatePayload(String id, String payload); ComponentPayload createButtonPayload(ButtonConfigModel buttonConfigModel, AServer server); ComponentPayload createButtonPayload(ButtonConfigModel buttonConfigModel, Long serverId); ComponentPayload createModalPayload(ModalConfigPayload payloadConfig, Long serverId); diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/interaction/ComponentPayloadService.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/interaction/ComponentPayloadService.java index f48f0ed35..74d4177f9 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/interaction/ComponentPayloadService.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/interaction/ComponentPayloadService.java @@ -6,5 +6,6 @@ import dev.sheldan.abstracto.core.interaction.button.ButtonPayload; public interface ComponentPayloadService { ComponentPayload createButtonPayload(String componentId, ButtonPayload payload, String origin, AServer server); + void updateButtonPayload(String componentId, ButtonPayload payload); ComponentPayload createSelectionPayload(String componentId, ButtonPayload payload, String origin, AServer server); } diff --git a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandService.java b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandService.java index 22e4c80d4..d6379384f 100644 --- a/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandService.java +++ b/abstracto-application/core/core-int/src/main/java/dev/sheldan/abstracto/core/interaction/slash/SlashCommandService.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.concurrent.CompletableFuture; public interface SlashCommandService { + void convertCommandConfigToCommandData(CommandConfiguration commandConfiguration, List, SlashCommandData>> existingCommands, Long serverId); void convertCommandConfigToCommandData(CommandConfiguration commandConfiguration, List, SlashCommandData>> existingCommands); CompletableFuture> updateGuildSlashCommand(Guild guild, List, SlashCommandData>> commandData); CompletableFuture deleteGuildSlashCommands(Guild guild, List slashCommandId, List commandInServerIdsToUnset);