mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-04-14 03:45:57 +00:00
[AB-66] adding mines game
fixing suggestion evaluation job not getting the correct parameters adding feature dependent parameters
This commit is contained in:
@@ -89,14 +89,14 @@ public class Slots extends AbstractConditionableCommand {
|
|||||||
.templated(true)
|
.templated(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Parameter lowParameter = Parameter
|
Parameter bidParameter = Parameter
|
||||||
.builder()
|
.builder()
|
||||||
.name(BID_PARAMETER)
|
.name(BID_PARAMETER)
|
||||||
.type(Integer.class)
|
.type(Integer.class)
|
||||||
.templated(true)
|
.templated(true)
|
||||||
.validators(Arrays.asList(MinIntegerValueValidator.min(0L)))
|
.validators(Arrays.asList(MinIntegerValueValidator.min(0L)))
|
||||||
.build();
|
.build();
|
||||||
parameters.add(lowParameter);
|
parameters.add(bidParameter);
|
||||||
|
|
||||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||||
.builder()
|
.builder()
|
||||||
|
|||||||
@@ -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<CommandResult> 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<EconomyUser> 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<CommandResult> executeAsync(CommandContext commandContext) {
|
||||||
|
Integer width = 5;
|
||||||
|
List<Object> 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<EconomyUser> 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<CompletableFuture<Message>> 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<Parameter> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import java.util.Optional;
|
|||||||
@Repository
|
@Repository
|
||||||
public interface EconomyUserRepository extends JpaRepository<EconomyUser, Long> {
|
public interface EconomyUserRepository extends JpaRepository<EconomyUser, Long> {
|
||||||
Optional<EconomyUser> findByUser(AUserInAServer aUserInAServer);
|
Optional<EconomyUser> findByUser(AUserInAServer aUserInAServer);
|
||||||
|
Optional<EconomyUser> findByServer_IdAndUser_UserReference_Id(Long serverId, Long userId);
|
||||||
|
|
||||||
@Query(value = "WITH economy_user_ranked AS" +
|
@Query(value = "WITH economy_user_ranked AS" +
|
||||||
"( " +
|
"( " +
|
||||||
|
|||||||
@@ -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<MineBoardField> 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<EconomyUser> 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<String> alreadyConsidered = new HashSet<>();
|
||||||
|
Queue<MineBoardField> 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<MineBoardField> neighbors = getNeighbors(board, fieldToHandle.getX(), fieldToHandle.getY(), false);
|
||||||
|
List<MineBoardField> 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<MineBoardField> getNeighbors(MineBoard mineBoard, int xPosition, int yPosition) {
|
||||||
|
return getNeighbors(mineBoard, xPosition, yPosition, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<MineBoardField> getNeighbors(MineBoard board, int xPosition, int yPosition, boolean directOnly) {
|
||||||
|
List<MineBoardField> 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<MineBoardRow> rows = new ArrayList<>();
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
List<MineBoardField> 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<String> usedKeys = new HashSet<>();
|
||||||
|
int maxIterations = 1_0000_000;
|
||||||
|
int iterations = 0;
|
||||||
|
List<Pair<Integer, Integer>> 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<MineBoardField> 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package dev.sheldan.abstracto.entertainment.service.management;
|
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.AServer;
|
||||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||||
import dev.sheldan.abstracto.entertainment.model.database.EconomyLeaderboardResult;
|
import dev.sheldan.abstracto.entertainment.model.database.EconomyLeaderboardResult;
|
||||||
@@ -39,6 +40,11 @@ public class EconomyUserManagementServiceBean implements EconomyUserManagementSe
|
|||||||
return repository.findByUser(aUserInAServer);
|
return repository.findByUser(aUserInAServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<EconomyUser> getUser(ServerUser serverUser) {
|
||||||
|
return repository.findByServer_IdAndUser_UserReference_Id(serverUser.getServerId(), serverUser.getUserId());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EconomyLeaderboardResult getRankOfUserInServer(AUserInAServer aUserInAServer) {
|
public EconomyLeaderboardResult getRankOfUserInServer(AUserInAServer aUserInAServer) {
|
||||||
return repository.getRankOfUserInServer(aUserInAServer.getUserInServerId(), aUserInAServer.getServerReference().getId());
|
return repository.getRankOfUserInServer(aUserInAServer.getUserInServerId(), aUserInAServer.getServerReference().getId());
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ abstracto.systemConfigs.slotsCooldown.longValue=30
|
|||||||
abstracto.featureFlags.economy.featureName=economy
|
abstracto.featureFlags.economy.featureName=economy
|
||||||
abstracto.featureFlags.economy.enabled=false
|
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
|
# for now this is fine
|
||||||
abstracto.systemConfigs.creditGambleJackpot.name=creditGambleJackpot
|
abstracto.systemConfigs.creditGambleJackpot.name=creditGambleJackpot
|
||||||
abstracto.systemConfigs.creditGambleJackpot.longValue=1000
|
abstracto.systemConfigs.creditGambleJackpot.longValue=1000
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||||
|
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||||
|
<property name="entertainmentModule" value="(SELECT id FROM module WHERE name = 'entertainment')"/>
|
||||||
|
<property name="gamesFeature" value="(SELECT id FROM feature WHERE key = 'games')"/>
|
||||||
|
|
||||||
|
<changeSet author="Sheldan" id="mines-commands">
|
||||||
|
<insert tableName="command">
|
||||||
|
<column name="name" value="mines"/>
|
||||||
|
<column name="module_id" valueComputed="${entertainmentModule}"/>
|
||||||
|
<column name="feature_id" valueComputed="${gamesFeature}"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||||
|
<include file="feature.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="command.xml" relativeToChangelogFile="true"/>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
|
||||||
|
<changeSet author="Sheldan" id="games_feature-insertion">
|
||||||
|
<insert tableName="feature">
|
||||||
|
<column name="key" value="games"/>
|
||||||
|
</insert>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
@@ -11,4 +11,5 @@
|
|||||||
<include file="1.2.9-entertainment/collection.xml" relativeToChangelogFile="true"/>
|
<include file="1.2.9-entertainment/collection.xml" relativeToChangelogFile="true"/>
|
||||||
<include file="1.4.0/collection.xml" relativeToChangelogFile="true"/>
|
<include file="1.4.0/collection.xml" relativeToChangelogFile="true"/>
|
||||||
<include file="1.4.3/collection.xml" relativeToChangelogFile="true"/>
|
<include file="1.4.3/collection.xml" relativeToChangelogFile="true"/>
|
||||||
|
<include file="1.4.11/collection.xml" relativeToChangelogFile="true"/>
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
@@ -5,7 +5,7 @@ import lombok.Getter;
|
|||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public enum EntertainmentFeatureDefinition implements FeatureDefinition {
|
public enum EntertainmentFeatureDefinition implements FeatureDefinition {
|
||||||
ENTERTAINMENT("entertainment"), ECONOMY("economy");
|
ENTERTAINMENT("entertainment"), ECONOMY("economy"), GAMES("games");
|
||||||
|
|
||||||
private String key;
|
private String key;
|
||||||
|
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ public class EntertainmentSlashCommandNames {
|
|||||||
public static final String ENTERTAINMENT = "entertainment";
|
public static final String ENTERTAINMENT = "entertainment";
|
||||||
public static final String UTILITY = "utility";
|
public static final String UTILITY = "utility";
|
||||||
public static final String ECONOMY = "economy";
|
public static final String ECONOMY = "economy";
|
||||||
|
public static final String GAME = "game";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<String> getRequiredSystemConfigKeys() {
|
||||||
|
return Arrays.asList(MINES_CREDITS_FACTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<MineBoardRow> 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<MineBoardField> getFields() {
|
||||||
|
List<MineBoardField> fields = new ArrayList<>();
|
||||||
|
rows.forEach(mineBoardRow -> fields.addAll(mineBoardRow.getFields()));
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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<MineBoardField> fields;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package dev.sheldan.abstracto.entertainment.service.management;
|
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.AServer;
|
||||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||||
import dev.sheldan.abstracto.entertainment.model.database.EconomyLeaderboardResult;
|
import dev.sheldan.abstracto.entertainment.model.database.EconomyLeaderboardResult;
|
||||||
@@ -11,6 +12,7 @@ import java.util.Optional;
|
|||||||
public interface EconomyUserManagementService {
|
public interface EconomyUserManagementService {
|
||||||
EconomyUser createUser(AUserInAServer aUserInAServer);
|
EconomyUser createUser(AUserInAServer aUserInAServer);
|
||||||
Optional<EconomyUser> getUser(AUserInAServer aUserInAServer);
|
Optional<EconomyUser> getUser(AUserInAServer aUserInAServer);
|
||||||
|
Optional<EconomyUser> getUser(ServerUser serverUser);
|
||||||
EconomyLeaderboardResult getRankOfUserInServer(AUserInAServer aUserInAServer);
|
EconomyLeaderboardResult getRankOfUserInServer(AUserInAServer aUserInAServer);
|
||||||
List<EconomyUser> getRanksInServer(AServer server, Integer page, Integer size);
|
List<EconomyUser> getRanksInServer(AServer server, Integer page, Integer size);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package dev.sheldan.abstracto.suggestion.job;
|
package dev.sheldan.abstracto.suggestion.job;
|
||||||
|
|
||||||
import dev.sheldan.abstracto.suggestion.service.SuggestionService;
|
import dev.sheldan.abstracto.suggestion.service.SuggestionService;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.quartz.DisallowConcurrentExecution;
|
import org.quartz.DisallowConcurrentExecution;
|
||||||
import org.quartz.JobExecutionContext;
|
import org.quartz.JobExecutionContext;
|
||||||
@@ -13,6 +15,8 @@ import org.springframework.stereotype.Component;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@DisallowConcurrentExecution
|
@DisallowConcurrentExecution
|
||||||
@Component
|
@Component
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
@PersistJobDataAfterExecution
|
@PersistJobDataAfterExecution
|
||||||
public class SuggestionEvaluationJob extends QuartzJobBean {
|
public class SuggestionEvaluationJob extends QuartzJobBean {
|
||||||
|
|
||||||
|
|||||||
@@ -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.execution.*;
|
||||||
import dev.sheldan.abstracto.core.command.handler.CommandParameterHandler;
|
import dev.sheldan.abstracto.core.command.handler.CommandParameterHandler;
|
||||||
import dev.sheldan.abstracto.core.command.handler.CommandParameterIterators;
|
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.CommandConfirmationModel;
|
||||||
import dev.sheldan.abstracto.core.interaction.button.CommandConfirmationPayload;
|
import dev.sheldan.abstracto.core.interaction.button.CommandConfirmationPayload;
|
||||||
import dev.sheldan.abstracto.core.command.service.CommandManager;
|
import dev.sheldan.abstracto.core.command.service.CommandManager;
|
||||||
@@ -109,6 +110,12 @@ public class CommandReceivedHandler extends ListenerAdapter {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private MessageService messageService;
|
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_ORIGIN = "commandConfirmation";
|
||||||
public static final String COMMAND_CONFIRMATION_MESSAGE_TEMPLATE_KEY = "command_confirmation_message";
|
public static final String COMMAND_CONFIRMATION_MESSAGE_TEMPLATE_KEY = "command_confirmation_message";
|
||||||
public static final String COMMAND_PROCESSED = "command.processed";
|
public static final String COMMAND_PROCESSED = "command.processed";
|
||||||
@@ -260,7 +267,7 @@ public class CommandReceivedHandler extends ListenerAdapter {
|
|||||||
.message(event.getMessage())
|
.message(event.getMessage())
|
||||||
.jda(event.getJDA())
|
.jda(event.getJDA())
|
||||||
.userInitiatedContext(userInitiatedContext);
|
.userInitiatedContext(userInitiatedContext);
|
||||||
validateCommandParameters(parsedParameters, foundCommand);
|
validateCommandParameters(parsedParameters, foundCommand, event.getGuild().getIdLong());
|
||||||
CommandContext commandContext = commandContextBuilder.parameters(parsedParameters).build();
|
CommandContext commandContext = commandContextBuilder.parameters(parsedParameters).build();
|
||||||
CompletableFuture<ConditionResult> conditionResultFuture = commandService.isCommandExecutable(foundCommand, commandContext);
|
CompletableFuture<ConditionResult> conditionResultFuture = commandService.isCommandExecutable(foundCommand, commandContext);
|
||||||
conditionResultFuture.thenAccept(conditionResult -> {
|
conditionResultFuture.thenAccept(conditionResult -> {
|
||||||
@@ -347,7 +354,7 @@ public class CommandReceivedHandler extends ListenerAdapter {
|
|||||||
reportException(event.getMessage(), event.getChannel(), event.getMember(), foundCommand, throwable, s);
|
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();
|
CommandConfiguration commandConfiguration = foundCommand.getConfiguration();
|
||||||
List<Parameter> parameterList = commandConfiguration.getParameters();
|
List<Parameter> parameterList = commandConfiguration.getParameters();
|
||||||
// we iterate only over the actually found parameters, that way we dont have to consider the optional parameters
|
// 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()) {
|
long necessaryParameters = commandManager.getParameterCountForCommandConfig(commandConfiguration, serverId);
|
||||||
String nextParameterName = commandConfiguration.getParameters().get(commandConfiguration.getNecessaryParameterCount() - 1).getName();
|
if (necessaryParameters > parameters.getParameters().size()) {
|
||||||
|
String nextParameterName = commandConfiguration.getParameters().get((int)necessaryParameters - 1).getName();
|
||||||
throw new InsufficientParametersException(foundCommand, nextParameterName);
|
throw new InsufficientParametersException(foundCommand, nextParameterName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -438,6 +446,22 @@ public class CommandReceivedHandler extends ListenerAdapter {
|
|||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if(!param.getDependentFeatures().isEmpty()) {
|
||||||
|
List<FeatureDefinition> 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);
|
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
|
// TODO might be able to do this without iterating, if we directly associated the handler required for each parameter
|
||||||
for (CommandParameterHandler handler : usedParameterHandler) {
|
for (CommandParameterHandler handler : usedParameterHandler) {
|
||||||
|
|||||||
@@ -4,13 +4,17 @@ import dev.sheldan.abstracto.core.command.Command;
|
|||||||
import dev.sheldan.abstracto.core.command.CommandReceivedHandler;
|
import dev.sheldan.abstracto.core.command.CommandReceivedHandler;
|
||||||
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
|
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
|
||||||
import dev.sheldan.abstracto.core.command.config.ModuleDefinition;
|
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.CommandNotFoundException;
|
||||||
import dev.sheldan.abstracto.core.command.exception.InsufficientParametersException;
|
import dev.sheldan.abstracto.core.command.exception.InsufficientParametersException;
|
||||||
import dev.sheldan.abstracto.core.command.execution.UnParsedCommandParameter;
|
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.ACommandInAServer;
|
||||||
import dev.sheldan.abstracto.core.command.model.database.ACommandInServerAlias;
|
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.metric.service.MetricService;
|
||||||
import dev.sheldan.abstracto.core.service.ConfigService;
|
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 dev.sheldan.abstracto.core.service.management.DefaultConfigManagementService;
|
||||||
import net.dv8tion.jda.api.entities.Message;
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -20,6 +24,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class CommandManager implements CommandRegistry {
|
public class CommandManager implements CommandRegistry {
|
||||||
@@ -40,9 +45,15 @@ public class CommandManager implements CommandRegistry {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private CommandInServerAliasService commandInServerAliasService;
|
private CommandInServerAliasService commandInServerAliasService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FeatureFlagService featureFlagService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FeatureConfigService featureConfigService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Command> findCommandByParameters(String name, UnParsedCommandParameter unParsedCommandParameter, Long serverId) {
|
public Optional<Command> findCommandByParameters(String name, UnParsedCommandParameter unParsedCommandParameter, Long serverId) {
|
||||||
Optional<Command> commandOptional = commands.stream().filter(getCommandByNameAndParameterPredicate(name, unParsedCommandParameter)).findFirst();
|
Optional<Command> commandOptional = commands.stream().filter(getCommandByNameAndParameterPredicate(name, unParsedCommandParameter, serverId)).findFirst();
|
||||||
if(!commandOptional.isPresent()) {
|
if(!commandOptional.isPresent()) {
|
||||||
commandOptional = getCommandViaAliasAndParameter(name, unParsedCommandParameter, serverId);
|
commandOptional = getCommandViaAliasAndParameter(name, unParsedCommandParameter, serverId);
|
||||||
}
|
}
|
||||||
@@ -55,7 +66,7 @@ public class CommandManager implements CommandRegistry {
|
|||||||
// if its present, retrieve the original command
|
// if its present, retrieve the original command
|
||||||
ACommandInAServer command = aliasOptional.get().getCommandInAServer();
|
ACommandInAServer command = aliasOptional.get().getCommandInAServer();
|
||||||
// and find the command based on the newly found name
|
// 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();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
@@ -71,7 +82,7 @@ public class CommandManager implements CommandRegistry {
|
|||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Predicate<Command> getCommandByNameAndParameterPredicate(String name, UnParsedCommandParameter unParsedCommandParameter) {
|
private Predicate<Command> getCommandByNameAndParameterPredicate(String name, UnParsedCommandParameter unParsedCommandParameter, Long serverId) {
|
||||||
return (Command commandObj) -> {
|
return (Command commandObj) -> {
|
||||||
CommandConfiguration commandConfiguration = commandObj.getConfiguration();
|
CommandConfiguration commandConfiguration = commandObj.getConfiguration();
|
||||||
if (commandConfiguration == null) {
|
if (commandConfiguration == null) {
|
||||||
@@ -80,7 +91,7 @@ public class CommandManager implements CommandRegistry {
|
|||||||
if (!commandNameOrAliasMatches(name, commandConfiguration)) {
|
if (!commandNameOrAliasMatches(name, commandConfiguration)) {
|
||||||
return false;
|
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) {
|
private boolean verifyCommandConfiguration(UnParsedCommandParameter unParsedCommandParameter, Command command, CommandConfiguration commandConfiguration, Long serverId) {
|
||||||
if(commandConfiguration.getParameters() != null && commandConfiguration.getNecessaryParameterCount() > unParsedCommandParameter.getParameters().size()){
|
if(commandConfiguration.getParameters() != null) {
|
||||||
String nextParameterName = commandConfiguration.getParameters().get(commandConfiguration.getNecessaryParameterCount() - 1).getName();
|
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);
|
metricService.incrementCounter(CommandReceivedHandler.COMMANDS_WRONG_PARAMETER_COUNTER);
|
||||||
throw new InsufficientParametersException(command, nextParameterName);
|
throw new InsufficientParametersException(command, nextParameterName);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return true;
|
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<FeatureDefinition> 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) {
|
private boolean commandNameOrAliasMatches(String name, CommandConfiguration commandConfiguration) {
|
||||||
return commandConfiguration.getName().equalsIgnoreCase(name) ||
|
return commandConfiguration.getName().equalsIgnoreCase(name) ||
|
||||||
commandConfiguration.getAliases() != null && commandConfiguration.getAliases().stream().anyMatch(s -> s.equalsIgnoreCase(name));
|
commandConfiguration.getAliases() != null && commandConfiguration.getAliases().stream().anyMatch(s -> s.equalsIgnoreCase(name));
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ public class CommandLoaderListener implements AsyncStartupListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.info("Updating slash command {} in guild {}.", command.getConfiguration().getName(), guild.getId());
|
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());
|
log.info("Updating context commands for guild {}.", guild.getIdLong());
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ public class ComponentPayloadServiceBean implements ComponentPayloadService {
|
|||||||
return componentPayloadManagementService.createPayload(componentId, json, payload.getClass(), origin, server, ComponentType.BUTTON);
|
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
|
@Override
|
||||||
public ComponentPayload createSelectionPayload(String componentId, ButtonPayload payload, String origin, AServer server) {
|
public ComponentPayload createSelectionPayload(String componentId, ButtonPayload payload, String origin, AServer server) {
|
||||||
String json = gson.toJson(payload);
|
String json = gson.toJson(payload);
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
package dev.sheldan.abstracto.core.interaction.slash;
|
package dev.sheldan.abstracto.core.interaction.slash;
|
||||||
|
|
||||||
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
|
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.ACommand;
|
||||||
import dev.sheldan.abstracto.core.command.model.database.ACommandInAServer;
|
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.CommandInServerManagementService;
|
||||||
import dev.sheldan.abstracto.core.command.service.management.CommandManagementService;
|
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.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.templating.service.TemplateService;
|
||||||
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
|
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
@@ -42,8 +46,14 @@ public class SlashCommandServiceBean implements SlashCommandService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private SlashCommandServiceBean self;
|
private SlashCommandServiceBean self;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FeatureConfigService featureConfigService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FeatureFlagService featureFlagService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void convertCommandConfigToCommandData(CommandConfiguration commandConfiguration, List<Pair<List<CommandConfiguration>, SlashCommandData>> existingCommands) {
|
public void convertCommandConfigToCommandData(CommandConfiguration commandConfiguration, List<Pair<List<CommandConfiguration>, SlashCommandData>> existingCommands, Long serverId) {
|
||||||
boolean isTemplated = commandConfiguration.isTemplated();
|
boolean isTemplated = commandConfiguration.isTemplated();
|
||||||
SlashCommandConfig slashConfig = commandConfiguration.getSlashCommandConfig();
|
SlashCommandConfig slashConfig = commandConfiguration.getSlashCommandConfig();
|
||||||
String description;
|
String description;
|
||||||
@@ -76,10 +86,10 @@ public class SlashCommandServiceBean implements SlashCommandService {
|
|||||||
groupData.addSubcommands(slashCommand);
|
groupData.addSubcommands(slashCommand);
|
||||||
rootCommand.addSubcommandGroups(groupData);
|
rootCommand.addSubcommandGroups(groupData);
|
||||||
}
|
}
|
||||||
List<OptionData> requiredParameters = getParameters(commandConfiguration, isTemplated, internalCommandName);
|
List<OptionData> requiredParameters = getParameters(commandConfiguration, isTemplated, internalCommandName, serverId);
|
||||||
slashCommand.addOptions(requiredParameters);
|
slashCommand.addOptions(requiredParameters);
|
||||||
} else {
|
} else {
|
||||||
List<OptionData> requiredParameters = getParameters(commandConfiguration, isTemplated, internalCommandName);
|
List<OptionData> requiredParameters = getParameters(commandConfiguration, isTemplated, internalCommandName, serverId);
|
||||||
rootCommand.addOptions(requiredParameters);
|
rootCommand.addOptions(requiredParameters);
|
||||||
}
|
}
|
||||||
if(!existingRootCommand.isPresent()) {
|
if(!existingRootCommand.isPresent()) {
|
||||||
@@ -95,10 +105,18 @@ public class SlashCommandServiceBean implements SlashCommandService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<OptionData> getParameters(CommandConfiguration commandConfiguration, boolean isTemplated, String internalCommandName) {
|
@Override
|
||||||
|
public void convertCommandConfigToCommandData(CommandConfiguration commandConfiguration, List<Pair<List<CommandConfiguration>, SlashCommandData>> existingCommands) {
|
||||||
|
convertCommandConfigToCommandData(commandConfiguration, existingCommands, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<OptionData> getParameters(CommandConfiguration commandConfiguration, boolean isTemplated, String internalCommandName, Long serverId) {
|
||||||
List<OptionData> requiredParameters = new ArrayList<>();
|
List<OptionData> requiredParameters = new ArrayList<>();
|
||||||
List<OptionData> optionalParameters = new ArrayList<>();
|
List<OptionData> optionalParameters = new ArrayList<>();
|
||||||
commandConfiguration.getParameters().forEach(parameter -> {
|
commandConfiguration.getParameters().forEach(parameter -> {
|
||||||
|
if(!shouldParameterBeCreated(parameter, serverId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
List<OptionType> types = slashCommandParameterService.getTypesFromParameter(parameter.getType());
|
List<OptionType> types = slashCommandParameterService.getTypesFromParameter(parameter.getType());
|
||||||
if(types.size() > 1) {
|
if(types.size() > 1) {
|
||||||
if(parameter.isListParam()) {
|
if(parameter.isListParam()) {
|
||||||
@@ -132,6 +150,25 @@ public class SlashCommandServiceBean implements SlashCommandService {
|
|||||||
return requiredParameters;
|
return requiredParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean shouldParameterBeCreated(Parameter parameter, Long serverId) {
|
||||||
|
if(parameter.getDependentFeatures().isEmpty()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
List<FeatureDefinition> 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
|
@Override
|
||||||
public CompletableFuture<List<Command>> updateGuildSlashCommand(Guild guild, List<Pair<List<CommandConfiguration>, SlashCommandData>> commandData) {
|
public CompletableFuture<List<Command>> updateGuildSlashCommand(Guild guild, List<Pair<List<CommandConfiguration>, SlashCommandData>> commandData) {
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public class SlashCommandFeatureActivationListener implements FeatureActivationL
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.info("Updating slash command {} in guild {}.", command.getConfiguration().getName(), guild.getId());
|
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)
|
slashCommandService.addGuildSlashCommands(guild, commandsToUpDate)
|
||||||
.thenAccept(commands1 -> log.info("Updating {} slash commands in guild {}.", commandsToUpDate.size(), guild.getIdLong()));
|
.thenAccept(commands1 -> log.info("Updating {} slash commands in guild {}.", commandsToUpDate.size(), guild.getIdLong()));
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ public class ComponentPayloadManagementServiceBean implements ComponentPayloadMa
|
|||||||
return repository.save(componentPayload);
|
return repository.save(componentPayload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updatePayload(String id, String payload) {
|
||||||
|
findPayload(id).ifPresent(componentPayload -> componentPayload.setPayload(payload));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ComponentPayload createButtonPayload(ButtonConfigModel buttonConfigModel, AServer server) {
|
public ComponentPayload createButtonPayload(ButtonConfigModel buttonConfigModel, AServer server) {
|
||||||
String payload = gson.toJson(buttonConfigModel.getButtonPayload());
|
String payload = gson.toJson(buttonConfigModel.getButtonPayload());
|
||||||
|
|||||||
@@ -100,7 +100,6 @@ public class CommandManagerTest {
|
|||||||
commands.add(firstCommand);
|
commands.add(firstCommand);
|
||||||
when(commandConfiguration.getName()).thenReturn(COMMAND_NAME);
|
when(commandConfiguration.getName()).thenReturn(COMMAND_NAME);
|
||||||
when(firstCommand.getConfiguration()).thenReturn(commandConfiguration);
|
when(firstCommand.getConfiguration()).thenReturn(commandConfiguration);
|
||||||
when(commandConfiguration.getNecessaryParameterCount()).thenReturn(0);
|
|
||||||
when(parsedCommandParameter.getParameters()).thenReturn(new ArrayList<>());
|
when(parsedCommandParameter.getParameters()).thenReturn(new ArrayList<>());
|
||||||
Optional<Command> foundCommand = testUnit.findCommandByParameters(COMMAND_NAME, parsedCommandParameter, SERVER_ID);
|
Optional<Command> foundCommand = testUnit.findCommandByParameters(COMMAND_NAME, parsedCommandParameter, SERVER_ID);
|
||||||
Assert.assertEquals(firstCommand, foundCommand.get());
|
Assert.assertEquals(firstCommand, foundCommand.get());
|
||||||
@@ -132,7 +131,6 @@ public class CommandManagerTest {
|
|||||||
when(commandConfiguration.getName()).thenReturn(COMMAND_NAME_2);
|
when(commandConfiguration.getName()).thenReturn(COMMAND_NAME_2);
|
||||||
when(commandConfiguration.getAliases()).thenReturn(Arrays.asList(COMMAND_NAME));
|
when(commandConfiguration.getAliases()).thenReturn(Arrays.asList(COMMAND_NAME));
|
||||||
when(firstCommand.getConfiguration()).thenReturn(commandConfiguration);
|
when(firstCommand.getConfiguration()).thenReturn(commandConfiguration);
|
||||||
when(commandConfiguration.getNecessaryParameterCount()).thenReturn(0);
|
|
||||||
when(parsedCommandParameter.getParameters()).thenReturn(new ArrayList<>());
|
when(parsedCommandParameter.getParameters()).thenReturn(new ArrayList<>());
|
||||||
Optional<Command> foundCommand = testUnit.findCommandByParameters(COMMAND_NAME, parsedCommandParameter, SERVER_ID);
|
Optional<Command> foundCommand = testUnit.findCommandByParameters(COMMAND_NAME, parsedCommandParameter, SERVER_ID);
|
||||||
Assert.assertEquals(firstCommand, foundCommand.get());
|
Assert.assertEquals(firstCommand, foundCommand.get());
|
||||||
@@ -144,7 +142,6 @@ public class CommandManagerTest {
|
|||||||
when(commandConfiguration.getParameters()).thenReturn(Arrays.asList(parameter));
|
when(commandConfiguration.getParameters()).thenReturn(Arrays.asList(parameter));
|
||||||
when(firstCommand.getConfiguration()).thenReturn(commandConfiguration);
|
when(firstCommand.getConfiguration()).thenReturn(commandConfiguration);
|
||||||
when(commandConfiguration.getName()).thenReturn(COMMAND_NAME);
|
when(commandConfiguration.getName()).thenReturn(COMMAND_NAME);
|
||||||
when(commandConfiguration.getNecessaryParameterCount()).thenReturn(1);
|
|
||||||
when(parsedCommandParameter.getParameters()).thenReturn(new ArrayList<>());
|
when(parsedCommandParameter.getParameters()).thenReturn(new ArrayList<>());
|
||||||
testUnit.findCommandByParameters(COMMAND_NAME, parsedCommandParameter, SERVER_ID);
|
testUnit.findCommandByParameters(COMMAND_NAME, parsedCommandParameter, SERVER_ID);
|
||||||
}
|
}
|
||||||
@@ -154,7 +151,6 @@ public class CommandManagerTest {
|
|||||||
commands.add(firstCommand);
|
commands.add(firstCommand);
|
||||||
when(commandConfiguration.getName()).thenReturn(COMMAND_NAME);
|
when(commandConfiguration.getName()).thenReturn(COMMAND_NAME);
|
||||||
when(firstCommand.getConfiguration()).thenReturn(commandConfiguration);
|
when(firstCommand.getConfiguration()).thenReturn(commandConfiguration);
|
||||||
when(commandConfiguration.getNecessaryParameterCount()).thenReturn(0);
|
|
||||||
when(parsedCommandParameter.getParameters()).thenReturn(new ArrayList<>());
|
when(parsedCommandParameter.getParameters()).thenReturn(new ArrayList<>());
|
||||||
setupAliasTest();
|
setupAliasTest();
|
||||||
Optional<Command> foundCommand = testUnit.findCommandByParameters(ALIAS_NAME, parsedCommandParameter, SERVER_ID);
|
Optional<Command> foundCommand = testUnit.findCommandByParameters(ALIAS_NAME, parsedCommandParameter, SERVER_ID);
|
||||||
|
|||||||
@@ -50,7 +50,4 @@ public class CommandConfiguration {
|
|||||||
.enabled(false)
|
.enabled(false)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public int getNecessaryParameterCount(){
|
|
||||||
return (int) parameters.stream().filter(parameter -> !parameter.isOptional()).count();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ public class Parameter implements Serializable {
|
|||||||
private List<ParameterValidator> validators = new ArrayList<>();
|
private List<ParameterValidator> validators = new ArrayList<>();
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
private Map<String, Object> additionalInfo = new HashMap<>();
|
private Map<String, Object> additionalInfo = new HashMap<>();
|
||||||
|
// these are the features which potentially require this parameter
|
||||||
|
@Builder.Default
|
||||||
|
private List<String> dependentFeatures = new ArrayList<>();
|
||||||
|
|
||||||
public String getSlashCompatibleName() {
|
public String getSlashCompatibleName() {
|
||||||
return name.toLowerCase(Locale.ROOT);
|
return name.toLowerCase(Locale.ROOT);
|
||||||
|
|||||||
@@ -38,6 +38,13 @@ public class MaxIntegerValueValidator implements ParameterValidator {
|
|||||||
return Arrays.asList(param);
|
return Arrays.asList(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MaxIntegerValueValidator max(Long number) {
|
||||||
|
return MaxIntegerValueValidator
|
||||||
|
.builder()
|
||||||
|
.maxValue(number)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getExceptionTemplateName() {
|
public String getExceptionTemplateName() {
|
||||||
return "command_parameter_validation_value_too_large";
|
return "command_parameter_validation_value_too_large";
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.core.command.service;
|
|||||||
|
|
||||||
|
|
||||||
import dev.sheldan.abstracto.core.command.Command;
|
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.config.ModuleDefinition;
|
||||||
import dev.sheldan.abstracto.core.command.execution.UnParsedCommandParameter;
|
import dev.sheldan.abstracto.core.command.execution.UnParsedCommandParameter;
|
||||||
import net.dv8tion.jda.api.entities.Message;
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
@@ -19,4 +20,5 @@ public interface CommandRegistry {
|
|||||||
Command getCommandByName(String name, boolean searchAliases, Long serverId);
|
Command getCommandByName(String name, boolean searchAliases, Long serverId);
|
||||||
Optional<Command> getCommandByNameOptional(String name, boolean searchAliases, Long serverId);
|
Optional<Command> getCommandByNameOptional(String name, boolean searchAliases, Long serverId);
|
||||||
String getCommandName(String input, Long serverId);
|
String getCommandName(String input, Long serverId);
|
||||||
|
long getParameterCountForCommandConfig(CommandConfiguration commandConfiguration, Long serverId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import java.util.Optional;
|
|||||||
|
|
||||||
public interface ComponentPayloadManagementService {
|
public interface ComponentPayloadManagementService {
|
||||||
ComponentPayload createPayload(String id, String payload, Class payloadType, String buttonOrigin, AServer server, ComponentType type);
|
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, AServer server);
|
||||||
ComponentPayload createButtonPayload(ButtonConfigModel buttonConfigModel, Long serverId);
|
ComponentPayload createButtonPayload(ButtonConfigModel buttonConfigModel, Long serverId);
|
||||||
ComponentPayload createModalPayload(ModalConfigPayload payloadConfig, Long serverId);
|
ComponentPayload createModalPayload(ModalConfigPayload payloadConfig, Long serverId);
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ import dev.sheldan.abstracto.core.interaction.button.ButtonPayload;
|
|||||||
|
|
||||||
public interface ComponentPayloadService {
|
public interface ComponentPayloadService {
|
||||||
ComponentPayload createButtonPayload(String componentId, ButtonPayload payload, String origin, AServer server);
|
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);
|
ComponentPayload createSelectionPayload(String componentId, ButtonPayload payload, String origin, AServer server);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import java.util.List;
|
|||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public interface SlashCommandService {
|
public interface SlashCommandService {
|
||||||
|
void convertCommandConfigToCommandData(CommandConfiguration commandConfiguration, List<Pair<List<CommandConfiguration>, SlashCommandData>> existingCommands, Long serverId);
|
||||||
void convertCommandConfigToCommandData(CommandConfiguration commandConfiguration, List<Pair<List<CommandConfiguration>, SlashCommandData>> existingCommands);
|
void convertCommandConfigToCommandData(CommandConfiguration commandConfiguration, List<Pair<List<CommandConfiguration>, SlashCommandData>> existingCommands);
|
||||||
CompletableFuture<List<Command>> updateGuildSlashCommand(Guild guild, List<Pair<List<CommandConfiguration>, SlashCommandData>> commandData);
|
CompletableFuture<List<Command>> updateGuildSlashCommand(Guild guild, List<Pair<List<CommandConfiguration>, SlashCommandData>> commandData);
|
||||||
CompletableFuture<Void> deleteGuildSlashCommands(Guild guild, List<Long> slashCommandId, List<Long> commandInServerIdsToUnset);
|
CompletableFuture<Void> deleteGuildSlashCommands(Guild guild, List<Long> slashCommandId, List<Long> commandInServerIdsToUnset);
|
||||||
|
|||||||
Reference in New Issue
Block a user