Compare commits

...

72 Commits

Author SHA1 Message Date
Sheldan
18050e2a8a [maven-release-plugin] prepare release abstracto-application-1.4.14 2022-12-10 16:54:32 +01:00
Sheldan
a0d83763d4 [AB-78] fixing users with previous experience not awarding the experience role if they join and are pending 2022-12-10 15:44:09 +01:00
Sheldan
0461c8e4ec [AB-77] deferring the reply for report with context interactions
do not defer the interaction all the time for modal interactions
2022-12-10 12:59:47 +01:00
Sheldan
072f32975a [maven-release-plugin] prepare for next development iteration 2022-12-03 20:17:11 +01:00
Sheldan
6810d28c50 [maven-release-plugin] prepare release abstracto-application-1.4.13 2022-12-03 20:16:57 +01:00
Sheldan
650a9099c4 [AB-66] fixing not reducing the bid on mines
fixing reaction report sometimes running into a timeout
2022-12-03 20:14:18 +01:00
Sheldan
1217e03725 [maven-release-plugin] prepare for next development iteration 2022-12-02 21:20:57 +01:00
Sheldan
2e837d8738 [maven-release-plugin] prepare release abstracto-application-1.4.12 2022-12-02 21:20:53 +01:00
Sheldan
9ddd386c6f [AB-66] adding min mines ratio and other input restrictions for mines game which should avoid some too easy setups 2022-12-02 21:16:32 +01:00
Sheldan
4e1db26df7 [AB-xxx] fixing java version in build file 2022-12-02 01:58:48 +01:00
Sheldan
de335a1e2a [AB-xxx] updating versions of GitHub actions in release/build job 2022-12-02 01:55:36 +01:00
Sheldan
9ba0ed711e [maven-release-plugin] prepare for next development iteration 2022-12-02 01:36:51 +01:00
Sheldan
b7376bf522 [maven-release-plugin] prepare release abstracto-application-1.4.11 2022-12-02 01:36:46 +01:00
Sheldan
0d6182c5d5 [AB-66] adding mines game
fixing suggestion evaluation job not getting the correct parameters
adding feature dependent parameters
2022-12-02 01:33:38 +01:00
Sheldan
96c38763b1 [maven-release-plugin] prepare for next development iteration 2022-11-20 23:22:21 +01:00
Sheldan
97b3099156 [maven-release-plugin] prepare release abstracto-application-1.4.10 2022-11-20 23:22:16 +01:00
Sheldan
a0b2fc9c31 [AB-25] artificial delay 2022-11-20 23:20:07 +01:00
Sheldan
2db36ef96b [maven-release-plugin] prepare for next development iteration 2022-11-20 22:39:40 +01:00
Sheldan
08b3913b04 [maven-release-plugin] prepare release abstracto-application-1.4.9 2022-11-20 22:39:36 +01:00
Sheldan
7a7ec5654f [AB-25] changing member loading to be partitioned 2022-11-20 22:37:10 +01:00
Sheldan
74679c2ccc [maven-release-plugin] prepare for next development iteration 2022-11-20 19:53:42 +01:00
Sheldan
063581ba1a [maven-release-plugin] prepare release abstracto-application-1.4.8 2022-11-20 19:53:38 +01:00
Sheldan
5c7b018b2a [AB-25] refactoring experience collection to work instantly instead of delayed job
adding level up notification for experience
2022-11-20 19:49:34 +01:00
Sheldan
d315113395 [AB-76] adding evaluation job for suggestions 2022-11-11 00:06:16 +01:00
Sheldan
ea2f62b721 [AB-74] adding more information to output of transfer credits command 2022-11-09 22:53:36 +01:00
Sheldan
1a91275a2d [maven-release-plugin] prepare for next development iteration 2022-10-27 20:56:03 +02:00
Sheldan
e76b22ca59 [maven-release-plugin] prepare release abstracto-application-1.4.7 2022-10-27 20:55:58 +02:00
Sheldan
5a21f9642c [CCR] fixing order of execution for updating suggestion
using edit components instead of full message edit in message service bean
2022-10-27 20:49:36 +02:00
Sheldan
e3dd89b0ef [AB-75] adding thread reader command 2022-10-22 14:55:18 +02:00
Sheldan
4fe81c0ea2 [maven-release-plugin] prepare for next development iteration 2022-10-16 13:24:50 +02:00
Sheldan
ab55f5ee07 [maven-release-plugin] prepare release abstracto-application-1.4.6 2022-10-16 13:24:46 +02:00
Sheldan
92cec30898 [AB-xx] fixing version 2022-10-16 13:22:38 +02:00
Sheldan
3a40ccccda [maven-release-plugin] prepare release abstracto-application-1.4.5 2022-10-16 13:19:35 +02:00
Sheldan
e6802a0851 [AB-xx] fixing javadoc 2022-10-16 13:16:50 +02:00
Sheldan
ce90370b9c [maven-release-plugin] prepare for next development iteration 2022-10-16 12:04:14 +02:00
Sheldan
b21afce1ad [maven-release-plugin] prepare release abstracto-application-1.4.5 2022-10-16 12:04:10 +02:00
Sheldan
58fd11a561 [AB-xx] upgrade to JDA 5.0.0-alpha.21 2022-10-16 12:02:10 +02:00
Sheldan
3e38085a25 [AB-xx] upgrade to JDA 5.0.0-alpha.21 2022-10-16 11:54:23 +02:00
Sheldan
48f62f8bdf [AB-xx] make close log by default 2022-10-08 11:39:55 +02:00
Sheldan
ad92ccd181 [AB-71] adding cache flag to cached message 2022-10-06 00:34:22 +02:00
Sheldan
8ab0eaedda [AB-73] refactoring reminder notification model to not use entities 2022-10-06 00:10:32 +02:00
Sheldan
e2a7d3b9e8 [AB-72] adding joined reminders to list of reminders
refactored reminder display to not use entities
2022-10-06 00:01:48 +02:00
Sheldan
8c101793a3 [AB-8] fixing test 2022-09-18 16:00:38 +02:00
Sheldan
4e1f9e0018 [AB-8] upgrading to new JDA alpha version 19
cleaning up imports
2022-09-18 15:51:49 +02:00
Sheldan
102209aaca [maven-release-plugin] prepare for next development iteration 2022-09-02 15:48:29 +02:00
Sheldan
9a64da480b [maven-release-plugin] prepare release abstracto-application-1.4.4 2022-09-02 15:48:25 +02:00
Sheldan
11c36691ce [AB-xx] fixing reaction reports being stored when they should not be 2022-09-02 15:41:18 +02:00
Sheldan
337ef87e47 [maven-release-plugin] prepare for next development iteration 2022-08-28 23:30:52 +02:00
Sheldan
056a7fe297 [maven-release-plugin] prepare release abstracto-application-1.4.3 2022-08-28 23:30:47 +02:00
Sheldan
82383449c0 [AB-55] fixing custom command not considering feature flag
ignoring case for custom command
2022-08-28 23:24:08 +02:00
Sheldan
30655dbfef [AB-65] adding ability to join a reminder via button 2022-08-28 23:13:40 +02:00
Sheldan
a43725df39 [AB-69] adding additional info to userInfo command 2022-08-27 14:44:46 +02:00
Sheldan
9a0bff154b [AB-67] adding missing tracking trigger for infraction parameter 2022-08-25 00:41:37 +02:00
Sheldan
4d54b11400 [AB-68] adding message context menu and slash command for mock 2022-08-25 00:33:14 +02:00
Sheldan
9c621954c8 adding more logging for lash command exceptions 2022-08-19 15:56:23 +02:00
Sheldan
f24552fdf2 [maven-release-plugin] prepare for next development iteration 2022-08-01 23:02:25 +02:00
Sheldan
e643080516 [maven-release-plugin] prepare release abstracto-application-1.4.2 2022-08-01 23:02:20 +02:00
Sheldan
b747516881 fixing command alternatives being required
fixing filename for export emote stats
fixing reaction report message update
2022-08-01 22:59:48 +02:00
Sheldan
0a4238c9f5 [maven-release-plugin] prepare for next development iteration 2022-07-27 22:20:45 +02:00
Sheldan
99fe84914b [maven-release-plugin] prepare release abstracto-application-1.4.1 2022-07-27 22:20:41 +02:00
Sheldan
f9b85feeaf enabling auto complete for youtube command 2022-07-27 22:13:48 +02:00
Sheldan
51edb50eee fixing null handling for server channel message 2022-07-25 19:13:39 +02:00
Sheldan
43a78a4989 [AB-8] adding slash command and message context commands to metrics 2022-07-25 18:23:44 +02:00
Sheldan
e3e0ad98ba [maven-release-plugin] prepare for next development iteration 2022-07-23 10:33:34 +02:00
Sheldan
9c02be7299 [maven-release-plugin] prepare release abstracto-application-1.4.0 2022-07-23 10:33:31 +02:00
Sheldan
9954515db0 [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
2022-07-23 10:20:41 +02:00
Sheldan
68cae74819 [AB-52] upgrading to alpha 12
adding anonymous reporting
reworking message context commands
refactoring interaction packages
adding post execution handling for message context commands and modals
reworking feature mode response
fixing setup using component ids
storing infraction parameters, for example mute duration, with every infraction
adding infractions for more moderation actions
creating general method to format a duration string
adding infractions command
reworking muting to use built-in functionality of discord
enabling chunking of members
removing manual unmuting feature mode
adding ability to update infractions with a command
implemented infraction listeners for ban and warn
refactored infraction notifications
storing log messages to the infraction for editing said log messages
2022-07-18 21:52:12 +02:00
Sheldan
1a1fde0800 [AB-11] fixing still considering bots when calculating the star amount
fixes #11
2022-06-21 00:15:47 +02:00
Sheldan
74cee39f1a [AB-12] fixing not ignoring ignored starboard posts in starstats
fixes #12
2022-06-20 23:50:39 +02:00
dependabot[bot]
5ad3e30cc1 Bump gson from 2.8.6 to 2.8.9 in /abstracto-application
Bumps [gson](https://github.com/google/gson) from 2.8.6 to 2.8.9.
- [Release notes](https://github.com/google/gson/releases)
- [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/gson/compare/gson-parent-2.8.6...gson-parent-2.8.9)

---
updated-dependencies:
- dependency-name: com.google.code.gson:gson
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-20 23:35:23 +02:00
Sheldan
c02bf4aa8e [AB-10] attempt to fix the race condition when creating a starboard post
fixes #10
2022-06-20 23:32:33 +02:00
Sheldan
c84a76b6a3 [maven-release-plugin] prepare for next development iteration 2022-06-20 00:20:23 +02:00
842 changed files with 12474 additions and 6417 deletions

View File

@@ -19,11 +19,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up JDK 1.8
uses: actions/setup-java@v1
uses: actions/setup-java@v3
with:
java-version: 1.8
distribution: 'corretto'
java-version: 8
- name: Build with Maven
run: mvn -B install --file abstracto-application/pom.xml
- name: Setup sonarqube

View File

@@ -6,13 +6,14 @@ jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
persist-credentials: false
- name: Set up Java for publishing to GitHub Packages
uses: actions/setup-java@v1
uses: actions/setup-java@v3
with:
java-version: 1.8
distribution: 'corretto'
java-version: 8
- name: Load current version
id: version
run: echo "version=$(mvn --file abstracto-application/pom.xml -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive exec:exec)" >> $GITHUB_ENV
@@ -21,7 +22,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Deploy documentation to GitHub pages
uses: JamesIves/github-pages-deploy-action@4.1.0
uses: JamesIves/github-pages-deploy-action@v4
with:
repository-name: Sheldan/abstracto-docs
target-folder: docs/${{ env.version }}
@@ -29,7 +30,7 @@ jobs:
ssh-key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
folder: abstracto-application/documentation/target/generated-docs
- name: Deploy documentation to GitHub pages latest
uses: JamesIves/github-pages-deploy-action@4.1.0
uses: JamesIves/github-pages-deploy-action@v4
with:
repository-name: Sheldan/abstracto-docs
target-folder: docs/current
@@ -37,7 +38,7 @@ jobs:
ssh-key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
folder: abstracto-application/documentation/target/generated-docs
- name: Login to GitHub Packages Docker Registry
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
registry: docker.pkg.github.com
username: ${{ github.repository_owner }}

View File

@@ -14,7 +14,7 @@ An example implementation of this bot can be seen [here](https://github.com/Shel
## Technologies
* [JDA](https://github.com/DV8FromTheWorld/JDA/) The Discord API Wrapper in the version 4.3.0_315
* [JDA](https://github.com/DV8FromTheWorld/JDA/) The Discord API Wrapper in the version 5.0.0-alpha.21
* [Spring boot](https://github.com/spring-projects/spring-boot) is used as a framework to create standalone application in Java with Java EE methods. (including Dependency injection and more)
* [Hibernate](https://github.com/hibernate/hibernate-orm) is used as a reference implementation of JPA.
* [Freemarker](https://github.com/apache/freemarker) is used as a templating engine. This is used to provide internationalization for user facing text and enable dynamic embed configuration.

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>anti-raid</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.0.RC2</version>
<version>1.4.14</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -7,8 +7,8 @@ import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageReceivedListener;
import dev.sheldan.abstracto.core.models.listener.MessageReceivedModel;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.ChannelType;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

View File

@@ -5,10 +5,7 @@ import dev.sheldan.abstracto.antiraid.model.MassPingNotificationModel;
import dev.sheldan.abstracto.core.models.ConditionContextInstance;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.service.ConditionService;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.service.SystemCondition;
import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
@@ -61,19 +58,22 @@ public class MassPingServiceBean implements MassPingService {
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private MemberService memberService;
@Override
public CompletableFuture<Void> processMessage(Message message) {
if(message.getMentionedMembers().size() > maxAllowedMentions) {
if(message.getMentions().getUsers().size() > maxAllowedMentions) {
Integer level = configService.getLongValueOrConfigDefault(MassPingService.MAX_AFFECTED_LEVEL_KEY, message.getGuild().getIdLong()).intValue();
boolean allowed = allowedToMassMention(message, level);
if(!allowed) {
return muteService.muteMemberWithoutContext(message.getMember())
return memberService.timeoutUserMaxDuration(message.getMember())
.thenAccept(unused -> self.sendMassPingMuteNotification(message))
.thenAccept(unused -> log.info("Muted member {} in server {} because of too many member mentions. (> {}).",
message.getMember().getIdLong(), message.getGuild().getIdLong(), maxAllowedMentions));
message.getAuthor().getIdLong(), message.getGuild().getIdLong(), maxAllowedMentions));
} else {
log.info("User {} in server {} is allowed to mass mention, because of level (or lack of level configuration).",
message.getMember().getIdLong(), message.getGuild().getIdLong());
message.getAuthor().getIdLong(), message.getGuild().getIdLong());
return CompletableFuture.completedFuture(null);
}
} else {
@@ -103,7 +103,7 @@ public class MassPingServiceBean implements MassPingService {
MassPingNotificationModel model = MassPingNotificationModel
.builder()
.messageLink(message.getJumpUrl())
.mentionCount(message.getMentionedMembers().size())
.mentionCount(message.getMentions().getUsers().size())
.messageContent(message.getContentRaw())
.memberDisplay(MemberDisplay.fromMember(member))
.build();

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>anti-raid</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.0.RC2</version>
<version>1.4.14</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.0.RC2</version>
<version>1.4.14</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>assignable-roles</artifactId>
<version>1.4.0.RC2</version>
<version>1.4.14</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -17,7 +17,7 @@ import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

View File

@@ -12,7 +12,7 @@ import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -48,8 +48,18 @@ public class MoveAssignableRolePlace extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter channel = Parameter.builder().name("channel").type(TextChannel.class).templated(true).build();
Parameter rolePostName = Parameter
.builder()
.name("name")
.type(String.class)
.templated(true)
.build();
Parameter channel = Parameter
.builder()
.name("channel")
.type(TextChannel.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(rolePostName, channel);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()

View File

@@ -19,10 +19,10 @@ import dev.sheldan.abstracto.assignableroles.service.management.AssignedRoleUser
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.listener.ButtonClickedListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.ButtonClickedListener;
import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListenerResult;
import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListener;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.ButtonClickedListenerModel;
import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListenerModel;
import dev.sheldan.abstracto.core.service.RoleService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.CompletableFutureList;

View File

@@ -1,6 +1,6 @@
package dev.sheldan.abstracto.assignableroles.model;
import dev.sheldan.abstracto.core.models.template.button.ButtonPayload;
import dev.sheldan.abstracto.core.interaction.button.ButtonPayload;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

View File

@@ -11,6 +11,9 @@ import dev.sheldan.abstracto.assignableroles.service.management.*;
import dev.sheldan.abstracto.core.command.exception.CommandParameterKeyValueWrongTypeException;
import dev.sheldan.abstracto.core.exception.ChannelNotInGuildException;
import dev.sheldan.abstracto.core.exception.EmoteNotUsableException;
import dev.sheldan.abstracto.core.interaction.ComponentPayloadManagementService;
import dev.sheldan.abstracto.core.interaction.ComponentPayloadService;
import dev.sheldan.abstracto.core.interaction.ComponentService;
import dev.sheldan.abstracto.core.models.FullEmote;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.ARole;
@@ -24,6 +27,8 @@ import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -114,7 +119,7 @@ public class AssignableRolePlaceServiceBean implements AssignableRolePlaceServic
// it only may be unusable if its a custom emote
log.debug("Using custom emote {} to create assignable role {} for assignable role place {} in server {}.",
fakeEmote.getEmote().getId(), role.getId(), placeId, serverId);
if (!emoteService.isEmoteUsableByBot(fakeEmote.getEmote()) && fakeEmote.getEmote().isAvailable()) {
if (!emoteService.isEmoteUsableByBot(fakeEmote.getEmote())) {
throw new EmoteNotUsableException(fakeEmote.getEmote());
}
}

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>assignable-roles</artifactId>
<version>1.4.0.RC2</version>
<version>1.4.14</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>assignable-roles-int</artifactId>

View File

@@ -9,7 +9,7 @@ import net.dv8tion.jda.api.entities.Role;
/**
* Exception thrown in case the {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRole} has already been
* defined for an {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace}. This is identified
* via the {@link net.dv8tion.jda.api.entities.Emote} which is used to react.
* via the emote which is used to react.
*/
public class AssignableRoleAlreadyDefinedException extends AbstractoRunTimeException implements Templatable {
private final AssignableRoleAlreadyDefinedExceptionModel model;

View File

@@ -6,7 +6,7 @@ import dev.sheldan.abstracto.core.templating.Templatable;
/**
* Exception thrown in case the {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace place}
* identified by {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace#key}
* identified by {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace} key
*/
public class AssignableRolePlaceAlreadyExistsException extends AbstractoRunTimeException implements Templatable {

View File

@@ -11,7 +11,7 @@ import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import java.util.concurrent.CompletableFuture;

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.4.0.RC2</version>
<version>1.4.14</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>custom-command</artifactId>
<version>1.4.14</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>custom-command-impl</artifactId>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/assembly/liquibase.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>custom-command-int</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,18 @@
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<id>liquibase</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<outputDirectory>.</outputDirectory>
<directory>${project.basedir}/src/main/resources/migrations</directory>
<includes>
<include>**/*</include>
</includes>
</fileSet>
</fileSets>
</assembly>

View File

@@ -0,0 +1,73 @@
package dev.sheldan.abstracto.customcommand.command;
import dev.sheldan.abstracto.core.command.CommandAlternative;
import dev.sheldan.abstracto.core.command.execution.UnParsedCommandParameter;
import dev.sheldan.abstracto.core.command.service.CommandRegistry;
import dev.sheldan.abstracto.core.config.ListenerPriority;
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.customcommand.config.CustomCommandFeatureConfig;
import dev.sheldan.abstracto.customcommand.model.command.CustomCommandResponseModel;
import dev.sheldan.abstracto.customcommand.model.database.CustomCommand;
import dev.sheldan.abstracto.customcommand.service.management.CustomCommandManagementService;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@Component
public class CustomCommandAlternative implements CommandAlternative {
private static final String CUSTOM_COMMAND_RESPONSE = "custom_command_response";
@Autowired
private CommandRegistry commandRegistry;
@Autowired
private CustomCommandManagementService customCommandManagementService;
@Autowired
private ChannelService channelService;
@Autowired
private TemplateService templateService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private CustomCommandFeatureConfig customCommandFeatureConfig;
@Override
public boolean shouldExecute(UnParsedCommandParameter parameter, Guild guild) {
return featureFlagService.isFeatureEnabled(customCommandFeatureConfig, guild.getIdLong());
}
@Override
public void execute(UnParsedCommandParameter parameter, Message message) {
String contentStripped = message.getContentRaw();
List<String> parameters = Arrays.asList(contentStripped.split(" "));
String commandName = commandRegistry.getCommandName(parameters.get(0), message.getGuild().getIdLong());
Optional<CustomCommand> customCommandOptional = customCommandManagementService.getCustomCommandByName(commandName, message.getGuild().getIdLong());
customCommandOptional.ifPresent(customCommand -> {
CustomCommandResponseModel model = CustomCommandResponseModel
.builder()
.additionalText(customCommand.getAdditionalMessage())
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(CUSTOM_COMMAND_RESPONSE, model, message.getGuild().getIdLong());
channelService.sendMessageToSendToChannel(messageToSend, message.getChannel());
});
}
@Override
public Integer getPriority() {
return ListenerPriority.MEDIUM;
}
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.customcommand.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource("classpath:custom-command-config.properties")
public class CustomCommandConfig {
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.customcommand.repository;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.customcommand.model.database.CustomCommand;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface CustomCommandRepository extends JpaRepository<CustomCommand, Long> {
Optional<CustomCommand> getByNameIgnoreCaseAndServer(String name, AServer server);
}

View File

@@ -0,0 +1,26 @@
package dev.sheldan.abstracto.customcommand.service.management;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.customcommand.model.database.CustomCommand;
import dev.sheldan.abstracto.customcommand.repository.CustomCommandRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
public class CustomCommandManagementServiceBean implements CustomCommandManagementService {
@Autowired
private CustomCommandRepository repository;
@Autowired
private ServerManagementService serverManagementService;
@Override
public Optional<CustomCommand> getCustomCommandByName(String name, Long serverId) {
AServer server = serverManagementService.loadServer(serverId);
return repository.getByNameIgnoreCaseAndServer(name, server);
}
}

View File

@@ -0,0 +1,2 @@
abstracto.featureFlags.customCommand.featureName=customCommand
abstracto.featureFlags.customCommand.enabled=false

View File

@@ -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="tables/tables.xml" relativeToChangelogFile="true"/>
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -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="feature.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -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="custom_command-feature-insertion">
<insert tableName="feature">
<column name="key" value="customCommand"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,51 @@
<?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="custom_command-table">
<createTable tableName="custom_command">
<column name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="custom_command_pkey"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="false"/>
</column>
<column name="creator_user_in_server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="additional_message" type="VARCHAR(2048)">
<constraints nullable="false"/>
</column>
<column name="NAME" type="VARCHAR(64)">
<constraints nullable="false"/>
</column>
<column name="server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<addUniqueConstraint columnNames="name"
constraintName="uc_custom_command_name"
disabled="false"
tableName="custom_command"/>
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="custom_command"
constraintName="fk_custom_command_server" deferrable="false" initiallyDeferred="false"
onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
<addForeignKeyConstraint baseColumnNames="creator_user_in_server_id" baseTableName="custom_command"
constraintName="fk_custom_command_creator" deferrable="false" initiallyDeferred="false"
onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="user_in_server_id" referencedTableName="user_in_server" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS custom_command_update_trigger ON custom_command;
CREATE TRIGGER custom_command_update_trigger BEFORE UPDATE ON custom_command FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS custom_command_insert_trigger ON custom_command;
CREATE TRIGGER custom_command_insert_trigger BEFORE INSERT ON custom_command FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -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="custom_command.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -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="1.4.0/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>custom-command</artifactId>
<version>1.4.14</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>custom-command-int</artifactId>
</project>

View File

@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.customcommand.config;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import org.springframework.stereotype.Component;
@Component
public class CustomCommandFeatureConfig implements FeatureConfig {
@Override
public FeatureDefinition getFeature() {
return CustomCommandFeatureDefinition.CUSTOM_COMMAND;
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.customcommand.config;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import lombok.Getter;
@Getter
public enum CustomCommandFeatureDefinition implements FeatureDefinition {
CUSTOM_COMMAND("customCommand");
private String key;
CustomCommandFeatureDefinition(String key) {
this.key = key;
}
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.customcommand.model.command;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class CustomCommandResponseModel {
private String additionalText;
}

View File

@@ -0,0 +1,45 @@
package dev.sheldan.abstracto.customcommand.model.database;
import dev.sheldan.abstracto.core.models.database.*;
import lombok.*;
import javax.persistence.*;
import java.io.Serializable;
import java.time.Instant;
@Entity
@Table(name = "custom_command")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode
public class CustomCommand implements Serializable {
@Id
@Column(name = "id", nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "additional_message")
private String additionalMessage;
@Column(name = "name")
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "server_id", nullable = false)
private AServer server;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "creator_user_in_server_id", nullable = false)
private AUserInAServer creator;
@Column(name = "created", nullable = false, insertable = false, updatable = false)
private Instant created;
@Column(name = "updated", insertable = false, updatable = false)
private Instant updated;
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.customcommand.service.management;
import dev.sheldan.abstracto.customcommand.model.database.CustomCommand;
import java.util.Optional;
public interface CustomCommandManagementService {
Optional<CustomCommand> getCustomCommandByName(String name, Long serverId);
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.14</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>custom-command</artifactId>
<packaging>pom</packaging>
<modules>
<module>custom-command-int</module>
<module>custom-command-impl</module>
</modules>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>core-int</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>dynamic-activity</artifactId>
<version>1.4.0.RC2</version>
<version>1.4.14</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>dynamic-activity</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.0.RC2</version>
<version>1.4.14</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.0.RC2</version>
<version>1.4.14</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>entertainment</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.0.RC2</version>
<version>1.4.14</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -4,11 +4,10 @@ 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.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ChannelService;

View File

@@ -4,11 +4,10 @@ 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.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ChannelService;

View File

@@ -4,11 +4,10 @@ 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.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ChannelService;

View File

@@ -6,17 +6,21 @@ 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.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.command.handler.parameter.CombinedParameter;
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.service.ChannelService;
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.MockResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import net.dv8tion.jda.api.entities.Member;
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;
@@ -29,12 +33,20 @@ import static dev.sheldan.abstracto.core.command.config.Parameter.ADDITIONAL_TYP
public class Mock extends AbstractConditionableCommand {
public static final String MOCK_RESPONSE_TEMPLATE_KEY = "mock_response";
public static final String MOCK_COMMAND = "mock";
public static final String MESSAGE_PARAMETER = "message";
@Autowired
private EntertainmentService entertainmentService;
@Autowired
private ChannelService channelService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
Object givenParameter = commandContext.getParameters().getParameters().get(0);
@@ -48,26 +60,63 @@ public class Mock extends AbstractConditionableCommand {
messageText = givenParameter.toString();
}
String mockingText = entertainmentService.createMockText(messageText, commandContext.getAuthor(), mockedMember);
MockResponseModel model = (MockResponseModel) ContextConverter.slimFromCommandContext(commandContext, MockResponseModel.class);
model.setOriginalText(messageText);
model.setMockingText(mockingText);
MockResponseModel model = MockResponseModel
.builder()
.originalText(messageText)
.mockingText(mockingText)
.build();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(MOCK_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String text = slashCommandParameterService.getCommandOption(MESSAGE_PARAMETER, event, String.class);
String mockingText = entertainmentService.createMockText(text, event.getMember(), null);
MockResponseModel model = MockResponseModel
.builder()
.originalText(text)
.mockingText(mockingText)
.build();
return interactionService.replyEmbed(MOCK_RESPONSE_TEMPLATE_KEY, model, event.getInteraction())
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
Map<String, Object> parameterAlternatives = new HashMap<>();
parameterAlternatives.put(ADDITIONAL_TYPES_KEY, Arrays.asList(Message.class, String.class));
parameters.add(Parameter.builder().name("message").type(CombinedParameter.class).remainder(true)
.additionalInfo(parameterAlternatives).templated(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
Parameter messageParameter = Parameter
.builder()
.name(MESSAGE_PARAMETER)
.type(CombinedParameter.class)
.remainder(true)
.additionalInfo(parameterAlternatives)
.templated(true)
.build();
parameters.add(messageParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(EntertainmentSlashCommandNames.ENTERTAINMENT)
.commandName(MOCK_COMMAND)
.build();
return CommandConfiguration.builder()
.name("mock")
.name(MOCK_COMMAND)
.module(EntertainmentModuleDefinition.ENTERTAINMENT)
.templated(true)
.async(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.parameters(parameters)
.help(helpInfo)

View File

@@ -4,11 +4,11 @@ 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.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
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.command.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.service.ChannelService;

View File

@@ -4,7 +4,7 @@ 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.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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 bidParameter = Parameter
.builder()
.name(BID_PARAMETER)
.type(Integer.class)
.templated(true)
.validators(Arrays.asList(MinIntegerValueValidator.min(0L)))
.build();
parameters.add(bidParameter);
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;
}
}

View File

@@ -0,0 +1,138 @@
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.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
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.TransferCreditsModel;
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;
@Autowired
private ChannelService channelService;
@Override
public CompletableFuture<CommandResult> executeAsync(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());
TransferCreditsModel responseModel = TransferCreditsModel
.builder()
.sourceMember(MemberDisplay.fromMember(commandContext.getAuthor()))
.targetMember(MemberDisplay.fromMember(targetMember))
.credits(amount)
.build();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(TRANSFER_CREDITS_RESPONSE, responseModel, commandContext.getChannel()))
.thenApply(unused -> 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());
TransferCreditsModel responseModel = TransferCreditsModel
.builder()
.sourceMember(MemberDisplay.fromMember(event.getMember()))
.targetMember(MemberDisplay.fromMember(targetMember))
.credits(amount)
.build();
economyService.transferCredits(sourceUser, targetUser, amount.longValue());
return interactionService.replyEmbed(TRANSFER_CREDITS_RESPONSE, responseModel, 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(false)
.async(true)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return EntertainmentFeatureDefinition.ECONOMY;
}
}

View File

@@ -0,0 +1,230 @@
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;
long serverId = event.getGuild().getIdLong();
boolean economyEnabled = featureFlagService.getFeatureFlagValue(EntertainmentFeatureDefinition.ECONOMY, serverId);
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, serverId);
board.setCreditsEnabled(economyEnabled);
board.setUserId(event.getMember().getIdLong());
board.setServerId(serverId);
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;
long serverId = commandContext.getGuild().getIdLong();
boolean economyEnabled = featureFlagService.getFeatureFlagValue(EntertainmentFeatureDefinition.ECONOMY, serverId);
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, serverId);
board.setCreditsEnabled(economyEnabled);
board.setUserId(commandContext.getAuthor().getIdLong());
board.setServerId(serverId);
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;
}
}

View File

@@ -0,0 +1,79 @@
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();
if(model.getEvent().getUser().getIdLong() != payload.getMineBoard().getUserId()) {
return ButtonClickedListenerResult.IGNORED;
}
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);
}
}

View File

@@ -0,0 +1,64 @@
package dev.sheldan.abstracto.entertainment.listener.interaction;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.MessageContextConfig;
import dev.sheldan.abstracto.core.interaction.context.ContextCommandService;
import dev.sheldan.abstracto.core.interaction.context.message.listener.MessageContextCommandListener;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.models.listener.interaction.MessageContextInteractionModel;
import dev.sheldan.abstracto.entertainment.config.EntertainmentFeatureDefinition;
import dev.sheldan.abstracto.entertainment.model.command.MockResponseModel;
import dev.sheldan.abstracto.entertainment.service.EntertainmentService;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import static dev.sheldan.abstracto.entertainment.command.Mock.MOCK_RESPONSE_TEMPLATE_KEY;
@Component
public class MockMessageContextCommandListener implements MessageContextCommandListener {
@Autowired
private ContextCommandService contextCommandService;
@Autowired
private EntertainmentService entertainmentService;
@Autowired
private InteractionService interactionService;
@Override
public DefaultListenerResult execute(MessageContextInteractionModel eventModel) {
Message targetMessage = eventModel.getEvent().getTarget();
String mockText = entertainmentService.createMockText(targetMessage.getContentRaw(), eventModel.getEvent().getMember(), targetMessage.getMember());
MockResponseModel model = MockResponseModel
.builder()
.originalText(targetMessage.getContentRaw())
.mockingText(mockText)
.build();
interactionService.replyEmbed(MOCK_RESPONSE_TEMPLATE_KEY, model, eventModel.getEvent());
return DefaultListenerResult.PROCESSED;
}
@Override
public FeatureDefinition getFeature() {
return EntertainmentFeatureDefinition.ENTERTAINMENT;
}
@Override
public MessageContextConfig getConfig() {
return MessageContextConfig
.builder()
.isTemplated(true)
.name("mock")
.templateKey("mock_message_context_menu_label")
.build();
}
@Override
public Boolean handlesEvent(MessageContextInteractionModel model) {
return contextCommandService.matchesGuildContextName(model, getConfig(), model.getServerId());
}
}

View File

@@ -0,0 +1,42 @@
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);
Optional<EconomyUser> findByServer_IdAndUser_UserReference_Id(Long serverId, Long userId);
@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);
}

View File

@@ -0,0 +1,353 @@
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 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;
}
}
}

View File

@@ -0,0 +1,312 @@
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;
import static dev.sheldan.abstracto.entertainment.config.GamesFeatureConfig.MINES_MINIMUM_MINES_RATIO;
@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, Long serverId) {
double minMinesRatio = configService.getDoubleValueOrConfigDefault(MINES_MINIMUM_MINES_RATIO, serverId);
if(mines >= width * height || width > 5 || height > 5 || mines <= 1 || height <= 1 || width <= 1) {
throw new InvalidGameBoardException(minMinesRatio);
}
if((double) mines / (width * height) < minMinesRatio) {
throw new InvalidGameBoardException(minMinesRatio);
}
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(), -credits);
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));
}
}

View File

@@ -0,0 +1,57 @@
package dev.sheldan.abstracto.entertainment.service.management;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.entertainment.model.database.EconomyLeaderboardResult;
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 Optional<EconomyUser> getUser(ServerUser serverUser) {
return repository.findByServer_IdAndUser_UserReference_Id(serverUser.getServerId(), serverUser.getUserId());
}
@Override
public EconomyLeaderboardResult getRankOfUserInServer(AUserInAServer aUserInAServer) {
return repository.getRankOfUserInServer(aUserInAServer.getUserInServerId(), aUserInAServer.getServerReference().getId());
}
@Override
public List<EconomyUser> getRanksInServer(AServer server, Integer page, Integer pagesize) {
return repository.findTop10ByServerOrderByCreditsDesc(server, PageRequest.of(page, pagesize));
}
}

View File

@@ -6,3 +6,29 @@ 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
abstracto.featureFlags.games.featureName=games
abstracto.featureFlags.games.enabled=false
abstracto.systemConfigs.minesCreditsFactor.name=minesCreditsFactor
abstracto.systemConfigs.minesCreditsFactor.doubleValue=5
abstracto.systemConfigs.minesMinMineRatio.name=minesMinMineRatio
abstracto.systemConfigs.minesMinMineRatio.doubleValue=0.2
# for now this is fine
abstracto.systemConfigs.creditGambleJackpot.name=creditGambleJackpot
abstracto.systemConfigs.creditGambleJackpot.longValue=1000

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -0,0 +1,19 @@
<?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="entertainmentFeature" value="(SELECT id FROM feature WHERE key = 'entertainment')"/>
<changeSet author="Sheldan" id="mock_context_command">
<insert tableName="context_command">
<column name="name" value="mock"/>
<column name="type" value="MESSAGE"/>
<column name="feature_id" valueComputed="${entertainmentFeature}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -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="context_command.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -9,4 +9,7 @@
<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"/>
<include file="1.4.3/collection.xml" relativeToChangelogFile="true"/>
<include file="1.4.11/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -11,7 +11,6 @@ import net.dv8tion.jda.api.entities.Member;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -20,7 +19,6 @@ import org.springframework.core.io.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Reader;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashMap;

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>entertainment</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.4.0.RC2</version>
<version>1.4.14</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -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);
}
}

View File

@@ -5,7 +5,7 @@ import lombok.Getter;
@Getter
public enum EntertainmentFeatureDefinition implements FeatureDefinition {
ENTERTAINMENT("entertainment");
ENTERTAINMENT("entertainment"), ECONOMY("economy"), GAMES("games");
private String key;

View File

@@ -3,4 +3,6 @@ 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";
public static final String GAME = "game";
}

View File

@@ -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 GamesFeatureConfig implements FeatureConfig {
public static final String MINES_CREDITS_FACTOR = "minesCreditsFactor";
public static final String MINES_MINIMUM_MINES_RATIO = "minesMinMineRatio";
@Override
public FeatureDefinition getFeature() {
return EntertainmentFeatureDefinition.GAMES;
}
@Override
public List<String> getRequiredSystemConfigKeys() {
return Arrays.asList(MINES_CREDITS_FACTOR, MINES_MINIMUM_MINES_RATIO);
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -0,0 +1,27 @@
package dev.sheldan.abstracto.entertainment.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
import dev.sheldan.abstracto.entertainment.model.exception.InvalidGameBoardExceptionModel;
public class InvalidGameBoardException extends AbstractoTemplatableException {
private final InvalidGameBoardExceptionModel model;
public InvalidGameBoardException(Double minRatio) {
super();
this.model = InvalidGameBoardExceptionModel
.builder()
.minMinesRatio(minRatio)
.build();
}
@Override
public String getTemplateName() {
return "invalid_mine_board_config_exception";
}
@Override
public Object getTemplateModel() {
return model;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,28 @@
package dev.sheldan.abstracto.entertainment.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -1,14 +1,13 @@
package dev.sheldan.abstracto.entertainment.model.command;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
@Getter
@Setter
@SuperBuilder
public class MockResponseModel extends SlimUserInitiatedServerContext {
@Builder
public class MockResponseModel {
private String originalText;
private String mockingText;
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.entertainment.model.command;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import lombok.Builder;
import lombok.Getter;
@Builder
@Getter
public class TransferCreditsModel {
private Integer credits;
private MemberDisplay sourceMember;
private MemberDisplay targetMember;
}

View File

@@ -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;
}
}

Some files were not shown because too many files have changed in this diff Show More