mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-01-27 06:04:54 +00:00
[AB-53] adding economy related commands
adding runtime exception to root command handling adding simple custom command module disabling slash command for show emote the utility is not given (the bot cannot show any emotes from servers its not in) fixing post target setup not allowing threads fixing post targets not supporting threads fixing interactions not considering the actual amount of embeds fixing some cases for events which are not in a guild
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
package dev.sheldan.abstracto.entertainment.command.economy;
|
||||
|
||||
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.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
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.dto.CreditGambleResult;
|
||||
import dev.sheldan.abstracto.entertainment.model.command.CreditGambleResultModel;
|
||||
import dev.sheldan.abstracto.entertainment.service.EconomyService;
|
||||
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.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class CreditGamble extends AbstractConditionableCommand {
|
||||
private static final String CREDIT_GAMBLE_COMMAND = "creditGamble";
|
||||
private static final String CREDIT_GAMBLE_RESPONSE = "creditGamble_response";
|
||||
|
||||
@Autowired
|
||||
private EconomyService economyService;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private ChannelService channelService;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(commandContext.getAuthor());
|
||||
CreditGambleResult result = economyService.triggerCreditGamble(aUserInAServer);
|
||||
CreditGambleResultModel model = CreditGambleResultModel.fromCreditGambleResult(result);
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(CREDIT_GAMBLE_RESPONSE, model, commandContext.getGuild().getIdLong());
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
|
||||
.thenApply(interactionHook -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(event.getMember());
|
||||
CreditGambleResult result = economyService.triggerCreditGamble(aUserInAServer);
|
||||
CreditGambleResultModel model = CreditGambleResultModel.fromCreditGambleResult(result);
|
||||
return interactionService.replyEmbed(CREDIT_GAMBLE_RESPONSE, model, event)
|
||||
.thenApply(interactionHook -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(EntertainmentSlashCommandNames.ECONOMY)
|
||||
.commandName("creditgamble")
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name(CREDIT_GAMBLE_COMMAND)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.async(true)
|
||||
.module(EntertainmentModuleDefinition.ENTERTAINMENT)
|
||||
.templated(true)
|
||||
.supportsEmbedException(true)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return EntertainmentFeatureDefinition.ECONOMY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package dev.sheldan.abstracto.entertainment.command.economy;
|
||||
|
||||
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.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.MemberService;
|
||||
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
|
||||
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.model.command.CreditsLeaderboardEntry;
|
||||
import dev.sheldan.abstracto.entertainment.model.command.CreditsLeaderboardResponseModel;
|
||||
import dev.sheldan.abstracto.entertainment.service.EconomyService;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class CreditLeaderboard extends AbstractConditionableCommand {
|
||||
|
||||
private static final String CREDIT_LEADERBOARD_COMMAND_NAME = "creditLeaderboard";
|
||||
private static final String CREDIT_LEADERBOARD_RESPONSE = "creditLeaderboard_response";
|
||||
private static final String PAGE_PARAMETER = "page";
|
||||
|
||||
@Autowired
|
||||
private SlashCommandParameterService slashCommandParameterService;
|
||||
|
||||
@Autowired
|
||||
private EconomyService economyService;
|
||||
|
||||
@Autowired
|
||||
private ServerManagementService serverManagementService;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private ChannelService channelService;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private MemberService memberService;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
AServer server = serverManagementService.loadServer(commandContext.getGuild());
|
||||
List<Object> parameters = commandContext.getParameters().getParameters();
|
||||
// parameter is optional, in case its not present, we default to the 0th page
|
||||
Integer page = !parameters.isEmpty() ? (Integer) parameters.get(0) : 1;
|
||||
List<CreditsLeaderboardEntry> creditLeaderboard = economyService.getCreditLeaderboard(server, page);
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(commandContext.getAuthor());
|
||||
CreditsLeaderboardEntry ownRank = economyService.getRankOfUser(aUserInAServer);
|
||||
CreditsLeaderboardResponseModel model = CreditsLeaderboardResponseModel
|
||||
.builder()
|
||||
.entries(creditLeaderboard)
|
||||
.ownRank(ownRank)
|
||||
.build();
|
||||
return enrichModelWithMembers(model, commandContext.getGuild().getIdLong())
|
||||
.thenCompose(model1 -> {
|
||||
MessageToSend message = templateService.renderEmbedTemplate(CREDIT_LEADERBOARD_RESPONSE, model1, commandContext.getGuild().getIdLong());
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(message, commandContext.getChannel()));
|
||||
})
|
||||
.thenApply(unused -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
event.deferReply().queue();
|
||||
Integer page;
|
||||
if(slashCommandParameterService.hasCommandOption(PAGE_PARAMETER, event)) {
|
||||
page = slashCommandParameterService.getCommandOption(PAGE_PARAMETER, event, Integer.class);
|
||||
} else {
|
||||
page = 1;
|
||||
}
|
||||
AServer server = serverManagementService.loadServer(event.getGuild());
|
||||
List<CreditsLeaderboardEntry> creditLeaderboard = economyService.getCreditLeaderboard(server, page);
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(event.getMember());
|
||||
CreditsLeaderboardEntry ownRank = economyService.getRankOfUser(aUserInAServer);
|
||||
CreditsLeaderboardResponseModel model = CreditsLeaderboardResponseModel
|
||||
.builder()
|
||||
.entries(creditLeaderboard)
|
||||
.ownRank(ownRank)
|
||||
.build();
|
||||
return enrichModelWithMembers(model, event.getGuild().getIdLong())
|
||||
.thenCompose(model1 -> FutureUtils.toSingleFutureGeneric(interactionService.sendMessageToInteraction(CREDIT_LEADERBOARD_RESPONSE, model1, event.getHook())))
|
||||
.thenApply(unused -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
private CompletableFuture<CreditsLeaderboardResponseModel> enrichModelWithMembers(CreditsLeaderboardResponseModel model, Long serverId) {
|
||||
List<CompletableFuture<Member>> memberFutures = new ArrayList<>();
|
||||
model.getEntries().forEach(creditsLeaderboardEntry -> {
|
||||
memberFutures.add(memberService.getMemberInServerAsync(serverId, creditsLeaderboardEntry.getMemberDisplay().getUserId()));
|
||||
});
|
||||
memberFutures.add(memberService.getMemberInServerAsync(serverId, model.getOwnRank().getMemberDisplay().getUserId()));
|
||||
CompletableFuture<CreditsLeaderboardResponseModel> modelFuture = new CompletableFuture<>();
|
||||
CompletableFutureList<Member> futureList = new CompletableFutureList<>(memberFutures);
|
||||
futureList.getMainFuture().whenComplete((unused, throwable) -> {
|
||||
Map<Long, Member> memberMap = new HashMap<>();
|
||||
futureList.getObjects().forEach(member -> memberMap.put(member.getIdLong(), member));
|
||||
model.getEntries().forEach(creditsLeaderboardEntry -> {
|
||||
if(memberMap.containsKey(creditsLeaderboardEntry.getMemberDisplay().getUserId())) {
|
||||
creditsLeaderboardEntry.setMember(memberMap.get(creditsLeaderboardEntry.getMemberDisplay().getUserId()));
|
||||
}
|
||||
});
|
||||
if(memberMap.containsKey(model.getOwnRank().getMemberDisplay().getUserId())) {
|
||||
model.getOwnRank().setMember(memberMap.get(model.getOwnRank().getMemberDisplay().getUserId()));
|
||||
}
|
||||
modelFuture.complete(model);
|
||||
});
|
||||
return modelFuture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
Parameter pageParameter = Parameter
|
||||
.builder()
|
||||
.name(PAGE_PARAMETER)
|
||||
.optional(true)
|
||||
.templated(true)
|
||||
.type(Integer.class)
|
||||
.build();
|
||||
List<Parameter> parameters = Arrays.asList(pageParameter);
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(EntertainmentSlashCommandNames.ECONOMY)
|
||||
.commandName("creditboard")
|
||||
.build();
|
||||
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name(CREDIT_LEADERBOARD_COMMAND_NAME)
|
||||
.module(EntertainmentModuleDefinition.ENTERTAINMENT)
|
||||
.templated(true)
|
||||
.async(true)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.supportsEmbedException(true)
|
||||
.causesReaction(false)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return EntertainmentFeatureDefinition.ECONOMY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package dev.sheldan.abstracto.entertainment.command.economy;
|
||||
|
||||
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.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
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.model.command.CreditsLeaderboardEntry;
|
||||
import dev.sheldan.abstracto.entertainment.model.command.CreditsModel;
|
||||
import dev.sheldan.abstracto.entertainment.service.EconomyService;
|
||||
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.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class Credits extends AbstractConditionableCommand {
|
||||
|
||||
private static final String CREDITS_COMMAND = "credits";
|
||||
private static final String CREDITS_RESPONSE = "credits_response";
|
||||
|
||||
@Autowired
|
||||
private EconomyService economyService;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private ChannelService channelService;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
AUserInAServer targetUser = userInServerManagementService.loadOrCreateUser(commandContext.getAuthor());
|
||||
CreditsLeaderboardEntry rankEntry = economyService.getRankOfUser(targetUser);
|
||||
rankEntry.setMember(commandContext.getAuthor());
|
||||
CreditsModel model = CreditsModel
|
||||
.builder()
|
||||
.entry(rankEntry)
|
||||
.build();
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(CREDITS_RESPONSE, model, commandContext.getGuild().getIdLong());
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
|
||||
.thenApply(interactionHook -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
AUserInAServer targetUser = userInServerManagementService.loadOrCreateUser(event.getMember());
|
||||
CreditsLeaderboardEntry rankEntry = economyService.getRankOfUser(targetUser);
|
||||
rankEntry.setMember(event.getMember());
|
||||
CreditsModel model = CreditsModel
|
||||
.builder()
|
||||
.entry(rankEntry)
|
||||
.build();
|
||||
return interactionService.replyEmbed(CREDITS_RESPONSE, model, event)
|
||||
.thenApply(interactionHook -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(EntertainmentSlashCommandNames.ECONOMY)
|
||||
.commandName("credits")
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name(CREDITS_COMMAND)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.async(true)
|
||||
.module(EntertainmentModuleDefinition.ENTERTAINMENT)
|
||||
.templated(true)
|
||||
.supportsEmbedException(true)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return EntertainmentFeatureDefinition.ECONOMY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package dev.sheldan.abstracto.entertainment.command.economy;
|
||||
|
||||
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.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
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.dto.PayDayResult;
|
||||
import dev.sheldan.abstracto.entertainment.model.command.PayDayResponseModel;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.EconomyLeaderboardResult;
|
||||
import dev.sheldan.abstracto.entertainment.service.EconomyService;
|
||||
import dev.sheldan.abstracto.entertainment.service.management.EconomyUserManagementService;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
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.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class Payday extends AbstractConditionableCommand {
|
||||
|
||||
private static final String PAYDAY_COMMAND_NAME = "payday";
|
||||
|
||||
@Autowired
|
||||
private EconomyService economyService;
|
||||
|
||||
@Autowired
|
||||
private EconomyUserManagementService economyUserManagementService;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private ChannelService channelService;
|
||||
|
||||
private static final String PAYDAY_RESPONSE = "payday_response";
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
Member member = commandContext.getAuthor();
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
|
||||
PayDayResult payDayResult = economyService.triggerPayDay(aUserInAServer);
|
||||
EconomyLeaderboardResult rank = economyUserManagementService.getRankOfUserInServer(aUserInAServer);
|
||||
PayDayResponseModel responseModel = PayDayResponseModel
|
||||
.builder()
|
||||
.currentCredits(payDayResult.getCurrentCredits())
|
||||
.gainedCredits(payDayResult.getGainedCredits())
|
||||
.leaderboardPosition(rank.getRank().longValue())
|
||||
.build();
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(PAYDAY_RESPONSE, responseModel, member.getGuild().getIdLong());
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
|
||||
.thenApply(unused -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
Member member = event.getMember();
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
|
||||
PayDayResult payDayResult = economyService.triggerPayDay(aUserInAServer);
|
||||
EconomyLeaderboardResult rank = economyUserManagementService.getRankOfUserInServer(aUserInAServer);
|
||||
PayDayResponseModel responseModel = PayDayResponseModel
|
||||
.builder()
|
||||
.currentCredits(payDayResult.getCurrentCredits())
|
||||
.gainedCredits(payDayResult.getGainedCredits())
|
||||
.leaderboardPosition(rank.getRank().longValue())
|
||||
.build();
|
||||
return interactionService.replyEmbed(PAYDAY_RESPONSE, responseModel, event)
|
||||
.thenApply(interactionHook -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(EntertainmentSlashCommandNames.ECONOMY)
|
||||
.commandName("payday")
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name(PAYDAY_COMMAND_NAME)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.async(true)
|
||||
.module(EntertainmentModuleDefinition.ENTERTAINMENT)
|
||||
.templated(true)
|
||||
.supportsEmbedException(true)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return EntertainmentFeatureDefinition.ECONOMY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package dev.sheldan.abstracto.entertainment.command.economy;
|
||||
|
||||
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.config.validator.MinIntegerValueValidator;
|
||||
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.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
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.dto.SlotsResult;
|
||||
import dev.sheldan.abstracto.entertainment.model.command.SlotsResponseModel;
|
||||
import dev.sheldan.abstracto.entertainment.service.EconomyServiceBean;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
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.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class Slots extends AbstractConditionableCommand {
|
||||
|
||||
private static final String SLOTS_COMMAND_NAME = "slots";
|
||||
private static final String BID_PARAMETER = "bid";
|
||||
private static final String SLOTS_RESPONSE = "slots_response";
|
||||
|
||||
@Autowired
|
||||
private SlashCommandParameterService slashCommandParameterService;
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private EconomyServiceBean economyUserServiceBean;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private ChannelService channelService;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
Integer bid = (Integer) commandContext.getParameters().getParameters().get(0);
|
||||
Member member = commandContext.getAuthor();
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
|
||||
SlotsResult slotsResult = economyUserServiceBean.triggerSlots(aUserInAServer, bid.longValue());
|
||||
SlotsResponseModel responseModel = SlotsResponseModel.fromSlotsResult(slotsResult);
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(SLOTS_RESPONSE, responseModel, member.getGuild().getIdLong());
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
|
||||
.thenApply(unused -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
Long bid = slashCommandParameterService.getCommandOption(BID_PARAMETER, event, Integer.class).longValue();
|
||||
Member member = event.getMember();
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
|
||||
SlotsResult slotsResult = economyUserServiceBean.triggerSlots(aUserInAServer, bid);
|
||||
SlotsResponseModel responseModel = SlotsResponseModel.fromSlotsResult(slotsResult);
|
||||
return interactionService.replyEmbed(SLOTS_RESPONSE, responseModel, event)
|
||||
.thenApply(interactionHook -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
Parameter lowParameter = Parameter
|
||||
.builder()
|
||||
.name(BID_PARAMETER)
|
||||
.type(Integer.class)
|
||||
.templated(true)
|
||||
.validators(Arrays.asList(MinIntegerValueValidator.min(0L)))
|
||||
.build();
|
||||
parameters.add(lowParameter);
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(EntertainmentSlashCommandNames.ECONOMY)
|
||||
.commandName("slots")
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name(SLOTS_COMMAND_NAME)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.async(true)
|
||||
.module(EntertainmentModuleDefinition.ENTERTAINMENT)
|
||||
.templated(true)
|
||||
.supportsEmbedException(true)
|
||||
.parameters(parameters)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return EntertainmentFeatureDefinition.ECONOMY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package dev.sheldan.abstracto.entertainment.command.economy;
|
||||
|
||||
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.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
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.service.EconomyService;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
public class TransferCredits extends AbstractConditionableCommand {
|
||||
private static final String TRANSFER_CREDITS_COMMAND = "transferCredits";
|
||||
private static final String TRANSFER_CREDITS_RESPONSE = "transferCredits_response";
|
||||
private static final String MEMBER_PARAMETER = "targetMember";
|
||||
private static final String AMOUNT_PARAMETER = "amount";
|
||||
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private SlashCommandParameterService slashCommandParameterService;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Autowired
|
||||
private EconomyService economyService;
|
||||
|
||||
@Override
|
||||
public CommandResult execute(CommandContext commandContext) {
|
||||
List<Object> parameters = commandContext.getParameters().getParameters();
|
||||
Member targetMember = (Member) parameters.get(0);
|
||||
Integer amount = (Integer) parameters.get(1);
|
||||
AUserInAServer targetUser = userInServerManagementService.loadOrCreateUser(targetMember);
|
||||
AUserInAServer sourceUser = userInServerManagementService.loadOrCreateUser(commandContext.getAuthor());
|
||||
economyService.transferCredits(sourceUser, targetUser, amount.longValue());
|
||||
return CommandResult.fromSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
Member targetMember = slashCommandParameterService.getCommandOption(MEMBER_PARAMETER, event, Member.class);
|
||||
Integer amount = slashCommandParameterService.getCommandOption(AMOUNT_PARAMETER, event, Integer.class);
|
||||
AUserInAServer targetUser = userInServerManagementService.loadOrCreateUser(targetMember);
|
||||
AUserInAServer sourceUser = userInServerManagementService.loadOrCreateUser(event.getMember());
|
||||
economyService.transferCredits(sourceUser, targetUser, amount.longValue());
|
||||
return interactionService.replyEmbed(TRANSFER_CREDITS_RESPONSE, event)
|
||||
.thenApply(interactionHook -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
HelpInfo helpInfo = HelpInfo
|
||||
.builder()
|
||||
.templated(true)
|
||||
.build();
|
||||
|
||||
Parameter memberParameter = Parameter
|
||||
.builder()
|
||||
.name(MEMBER_PARAMETER)
|
||||
.templated(true)
|
||||
.type(Member.class)
|
||||
.optional(true)
|
||||
.build();
|
||||
|
||||
Parameter amountParameter = Parameter
|
||||
.builder()
|
||||
.name(AMOUNT_PARAMETER)
|
||||
.templated(true)
|
||||
.type(Integer.class)
|
||||
.optional(true)
|
||||
.build();
|
||||
|
||||
List<Parameter> parameters = Arrays.asList(memberParameter, amountParameter);
|
||||
|
||||
SlashCommandConfig slashCommandConfig = SlashCommandConfig
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(EntertainmentSlashCommandNames.ECONOMY)
|
||||
.commandName("transfer")
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name(TRANSFER_CREDITS_COMMAND)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
.module(EntertainmentModuleDefinition.ENTERTAINMENT)
|
||||
.templated(true)
|
||||
.supportsEmbedException(true)
|
||||
.parameters(parameters)
|
||||
.causesReaction(true)
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return EntertainmentFeatureDefinition.ECONOMY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package dev.sheldan.abstracto.entertainment.repository;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.EconomyLeaderboardResult;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.EconomyUser;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface EconomyUserRepository extends JpaRepository<EconomyUser, Long> {
|
||||
Optional<EconomyUser> findByUser(AUserInAServer aUserInAServer);
|
||||
|
||||
@Query(value = "WITH economy_user_ranked AS" +
|
||||
"( " +
|
||||
" SELECT eu.id, eu.credits, uis.user_id, ROW_NUMBER() OVER ( ORDER BY credits DESC ) " +
|
||||
" FROM economy_user eu INNER JOIN user_in_server uis ON eu.id = uis.user_in_server_id INNER JOIN server s ON s.id = uis.server_id WHERE s.id = :serverId" +
|
||||
") " +
|
||||
"SELECT rank.id as \"id\", rank.user_id as \"userid\", rank.credits as \"credits\", rank.row_number as \"rank\" " +
|
||||
"FROM economy_user_ranked rank " +
|
||||
"WHERE rank.id = :userInServerId", nativeQuery = true)
|
||||
EconomyLeaderboardResult getRankOfUserInServer(@Param("userInServerId") Long id, @Param("serverId") Long serverId);
|
||||
|
||||
@Query(value = "WITH economy_user_ranked AS" +
|
||||
"( " +
|
||||
" SELECT eu.id, eu.credits, uis.user_id, ROW_NUMBER() OVER ( ORDER BY credits DESC ) " +
|
||||
" FROM economy_user eu INNER JOIN user_in_server uis ON eu.id = uis.user_in_server_id INNER JOIN server s ON s.id = uis.server_id WHERE s.id = :serverId" +
|
||||
") " +
|
||||
"SELECT rank.id as \"id\", rank.user_id as \"userid\", rank.credits as \"credits\", rank.row_number as \"rank\" " +
|
||||
"FROM economy_user_ranked rank ", nativeQuery = true)
|
||||
List<EconomyLeaderboardResult> getRanksInServer(@Param("serverId") Long serverId);
|
||||
List<EconomyUser> findTop10ByServerOrderByCreditsDesc(AServer server, Pageable pageable);
|
||||
|
||||
List<EconomyUser> findByServerOrderByCredits(AServer server);
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
package dev.sheldan.abstracto.entertainment.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
|
||||
import dev.sheldan.abstracto.core.service.ConfigService;
|
||||
import dev.sheldan.abstracto.entertainment.config.EconomyFeatureConfig;
|
||||
import dev.sheldan.abstracto.entertainment.dto.CreditGambleResult;
|
||||
import dev.sheldan.abstracto.entertainment.dto.PayDayResult;
|
||||
import dev.sheldan.abstracto.entertainment.dto.SlotsResult;
|
||||
import dev.sheldan.abstracto.entertainment.exception.NotEnoughCreditsException;
|
||||
import dev.sheldan.abstracto.entertainment.exception.PayDayCooldownException;
|
||||
import dev.sheldan.abstracto.entertainment.exception.SlotsCooldownException;
|
||||
import dev.sheldan.abstracto.entertainment.model.command.CreditsLeaderboardEntry;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.EconomyLeaderboardResult;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.EconomyUser;
|
||||
import dev.sheldan.abstracto.entertainment.service.management.EconomyUserManagementService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static dev.sheldan.abstracto.entertainment.config.EconomyFeatureConfig.PAYDAY_COOLDOWN_CONFIG_KEY;
|
||||
import static dev.sheldan.abstracto.entertainment.config.EconomyFeatureConfig.SLOTS_COOLDOWN_CONFIG_KEY;
|
||||
|
||||
@Component
|
||||
public class EconomyServiceBean implements EconomyService {
|
||||
|
||||
@Autowired
|
||||
private ConfigService configService;
|
||||
|
||||
@Autowired
|
||||
private EconomyUserManagementService economyUserManagementService;
|
||||
|
||||
@Autowired
|
||||
private SecureRandom secureRandom;
|
||||
|
||||
private static final String CREDIT_GAMBLE_STORAGE = "creditGambleJackpot";
|
||||
|
||||
private static final String SNOWFLAKE = "❄";
|
||||
private static final String CHERRY = "🍒";
|
||||
private static final String COOKIE = "🍪";
|
||||
private static final String TWO = "2️⃣";
|
||||
private static final String CLOVER = "🍀";
|
||||
private static final String MUSHROOM = "🍄";
|
||||
private static final String SUNFLOWER = "🌻";
|
||||
private static final String HEART = "❤";
|
||||
private static final String SIX = "6️⃣";
|
||||
private static final String CYCLONE = "🌀";
|
||||
private static final String OUTCOME_KEY_THREE_CHERRIES = "threecherries";
|
||||
private static final String OUTCOME_KEY_NOTHING = "nothing";
|
||||
private static final String OUTCOME_KEY_JACKPOT = "jackpot";
|
||||
private static final String OUTCOME_KEY_CLOVERS = "clovers";
|
||||
private static final String OUTCOME_KEY_TWO_CHERRIES = "twocherries";
|
||||
private static final String OUTCOME_KEY_TWOSIX = "twosix";
|
||||
private static final String OUTCOME_KEY_3SYMBOLS = "threesymbols";
|
||||
private static final String OUTCOME_KEY_2SYMBOLS = "twosymbols";
|
||||
private static final List<String> POSSIBLE_SLOTS = Arrays.asList(SNOWFLAKE, CHERRY, COOKIE, TWO, CLOVER, MUSHROOM, SUNFLOWER, HEART, SIX, CYCLONE);
|
||||
private static final List<SlotMapping> WINNING_PATTERNS = Arrays.asList(
|
||||
new SlotMapping(Arrays.asList(CHERRY, CHERRY, CHERRY), 20, OUTCOME_KEY_THREE_CHERRIES),
|
||||
new SlotMapping(Arrays.asList(CLOVER, CLOVER, CLOVER), 25, OUTCOME_KEY_CLOVERS),
|
||||
new SlotMapping(Arrays.asList(TWO, TWO, SIX), 50, OUTCOME_KEY_JACKPOT),
|
||||
new SlotMapping(Arrays.asList(TWO, SIX), 4, OUTCOME_KEY_TWOSIX),
|
||||
new SlotMapping(Arrays.asList(CHERRY, CHERRY), 3, OUTCOME_KEY_TWO_CHERRIES)
|
||||
);
|
||||
private static final Integer TRIPLE_FACTOR = 10;
|
||||
private static final Integer DOUBLE_FACTOR = 2;
|
||||
|
||||
@Override
|
||||
public boolean canTriggerPayDay(AUserInAServer aUserInAServer) {
|
||||
Optional<EconomyUser> userOptional = economyUserManagementService.getUser(aUserInAServer);
|
||||
return userOptional.map(this::canTriggerPayDay).orElse(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTriggerSlots(AUserInAServer aUserInAServer) {
|
||||
Optional<EconomyUser> userOptional = economyUserManagementService.getUser(aUserInAServer);
|
||||
return userOptional.map(this::canTriggerSlots).orElse(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTriggerPayDay(EconomyUser economyUser) {
|
||||
return slotsTriggerIn(economyUser).isNegative();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTriggerSlots(EconomyUser economyUser) {
|
||||
return payDayTriggerIn(economyUser).isNegative();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration payDayTriggerIn(EconomyUser economyUser) {
|
||||
Long cooldownSeconds = configService.getLongValueOrConfigDefault(PAYDAY_COOLDOWN_CONFIG_KEY, economyUser.getServer().getId());
|
||||
Instant minTimeStamp = Instant.now().minus(cooldownSeconds, ChronoUnit.SECONDS);
|
||||
return Duration.between(economyUser.getLastPayDay(), minTimeStamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration slotsTriggerIn(EconomyUser economyUser) {
|
||||
Long cooldownSeconds = configService.getLongValueOrConfigDefault(SLOTS_COOLDOWN_CONFIG_KEY, economyUser.getServer().getId());
|
||||
Instant minTimeStamp = Instant.now().minus(cooldownSeconds, ChronoUnit.SECONDS);
|
||||
return Duration.between(economyUser.getLastSlots(), minTimeStamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EconomyUser addCredits(AUserInAServer aUserInAServer, Long credits) {
|
||||
Optional<EconomyUser> existingUserOptional = economyUserManagementService.getUser(aUserInAServer);
|
||||
if (existingUserOptional.isPresent()) {
|
||||
EconomyUser existingUser = existingUserOptional.get();
|
||||
addCredits(existingUser, credits);
|
||||
return existingUser;
|
||||
} else {
|
||||
EconomyUser user = economyUserManagementService.createUser(aUserInAServer);
|
||||
user.setCredits(credits);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCredits(EconomyUser economyUser, Long credits) {
|
||||
economyUser.setCredits(economyUser.getCredits() + credits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPayDayCredits(AUserInAServer aUserInAServer) {
|
||||
Long creditsToAdd = configService.getLongValueOrConfigDefault(EconomyFeatureConfig.PAYDAY_CREDITS_CONFIG_KEY,
|
||||
aUserInAServer.getServerReference().getId());
|
||||
addCredits(aUserInAServer, creditsToAdd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayDayResult triggerPayDay(AUserInAServer aUserInAServer) {
|
||||
Long creditsToAdd = configService.getLongValueOrConfigDefault(EconomyFeatureConfig.PAYDAY_CREDITS_CONFIG_KEY,
|
||||
aUserInAServer.getServerReference().getId());
|
||||
EconomyUser economyUser = addCredits(aUserInAServer, creditsToAdd);
|
||||
Duration durationForPayday = payDayTriggerIn(economyUser);
|
||||
if (durationForPayday.isNegative()) {
|
||||
throw new PayDayCooldownException(durationForPayday.abs());
|
||||
}
|
||||
economyUser.setLastPayDay(Instant.now());
|
||||
return PayDayResult
|
||||
.builder()
|
||||
.currentCredits(economyUser.getCredits())
|
||||
.gainedCredits(creditsToAdd)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SlotsResult triggerSlots(AUserInAServer aUserInAServer, Long bid) {
|
||||
Optional<EconomyUser> userOptional = economyUserManagementService.getUser(aUserInAServer);
|
||||
if(!userOptional.isPresent()) {
|
||||
throw new NotEnoughCreditsException();
|
||||
}
|
||||
EconomyUser user = userOptional.get();
|
||||
Long oldCredits = user.getCredits();
|
||||
if(user.getCredits() < bid) {
|
||||
throw new NotEnoughCreditsException();
|
||||
}
|
||||
Duration durationForSlots = slotsTriggerIn(user);
|
||||
if (durationForSlots.isNegative()) {
|
||||
throw new SlotsCooldownException(durationForSlots.abs());
|
||||
}
|
||||
SlotGame slotGame = playSlots();
|
||||
Integer factor = slotGame.getResultFactor();
|
||||
Long creditChange = bid * factor;
|
||||
addCredits(user, -bid);
|
||||
addCredits(user, creditChange);
|
||||
|
||||
user.setLastSlots(Instant.now());
|
||||
return SlotsResult
|
||||
.builder()
|
||||
.bid(bid)
|
||||
.factor(factor.longValue())
|
||||
.newCredits(user.getCredits())
|
||||
.outComeKey(slotGame.getOutcome())
|
||||
.oldCredits(oldCredits)
|
||||
.winnings(creditChange)
|
||||
.rows(slotGame.getRows())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SlotGame playSlots() {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
List<String> tempSlots = new ArrayList<>(POSSIBLE_SLOTS);
|
||||
Collections.rotate(tempSlots, secureRandom.nextInt(2000) - 1000);
|
||||
result.add(tempSlots.get(0));
|
||||
result.add(tempSlots.get(1));
|
||||
result.add(tempSlots.get(2));
|
||||
}
|
||||
List<List<String>> rows = new ArrayList<>();
|
||||
rows.add(Arrays.asList(result.get(0), result.get(3), result.get(6)));
|
||||
List<String> decidingRow = Arrays.asList(result.get(1), result.get(4), result.get(7));
|
||||
rows.add(decidingRow);
|
||||
rows.add(Arrays.asList(result.get(2), result.get(5), result.get(8)));
|
||||
String decidingRowAsString = String.join("", decidingRow);
|
||||
SlotMapping specialPattern = getSpecialPattern(decidingRowAsString);
|
||||
Integer factor = 0;
|
||||
String outcomeKey = OUTCOME_KEY_NOTHING;
|
||||
if(specialPattern != null){
|
||||
factor = specialPattern.factor;
|
||||
outcomeKey = specialPattern.outcome;
|
||||
} else {
|
||||
Set<String> uniqueChars = new HashSet<>(decidingRow);
|
||||
if(uniqueChars.size() == 1) {
|
||||
factor = TRIPLE_FACTOR;
|
||||
outcomeKey = OUTCOME_KEY_3SYMBOLS;
|
||||
} else if(decidingRow.get(0).equals(decidingRow.get(1)) || decidingRow.get(1).equals(decidingRow.get(2)) ) {
|
||||
factor = DOUBLE_FACTOR;
|
||||
outcomeKey = OUTCOME_KEY_2SYMBOLS;
|
||||
}
|
||||
}
|
||||
return SlotGame
|
||||
.builder()
|
||||
.rows(rows)
|
||||
.outcome(outcomeKey)
|
||||
.resultFactor(factor)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CreditsLeaderboardEntry> getCreditLeaderboard(AServer server, Integer page) {
|
||||
if(page <= 0) {
|
||||
throw new IllegalArgumentException("Page needs to be >= 1");
|
||||
}
|
||||
page--;
|
||||
int pageSize = 10;
|
||||
List<CreditsLeaderboardEntry> entries = new ArrayList<>();
|
||||
List<EconomyUser> ranks = economyUserManagementService.getRanksInServer(server, page, pageSize);
|
||||
int pageOffset = page * pageSize;
|
||||
for (int i = 0; i < ranks.size(); i++) {
|
||||
EconomyUser rank = ranks.get(i);
|
||||
CreditsLeaderboardEntry entry = CreditsLeaderboardEntry
|
||||
.builder()
|
||||
.credits(rank.getCredits())
|
||||
.memberDisplay(MemberDisplay.fromAUserInAServer(rank.getUser()))
|
||||
.rank(pageOffset + i + 1)
|
||||
.build();
|
||||
entries.add(entry);
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CreditsLeaderboardEntry getRankOfUser(AUserInAServer aUserInAServer) {
|
||||
EconomyLeaderboardResult rank = economyUserManagementService.getRankOfUserInServer(aUserInAServer);
|
||||
if(rank != null) {
|
||||
return CreditsLeaderboardEntry
|
||||
.builder()
|
||||
.credits(rank.getCredits())
|
||||
.memberDisplay(MemberDisplay.fromAUserInAServer(aUserInAServer))
|
||||
.rank(rank.getRank())
|
||||
.build();
|
||||
} else {
|
||||
return CreditsLeaderboardEntry
|
||||
.builder()
|
||||
.credits(0L)
|
||||
.memberDisplay(MemberDisplay.fromAUserInAServer(aUserInAServer))
|
||||
.rank(-1)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transferCredits(AUserInAServer source, AUserInAServer target, Long amount) {
|
||||
Optional<EconomyUser> userOptional = economyUserManagementService.getUser(source);
|
||||
if(!userOptional.isPresent()) {
|
||||
throw new NotEnoughCreditsException();
|
||||
}
|
||||
EconomyUser user = userOptional.get();
|
||||
if(user.getCredits() < amount) {
|
||||
throw new NotEnoughCreditsException();
|
||||
}
|
||||
addCredits(target, amount);
|
||||
addCredits(user, -amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CreditGambleResult triggerCreditGamble(AUserInAServer aUserInAServer) {
|
||||
// TODO move these constants to system configs
|
||||
Optional<EconomyUser> userOptional = economyUserManagementService.getUser(aUserInAServer);
|
||||
if(!userOptional.isPresent()) {
|
||||
throw new NotEnoughCreditsException();
|
||||
}
|
||||
EconomyUser user = userOptional.get();
|
||||
Long bid = 25L;
|
||||
if(user.getCredits() < bid) {
|
||||
throw new NotEnoughCreditsException();
|
||||
}
|
||||
Long serverId = aUserInAServer.getServerReference().getId();
|
||||
Long currentJackpot = configService.getLongValueOrConfigDefault(CREDIT_GAMBLE_STORAGE, serverId);
|
||||
List<Integer> diceRoles = new ArrayList<>();
|
||||
diceRoles.add(creditGambleDiceResult());
|
||||
diceRoles.add(creditGambleDiceResult());
|
||||
diceRoles.add(creditGambleDiceResult());
|
||||
diceRoles.add(creditGambleDiceResult());
|
||||
|
||||
Long toJackpot = 20L;
|
||||
Integer uniqueNumbers = new HashSet<>(diceRoles).size();
|
||||
Boolean won = uniqueNumbers == 1;
|
||||
CreditGambleResult result = CreditGambleResult
|
||||
.builder()
|
||||
.uniqueNumbers(uniqueNumbers)
|
||||
.won(won)
|
||||
.bid(bid)
|
||||
.toBank(bid - toJackpot)
|
||||
.toJackpot(toJackpot)
|
||||
.rolls(diceRoles)
|
||||
.currentJackpot(currentJackpot + toJackpot)
|
||||
.build();
|
||||
if(won) {
|
||||
addCredits(user, currentJackpot);
|
||||
currentJackpot = 1000L;
|
||||
} else {
|
||||
currentJackpot += toJackpot;
|
||||
addCredits(user, -bid);
|
||||
}
|
||||
configService.setOrCreateConfigValue(CREDIT_GAMBLE_STORAGE, serverId, currentJackpot.toString());
|
||||
return result;
|
||||
}
|
||||
|
||||
private Integer creditGambleDiceResult() {
|
||||
return secureRandom.nextInt(7) + 1;
|
||||
}
|
||||
|
||||
private SlotMapping getSpecialPattern(String row){
|
||||
return WINNING_PATTERNS
|
||||
.stream()
|
||||
.filter(slotMapping -> row.contains(slotMapping.processedMapping))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private static class SlotMapping {
|
||||
private Integer factor;
|
||||
private String processedMapping;
|
||||
private String outcome;
|
||||
|
||||
public SlotMapping(List<String> slots, Integer factor, String outcome) {
|
||||
this.factor = factor;
|
||||
this.processedMapping = String.join("", slots);
|
||||
this.outcome = outcome;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package dev.sheldan.abstracto.entertainment.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.EconomyLeaderboardResult;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.EconomyUser;
|
||||
import dev.sheldan.abstracto.entertainment.repository.EconomyUserRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Component
|
||||
public class EconomyUserManagementServiceBean implements EconomyUserManagementService {
|
||||
|
||||
@Autowired
|
||||
private EconomyUserRepository repository;
|
||||
|
||||
@Override
|
||||
public EconomyUser createUser(AUserInAServer aUserInAServer) {
|
||||
EconomyUser user = EconomyUser
|
||||
.builder()
|
||||
.id(aUserInAServer.getUserInServerId())
|
||||
.server(aUserInAServer.getServerReference())
|
||||
.lastPayDay(Instant.now().minus(1, ChronoUnit.DAYS))
|
||||
.lastSlots(Instant.now().minus(1, ChronoUnit.DAYS))
|
||||
.credits(0L)
|
||||
.user(aUserInAServer)
|
||||
.build();
|
||||
return repository.save(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<EconomyUser> getUser(AUserInAServer aUserInAServer) {
|
||||
return repository.findByUser(aUserInAServer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EconomyLeaderboardResult getRankOfUserInServer(AUserInAServer aUserInAServer) {
|
||||
return repository.getRankOfUserInServer(aUserInAServer.getUserInServerId(), aUserInAServer.getServerReference().getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EconomyUser> getRanksInServer(AServer server, Integer page, Integer pagesize) {
|
||||
return repository.findTop10ByServerOrderByCreditsDesc(server, PageRequest.of(page, pagesize));
|
||||
}
|
||||
}
|
||||
@@ -6,3 +6,20 @@ abstracto.systemConfigs.rollDefaultHigh.longValue=6
|
||||
|
||||
abstracto.featureFlags.entertainment.featureName=entertainment
|
||||
abstracto.featureFlags.entertainment.enabled=false
|
||||
|
||||
abstracto.systemConfigs.paydayCredits.name=paydayCredits
|
||||
abstracto.systemConfigs.paydayCredits.longValue=120
|
||||
|
||||
# maybe replace this by command cooldowns which are globally on the server, instead of only channel group based
|
||||
abstracto.systemConfigs.paydayCooldown.name=paydayCooldown
|
||||
abstracto.systemConfigs.paydayCooldown.longValue=300
|
||||
|
||||
abstracto.systemConfigs.slotsCooldown.name=slotsCooldown
|
||||
abstracto.systemConfigs.slotsCooldown.longValue=30
|
||||
|
||||
abstracto.featureFlags.economy.featureName=economy
|
||||
abstracto.featureFlags.economy.enabled=false
|
||||
|
||||
# for now this is fine
|
||||
abstracto.systemConfigs.creditGambleJackpot.name=creditGambleJackpot
|
||||
abstracto.systemConfigs.creditGambleJackpot.longValue=1000
|
||||
@@ -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="seedData/data.xml" relativeToChangelogFile="true"/>
|
||||
<include file="tables/tables.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,45 @@
|
||||
<?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="economyFeature" value="(SELECT id FROM feature WHERE key = 'economy')"/>
|
||||
|
||||
<changeSet author="Sheldan" id="economy-command">
|
||||
<insert tableName="command">
|
||||
<column name="name" value="payday"/>
|
||||
<column name="module_id" valueComputed="${entertainmentModule}"/>
|
||||
<column name="feature_id" valueComputed="${economyFeature}"/>
|
||||
</insert>
|
||||
<insert tableName="command">
|
||||
<column name="name" value="slots"/>
|
||||
<column name="module_id" valueComputed="${entertainmentModule}"/>
|
||||
<column name="feature_id" valueComputed="${economyFeature}"/>
|
||||
</insert>
|
||||
<insert tableName="command">
|
||||
<column name="name" value="creditLeaderboard"/>
|
||||
<column name="module_id" valueComputed="${entertainmentModule}"/>
|
||||
<column name="feature_id" valueComputed="${economyFeature}"/>
|
||||
</insert>
|
||||
<insert tableName="command">
|
||||
<column name="name" value="credits"/>
|
||||
<column name="module_id" valueComputed="${entertainmentModule}"/>
|
||||
<column name="feature_id" valueComputed="${economyFeature}"/>
|
||||
</insert>
|
||||
<insert tableName="command">
|
||||
<column name="name" value="creditGamble"/>
|
||||
<column name="module_id" valueComputed="${entertainmentModule}"/>
|
||||
<column name="feature_id" valueComputed="${economyFeature}"/>
|
||||
</insert>
|
||||
<insert tableName="command">
|
||||
<column name="name" value="transferCredits"/>
|
||||
<column name="module_id" valueComputed="${entertainmentModule}"/>
|
||||
<column name="feature_id" valueComputed="${economyFeature}"/>
|
||||
</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="economy_feature-insertion">
|
||||
<insert tableName="feature">
|
||||
<column name="key" value="economy"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,44 @@
|
||||
<?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="economy_user-table">
|
||||
<createTable tableName="economy_user">
|
||||
<column name="id" type="BIGINT">
|
||||
<constraints nullable="false" primaryKey="true" primaryKeyName="economy_user_pkey"/>
|
||||
</column>
|
||||
<column name="credits" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="last_pay_day" type="TIMESTAMP WITHOUT TIME ZONE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="last_slots" type="TIMESTAMP WITHOUT TIME ZONE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
|
||||
<column name="server_id" type="BIGINT">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="economy_user" constraintName="fk_economy_user_server"
|
||||
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION"
|
||||
referencedColumnNames="id" referencedTableName="server" validate="true"/>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS economy_user_update_trigger ON economy_user;
|
||||
CREATE TRIGGER economy_user_update_trigger BEFORE UPDATE ON economy_user FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
|
||||
</sql>
|
||||
<sql>
|
||||
DROP TRIGGER IF EXISTS economy_user_insert_trigger ON economy_user;
|
||||
CREATE TRIGGER economy_user_insert_trigger BEFORE INSERT ON economy_user FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
|
||||
</sql>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
@@ -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="economy_user.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -9,4 +9,5 @@
|
||||
<include file="1.0-entertainment/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.2.8-entertainment/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.2.9-entertainment/collection.xml" relativeToChangelogFile="true"/>
|
||||
<include file="1.4.0/collection.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,26 @@
|
||||
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 EconomyFeatureConfig implements FeatureConfig {
|
||||
|
||||
public static final String PAYDAY_CREDITS_CONFIG_KEY = "paydayCredits";
|
||||
public static final String PAYDAY_COOLDOWN_CONFIG_KEY = "paydayCooldown";
|
||||
public static final String SLOTS_COOLDOWN_CONFIG_KEY = "slotsCooldown";
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return EntertainmentFeatureDefinition.ECONOMY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRequiredSystemConfigKeys() {
|
||||
return Arrays.asList(PAYDAY_CREDITS_CONFIG_KEY, PAYDAY_COOLDOWN_CONFIG_KEY, SLOTS_COOLDOWN_CONFIG_KEY);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public enum EntertainmentFeatureDefinition implements FeatureDefinition {
|
||||
ENTERTAINMENT("entertainment");
|
||||
ENTERTAINMENT("entertainment"), ECONOMY("economy");
|
||||
|
||||
private String key;
|
||||
|
||||
|
||||
@@ -3,4 +3,5 @@ package dev.sheldan.abstracto.entertainment.config;
|
||||
public class EntertainmentSlashCommandNames {
|
||||
public static final String ENTERTAINMENT = "entertainment";
|
||||
public static final String UTILITY = "utility";
|
||||
public static final String ECONOMY = "economy";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package dev.sheldan.abstracto.entertainment.dto;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class CreditGambleResult {
|
||||
private List<Integer> rolls;
|
||||
private Integer uniqueNumbers;
|
||||
private Long bid;
|
||||
private Long toBank;
|
||||
private Long toJackpot;
|
||||
private Long currentJackpot;
|
||||
private Boolean won;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package dev.sheldan.abstracto.entertainment.dto;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class PayDayResult {
|
||||
private Long currentCredits;
|
||||
private Long gainedCredits;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package dev.sheldan.abstracto.entertainment.dto;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class SlotsResult {
|
||||
private Long bid;
|
||||
private Long oldCredits;
|
||||
private Long newCredits;
|
||||
private Long winnings;
|
||||
private Long factor;
|
||||
private String outComeKey;
|
||||
private List<List<String>> rows;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.sheldan.abstracto.entertainment.exception;
|
||||
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
|
||||
|
||||
public class NotEnoughCreditsException extends AbstractoTemplatableException {
|
||||
@Override
|
||||
public String getTemplateName() {
|
||||
return "not_enough_wealth_exception";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTemplateModel() {
|
||||
return new Object();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package dev.sheldan.abstracto.entertainment.exception;
|
||||
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
|
||||
import dev.sheldan.abstracto.entertainment.model.exception.PayDayCooldownExceptionModel;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public class PayDayCooldownException extends AbstractoTemplatableException {
|
||||
|
||||
private final PayDayCooldownExceptionModel model;
|
||||
|
||||
public PayDayCooldownException(Duration duration) {
|
||||
this.model = PayDayCooldownExceptionModel
|
||||
.builder()
|
||||
.tryAgainDuration(duration)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplateName() {
|
||||
return "payday_cooldown_exception";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTemplateModel() {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package dev.sheldan.abstracto.entertainment.exception;
|
||||
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
|
||||
import dev.sheldan.abstracto.entertainment.model.exception.PayDayCooldownExceptionModel;
|
||||
import dev.sheldan.abstracto.entertainment.model.exception.SlotsCooldownExceptionModel;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public class SlotsCooldownException extends AbstractoTemplatableException {
|
||||
|
||||
private final SlotsCooldownExceptionModel model;
|
||||
|
||||
public SlotsCooldownException(Duration duration) {
|
||||
this.model = SlotsCooldownExceptionModel
|
||||
.builder()
|
||||
.tryAgainDuration(duration)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplateName() {
|
||||
return "slots_cooldown_exception";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTemplateModel() {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package dev.sheldan.abstracto.entertainment.model.command;
|
||||
|
||||
import dev.sheldan.abstracto.entertainment.dto.CreditGambleResult;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class CreditGambleResultModel {
|
||||
private List<Integer> rolls;
|
||||
private Integer uniqueNumbers;
|
||||
private Long bid;
|
||||
private Long toBank;
|
||||
private Long toJackpot;
|
||||
private Long currentJackpot;
|
||||
private Boolean won;
|
||||
|
||||
public static CreditGambleResultModel fromCreditGambleResult(CreditGambleResult creditGambleResult) {
|
||||
return CreditGambleResultModel
|
||||
.builder()
|
||||
.rolls(creditGambleResult.getRolls())
|
||||
.uniqueNumbers(creditGambleResult.getUniqueNumbers())
|
||||
.bid(creditGambleResult.getBid())
|
||||
.toBank(creditGambleResult.getToBank())
|
||||
.toJackpot(creditGambleResult.getToJackpot())
|
||||
.currentJackpot(creditGambleResult.getCurrentJackpot())
|
||||
.won(creditGambleResult.getWon())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package dev.sheldan.abstracto.entertainment.model.command;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class CreditsLeaderboardEntry {
|
||||
private MemberDisplay memberDisplay;
|
||||
private Member member;
|
||||
private Long credits;
|
||||
private Integer rank;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package dev.sheldan.abstracto.entertainment.model.command;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class CreditsLeaderboardResponseModel {
|
||||
private List<CreditsLeaderboardEntry> entries;
|
||||
private CreditsLeaderboardEntry ownRank;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package dev.sheldan.abstracto.entertainment.model.command;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class CreditsModel {
|
||||
private CreditsLeaderboardEntry entry;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package dev.sheldan.abstracto.entertainment.model.command;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class PayDayResponseModel {
|
||||
private Long currentCredits;
|
||||
private Long leaderboardPosition;
|
||||
private Long gainedCredits;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package dev.sheldan.abstracto.entertainment.model.command;
|
||||
|
||||
import dev.sheldan.abstracto.entertainment.dto.SlotsResult;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class SlotsResponseModel {
|
||||
private Long bid;
|
||||
private Long oldCredits;
|
||||
private Long newCredits;
|
||||
private Long winnings;
|
||||
private Long factor;
|
||||
private String outComeKey;
|
||||
private List<List<String>> rows;
|
||||
|
||||
public static SlotsResponseModel fromSlotsResult(SlotsResult result) {
|
||||
return SlotsResponseModel
|
||||
.builder()
|
||||
.bid(result.getBid())
|
||||
.factor(result.getFactor())
|
||||
.newCredits(result.getNewCredits())
|
||||
.outComeKey(result.getOutComeKey())
|
||||
.oldCredits(result.getOldCredits())
|
||||
.rows(result.getRows())
|
||||
.winnings(result.getWinnings())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package dev.sheldan.abstracto.entertainment.model.database;
|
||||
|
||||
public interface EconomyLeaderboardResult {
|
||||
|
||||
Long getId();
|
||||
|
||||
Long getUserid();
|
||||
|
||||
Long getCredits();
|
||||
|
||||
Integer getRank();
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package dev.sheldan.abstracto.entertainment.model.database;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import lombok.*;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
|
||||
@Builder
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "economy_user")
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode
|
||||
public class EconomyUser implements Serializable {
|
||||
|
||||
@Id
|
||||
@Column(name = "id", nullable = false)
|
||||
private Long id;
|
||||
|
||||
@OneToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
|
||||
@PrimaryKeyJoinColumn
|
||||
private AUserInAServer user;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "server_id", nullable = false)
|
||||
private AServer server;
|
||||
|
||||
@Column(name = "credits", nullable = false)
|
||||
private Long credits;
|
||||
|
||||
@Column(name = "last_slots", nullable = false)
|
||||
private Instant lastSlots;
|
||||
|
||||
@Column(name = "last_pay_day", nullable = false)
|
||||
private Instant lastPayDay;
|
||||
|
||||
@Column(name = "created", nullable = false, insertable = false, updatable = false)
|
||||
private Instant created;
|
||||
|
||||
@Column(name = "updated", insertable = false, updatable = false)
|
||||
private Instant updated;
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package dev.sheldan.abstracto.entertainment.model.exception;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class PayDayCooldownExceptionModel {
|
||||
private Duration tryAgainDuration;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package dev.sheldan.abstracto.entertainment.model.exception;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class SlotsCooldownExceptionModel {
|
||||
private Duration tryAgainDuration;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package dev.sheldan.abstracto.entertainment.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.entertainment.dto.CreditGambleResult;
|
||||
import dev.sheldan.abstracto.entertainment.dto.PayDayResult;
|
||||
import dev.sheldan.abstracto.entertainment.dto.SlotsResult;
|
||||
import dev.sheldan.abstracto.entertainment.model.command.CreditsLeaderboardEntry;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.EconomyUser;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
public interface EconomyService {
|
||||
boolean canTriggerPayDay(AUserInAServer aUserInAServer);
|
||||
boolean canTriggerSlots(AUserInAServer aUserInAServer);
|
||||
boolean canTriggerPayDay(EconomyUser economyUser);
|
||||
boolean canTriggerSlots(EconomyUser economyUser);
|
||||
Duration payDayTriggerIn(EconomyUser economyUser);
|
||||
Duration slotsTriggerIn(EconomyUser economyUser);
|
||||
EconomyUser addCredits(AUserInAServer aUserInAServer, Long credits);
|
||||
void addCredits(EconomyUser economyUser, Long credits);
|
||||
void addPayDayCredits(AUserInAServer aUserInAServer);
|
||||
PayDayResult triggerPayDay(AUserInAServer aUserInAServer);
|
||||
SlotsResult triggerSlots(AUserInAServer aUserInAServer, Long bid);
|
||||
SlotGame playSlots();
|
||||
List<CreditsLeaderboardEntry> getCreditLeaderboard(AServer server, Integer page);
|
||||
CreditsLeaderboardEntry getRankOfUser(AUserInAServer aUserInAServer);
|
||||
void transferCredits(AUserInAServer source, AUserInAServer target, Long amount);
|
||||
CreditGambleResult triggerCreditGamble(AUserInAServer aUserInAServer);
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
class SlotGame {
|
||||
private List<List<String>> rows;
|
||||
private String outcome;
|
||||
private Integer resultFactor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package dev.sheldan.abstracto.entertainment.service.management;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.EconomyLeaderboardResult;
|
||||
import dev.sheldan.abstracto.entertainment.model.database.EconomyUser;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface EconomyUserManagementService {
|
||||
EconomyUser createUser(AUserInAServer aUserInAServer);
|
||||
Optional<EconomyUser> getUser(AUserInAServer aUserInAServer);
|
||||
EconomyLeaderboardResult getRankOfUserInServer(AUserInAServer aUserInAServer);
|
||||
List<EconomyUser> getRanksInServer(AServer server, Integer page, Integer size);
|
||||
}
|
||||
Reference in New Issue
Block a user