Compare commits

...

135 Commits

Author SHA1 Message Date
Sheldan
e35071d8d5 [maven-release-plugin] prepare release abstracto-application-1.3.11 2022-02-12 11:37:08 +01:00
Sheldan
14865a32f2 [AB-xxx] fixing year 2022-02-12 11:34:41 +01:00
Sheldan
ae3c66384f [AB-xxx] not requiring async voice channel listeners
fixing duplicated error reporting for command received handler
2022-02-10 23:05:35 +01:00
Sheldan
986b65a1e4 [AB-347] adding support for multiple and generic attachments to modmail
fixing editing messages with multiple embeds
fixing message limit also bein imposed on pure embed count
2021-12-25 16:29:23 +01:00
Sheldan
cc898b27bb [AB-353] reducing chance of concurrent modification exception when registering a new metric on the fly 2021-12-25 10:53:55 +01:00
Sheldan
9ce07a1a4a [AB-352] removing conversion tests 2021-12-24 23:49:26 +01:00
Sheldan
ee01a3f07c [AB-352] adding caching to the loading of members for warnings/user notes/mutes. this becomes relevant as members which left the guild are not cached by jda, which means, we retry it again
improved models for templates to simplify the objects passed
2021-12-24 23:41:27 +01:00
Sheldan
9230a13218 [maven-release-plugin] prepare for next development iteration 2021-12-24 13:47:56 +01:00
Sheldan
c248351721 [maven-release-plugin] prepare release abstracto-application-1.3.10 2021-12-24 13:47:52 +01:00
Sheldan
b0376778fe [AB-xxx] adding utility function to change color of a role 2021-12-24 13:44:57 +01:00
Sheldan
7f5d17e4dc [AB-349] supporting multiple embeds in templating mechanism
fixing paginator without embeds
2021-12-08 16:26:11 +01:00
Sheldan
0e7ea25aef [AB-291] refactoring pagination to custom button pagination
adding mutes command
fixing edit message not considering components
reversing the origin comparison in a few button click listeners
fixing ordering of warnings and mutes from converter
2021-12-01 23:22:46 +01:00
Sheldan
f2aa7035aa [AB-307] enabling dependent features and including them into the setup wizard
this comes with the usual limitation, that dependent features must be referenced via importing
2021-11-28 13:19:17 +01:00
Sheldan
c10296251c [AB-327] adding a third state to the result of a system condition: Ignored, this means that the condition was not evaluated, and is neither true nor false, this is for example used when the feature the condition is about is disabled
adding server ID to has level condition parameters
2021-11-27 22:08:12 +01:00
Sheldan
15fb9d95a8 [AB-343] fixing link buttons not being properly supported in templates 2021-11-23 19:57:16 +01:00
Sheldan
79633d7a8e [AB-341] fixing unSuggest not working correctly in case there are votes on the suggestion 2021-11-20 11:44:07 +01:00
Sheldan
cef46737c5 [AB-170] adding emote stat command for single emote
fixing naming of fields in show tracked emotes
2021-10-27 00:25:10 +02:00
Sheldan
c7076795a2 [AB-340] fixing test 2021-10-26 13:59:48 +02:00
Sheldan
9e1527ccd1 [AB-340] adding timestamp support for serverInfo command 2021-10-26 13:53:13 +02:00
Sheldan
326d488059 [maven-release-plugin] prepare for next development iteration 2021-10-25 23:20:39 +02:00
Sheldan
a39b6695a6 [maven-release-plugin] prepare release abstracto-application-1.3.9 2021-10-25 23:20:31 +02:00
Sheldan
362d87778e [AB-339] repurposing the softban command for a ban command with deletion days 2021-10-25 01:11:32 +02:00
Sheldan
0514d355c7 [AB-167] adding warning created events and infraction counter
adding disabling of post targets
adding some logging for message sending failure consumer
2021-10-25 00:04:08 +02:00
Sheldan
8909e8ebe5 [AB-333] providing some dates as separate instants instead of relying on the member attributes
truncating date to day when displaying the date for export emote stats
fixing wrong message when confirming a command and adding missing exception handling
2021-10-14 01:31:52 +02:00
Sheldan
36ca9b11e4 [AB-334] adding vote result to suggestion reminder 2021-10-13 18:00:54 +02:00
Sheldan
4a66b7fc67 [AB-331] showing separate notifications for the decision on a suggestion 2021-09-28 22:20:11 +02:00
Sheldan
b2a94059e2 [AB-332] showing separate message in case there are no warnings 2021-09-28 22:05:51 +02:00
Sheldan
16e6caa1f0 [AB-196] adding confirmation requirement to various commands
refactoring command received handler in order to re-use when confirmation has been given
removing reaction from showSuggestion and remind command
adding log message for message deleted message, in case the member left
all commands from now on must work without the member field from the message, as this is not available when retrieving the message
2021-09-27 00:22:37 +02:00
Sheldan
da1a71ecdc [AB-319] splitting emotes into two separate fields (animated and static) for server info
adding emote display model
2021-09-10 00:41:15 +02:00
Sheldan
0646efe33d [AB-163] fixing printing all possible feature modes in case an unknown one was provided for enableMode/disableMode 2021-09-10 00:21:30 +02:00
Sheldan
db856f2647 [AB-xxx] improving documentation 2021-09-09 02:20:45 +02:00
Sheldan
1d85eb1e7e [AB-297] adding server names to invite deletion log (if available) 2021-09-08 23:04:35 +02:00
Sheldan
5e6999cd45 [AB-328] fixing case sensitivity of allowed invite links 2021-09-07 01:13:34 +02:00
Sheldan
9659a3487a [maven-release-plugin] prepare for next development iteration 2021-09-06 01:54:00 +02:00
Sheldan
6d27c487f2 [maven-release-plugin] prepare release abstracto-application-1.3.8 2021-09-06 01:53:55 +02:00
Sheldan
8ac3b327e4 [AB-268] adding button feature mode to suggestions which allows for hidden suggestion votes
moving gateway metric to separate service in case JDA is not ready yet
2021-09-06 01:39:27 +02:00
Sheldan
448332f24f [AB-318] adding a mention of the original message to link embed
updating JDA version
2021-09-04 16:30:16 +02:00
Sheldan
d7f889971d [AB-325] adding async command conditions - as this is required for some conditions
adding user parameter to immune user condition evaluation
2021-09-04 13:47:03 +02:00
Sheldan
69abff77fb [AB-xxx] fixing test 2021-08-21 16:05:01 +02:00
Sheldan
2c31fa1c1e [AB-xxx] not always creating a user instance in the experience listener
adding logging in case the warn decay notification fails
2021-08-21 15:50:11 +02:00
Sheldan
19a4858da1 [AB-323] improving logging when using whenComplete 2021-08-14 12:15:18 +02:00
Sheldan
3ed1f0c54a [AB-322] adding possibility to define a max age in days for messages to be eligible for stars 2021-08-14 09:45:25 +02:00
Sheldan
d01e46a9a6 [AB-xxx] fixing modmail sometimes failing to log messages 2021-08-14 08:02:04 +02:00
Sheldan
222250b795 [AB-321] changing caching policy
changing logging messages
2021-08-14 08:00:40 +02:00
Sheldan
9a87b75998 [maven-release-plugin] prepare for next development iteration 2021-08-09 23:52:49 +02:00
Sheldan
b16cef0d3c [maven-release-plugin] prepare release abstracto-application-1.3.7 2021-08-09 23:52:40 +02:00
Sheldan
cc55934ff2 [AB-xxx] fixing not removing component payloads for message embed cleanup job 2021-08-09 00:40:36 +02:00
Sheldan
168b4a52c8 [AB-xxx] changing some exception logging
fixing moderator member not re-used for reply command
2021-08-09 00:11:49 +02:00
Sheldan
73b5684a7e [maven-release-plugin] prepare for next development iteration 2021-07-26 01:37:38 +02:00
Sheldan
23ba9a88aa [maven-release-plugin] prepare release abstracto-application-1.3.6 2021-07-26 01:37:31 +02:00
Sheldan
a5664946e1 [AB-xxx] hotfix for component payload 2021-07-26 01:30:47 +02:00
Sheldan
ec16548cea [maven-release-plugin] prepare for next development iteration 2021-07-25 20:28:46 +02:00
Sheldan
9c9082034a [maven-release-plugin] prepare release abstracto-application-1.3.5 2021-07-25 20:28:37 +02:00
Sheldan
1486a0e9c3 [AB-314] forcing modmail messages being logged in certain order 2021-07-25 20:17:32 +02:00
Sheldan
533f1afcd5 [AB-313] removing restriction to string parameter for combined parameters 2021-07-25 17:28:42 +02:00
Sheldan
a6c3bb5aa2 [AB-313] ignoring referenced message parameter in parameter handler if not needed 2021-07-25 17:27:32 +02:00
Sheldan
5311cfcc2e [AB-xxx] fixing not showing assignable role actions in metrics 2021-07-25 15:18:54 +02:00
Sheldan
ee7f9180dc [AB-xxx] adding JDA metrics for all events
adding support to template environment variables in liquibase configuration
removing not needed column from component payload
2021-07-25 15:17:27 +02:00
Sheldan
3f67593ef4 [maven-release-plugin] prepare for next development iteration 2021-07-21 23:34:33 +02:00
Sheldan
0ccee3b211 [maven-release-plugin] prepare release abstracto-application-1.3.4 2021-07-21 23:34:25 +02:00
Sheldan
86b9ccb164 [AB-300] adding ability to determine the channel names for modmail 2021-07-21 01:38:04 +02:00
Sheldan
bc1eb0b55f [AB-305] fixing not correctly persisting removal of an assignable role from an assignable role place 2021-07-21 00:46:11 +02:00
Sheldan
92a8b5ba64 [AB-311] adding softban command 2021-07-20 02:02:42 +02:00
Sheldan
7535b2e66d [AB-xxx] make ban reason mandatory 2021-07-18 19:29:58 +02:00
Sheldan
7117ac26d3 [AB-308] adding a separate type for assignable role places to enable booster only places
adding more detailed logging to assignable roles
adding some fall through logic to the banned listener to always log at least the basic information
refactoring some command structure for showing configuration, so the command actually executes the message response
fixing potential exception case for starboard updates causing the message ID to not be persisted
2021-07-18 19:20:14 +02:00
Sheldan
32056cd6b9 [maven-release-plugin] prepare for next development iteration 2021-07-14 02:17:57 +02:00
Sheldan
efd6c23713 [maven-release-plugin] prepare release abstracto-application-1.3.3 2021-07-14 02:17:48 +02:00
Sheldan
794fc7ceac [AB-71] adding very simple anti raid feature to automatically mute member who mass mention users 2021-07-14 01:48:53 +02:00
Sheldan
6b5a255aa8 [maven-release-plugin] prepare for next development iteration 2021-07-13 01:19:43 +02:00
Sheldan
620ef0708a [maven-release-plugin] prepare release abstracto-application-1.3.2 2021-07-13 01:19:35 +02:00
Sheldan
3564075e7f [AB-xxx] fixing javadoc build 2021-07-13 01:13:58 +02:00
Sheldan
de5ac0e3f4 [maven-release-plugin] prepare for next development iteration 2021-07-13 00:56:29 +02:00
Sheldan
419ac3f994 [maven-release-plugin] prepare release abstracto-application-1.3.1 2021-07-13 00:56:20 +02:00
Sheldan
ac605e4791 [AB-303] adding scheduled configurable activity updates 2021-07-13 00:35:15 +02:00
Sheldan
c7514a6bad [AB-64] adding voice context channel feature 2021-07-12 00:56:15 +02:00
Sheldan
7e7591a4b3 [AB-302] refactoring assignable roles to use buttons instead of reactions
moved button related services to component service instead of message service
adding component type to component payload
renamed async role service methods
ignoring bot reactions in starboard
fixing rank not showing correct experience to next level for other members
2021-07-11 19:05:53 +02:00
Sheldan
c08134a150 [maven-release-plugin] prepare for next development iteration 2021-07-04 13:39:29 +02:00
Sheldan
c26bcc9a66 [maven-release-plugin] prepare release abstracto-application-1.3.0 2021-07-04 13:39:22 +02:00
Sheldan
bc276c0b12 [AB-299] fixing incorrect handling of ban notifications 2021-07-04 13:00:36 +02:00
Sheldan
18929c9a01 [AB-298] fixing various issues related to modmail:
fixing editing a message without messages but only embeds not possible in channel service
refactoring closing parameters to use an object instead of parameters always
adding a progress indicator to closing a modmail thread
adding a notification to contact in order to show where the thread was created
fixing configuration for category (this caused the setup to fail, because there was no default value) and threadMessage feature modes not being correct
refactored model for closing header and added additional information
refactored modmail message logging to use the message history instead of individually loading the messages
adding nicer exception in case the mod mail message update failed
adding creation of AUserInAServer in case the user did not interact on the server yet
changed ID of modmail thread to be identical to the channel it was created in, this is so we can load the channel easier
2021-07-04 12:14:04 +02:00
Sheldan
61eefd53c3 [AB-296] adding support for buttons
adding buttons for message embed via feature mode
2021-07-03 10:40:27 +02:00
Sheldan
bbc5918d88 [AB-xxx] upgrading to newer JDA version 2021-06-27 22:47:10 +02:00
Sheldan
67121b318d [AB-271] fixing tests 2021-06-27 19:57:38 +02:00
Sheldan
5a35137132 [AB-271] limiting certain arguments for commands to require the same server 2021-06-27 17:07:01 +02:00
Sheldan
e655adf95e [AB-295] adding hard limitations for field values, names, embed description and additional message lengths 2021-06-27 16:05:40 +02:00
Sheldan
ebf659fb14 [maven-release-plugin] prepare for next development iteration 2021-06-27 00:45:18 +02:00
Sheldan
8ef236ba19 [maven-release-plugin] prepare release abstracto-application-1.2.17 2021-06-27 00:45:09 +02:00
Sheldan
7e2477a321 [AB-xxx] adding support for automatic setup steps
reducing eventwaiter threads
adding abstracto templated exception
adding json validation service
increasing threads for scheduling
adding convenience method to retrieve users
2021-06-27 00:20:15 +02:00
Sheldan
942a20700e [AB-294] fixing test 2021-06-21 21:51:12 +02:00
Sheldan
0d25b9c384 [AB-294] fixing unmuting not cached members
adding logging and catches for unbanned listener
2021-06-21 21:47:23 +02:00
Sheldan
d5f572262f [AB-287] fixing members not being available breaking warning display converter 2021-06-09 12:56:01 +02:00
Sheldan
2c3b5fb651 [AB-290] fixing showing wrong active warning count in warn decay notification
fixing un mute log failing to render in case the mute did not come from a message
fixing notification failing with exception in case there were no warnings to decay
2021-06-09 12:18:36 +02:00
Sheldan
ba4d8f1da6 [AB-293] fixing wrong emote for 18 character 2021-06-09 12:02:37 +02:00
Sheldan
cd625a8907 [AB-292] fixing post target for suggestion reminder 2021-06-09 11:58:59 +02:00
Sheldan
51a835df06 [maven-release-plugin] prepare for next development iteration 2021-06-02 12:50:09 +02:00
Sheldan
78e226aa4c [maven-release-plugin] prepare release abstracto-application-1.2.16 2021-06-02 12:49:59 +02:00
Sheldan
82281cbd6c [AB-285] fixing interval of warn decay job
removing has example config from unban
enabling to change the configuration for cron scheduler job
adding HTTP method to okhttp logger
fixing unmute job parameters
changing to use cache when retrieving reactions for a message to cache it
2021-06-02 12:27:10 +02:00
Sheldan
432dc0ffae [maven-release-plugin] prepare for next development iteration 2021-06-02 00:45:59 +02:00
Sheldan
89a3afd10e [maven-release-plugin] prepare release abstracto-application-1.2.15 2021-06-02 00:45:51 +02:00
Sheldan
793a1c3657 [AB-284] resizing warn length and changing default date behaviour for mute 2021-06-02 00:36:36 +02:00
Sheldan
7fdb87ef1f [AB-284] adding warn decay notification
adding default mute reason
fixing incorrect muting user being stored
2021-06-01 23:38:06 +02:00
Sheldan
2f8e7c3947 [AB-282] adding okhttp interceptor to log requests 2021-06-01 21:44:03 +02:00
Sheldan
3bf761a2e5 [AB-281] adding ban and unban command
removing banId command in favor of the normal ban command
removing various feature modes for moderation for clearer configuration
2021-06-01 21:10:17 +02:00
Sheldan
55e0879e06 [AB-277] adding report mechanism via reactions 2021-05-30 21:00:49 +02:00
Sheldan
d69f597663 [AB-279] extending max level for experience gain to 150 2021-05-30 20:00:06 +02:00
Sheldan
148c25da4c [AB-275] fixing channel groups not being able to be found, because of upper/lowercase
adding performance improvements for experience listener
adding logging to when a user gains a level
fixing creating a user experience instance in the join listener, which was not persisted
2021-05-29 01:09:14 +02:00
Sheldan
f3bb9b9a69 [AB-276] enabling to cache more members in JDA to reduce requests 2021-05-29 00:56:02 +02:00
Sheldan
0366d48764 [maven-release-plugin] prepare for next development iteration 2021-05-26 21:51:42 +02:00
Sheldan
118b4c0e5d [maven-release-plugin] prepare release abstracto-application-1.2.14 2021-05-26 21:51:33 +02:00
Sheldan
95a639a733 [AB-272] improving leaderboard performance
fixing leaderboard returning wrong pages
making template cache update duration longer
2021-05-26 21:41:04 +02:00
Sheldan
919b52a607 [maven-release-plugin] prepare for next development iteration 2021-05-26 14:48:13 +02:00
Sheldan
a9229d4b28 [maven-release-plugin] prepare release abstracto-application-1.2.13 2021-05-26 14:48:06 +02:00
Sheldan
47ce03ac25 [AB-xxx] fixing version 2021-05-26 14:37:57 +02:00
Sheldan
cef96515aa Revert "[maven-release-plugin] prepare release abstracto-application-1.3.13"
This reverts commit 072d680e
2021-05-26 14:16:21 +02:00
Sheldan
23ac70bb69 [maven-release-plugin] prepare for next development iteration 2021-05-26 13:52:20 +02:00
Sheldan
072d680e2f [maven-release-plugin] prepare release abstracto-application-1.3.13 2021-05-26 13:52:11 +02:00
Sheldan
04b3e073aa [AB-270] fixing test 2021-05-26 11:51:45 +02:00
Sheldan
08e2a31f15 [AB-270] adding a channel group to define the channels in which experience gain is disabled
adding a service method to enable migration
2021-05-26 11:29:34 +02:00
Sheldan
184271ff7c [AB-247/AB-252] fixing issue with set experience role failing to consider the correct experience roles
adding levelRoles command
ignoring bots from experience gain
adding a bit more logging to user experience handling
2021-05-26 01:22:37 +02:00
Sheldan
c4fe73b3f8 [AB-251] adding optional member parameter to rank command to show the rank of another member 2021-05-25 00:24:07 +02:00
Sheldan
2dc73ef3b9 [AB-267] fixing some combinations of reaction characters being ignored 2021-05-24 19:05:41 +02:00
Sheldan
2dd02cdae8 [AB-266] fixing online status and activity not showing up in userinfo 2021-05-24 18:32:41 +02:00
Sheldan
d39ee4359f [AB-231] fixing parameter letting less than required commands to the commands, in case the parameters were not able to be parsed (expected emote got string), the check for parameter count as only done before actually parsing the parameters 2021-05-24 18:16:39 +02:00
Sheldan
d7cc7f579f [AB-254] fixing exception for unSuggest in case the suggestion message has been deleted 2021-05-24 17:53:58 +02:00
Sheldan
7d30afbd2c [AB-262] adding feature mode to suggestion to be automatically reminded of a suggestion after a configurable amount of time 2021-05-24 17:39:19 +02:00
Sheldan
f4c1dcb27f [maven-release-plugin] prepare for next development iteration 2021-05-23 23:26:29 +02:00
Sheldan
fda5262df0 [maven-release-plugin] prepare release abstracto-application-1.2.12 2021-05-23 23:26:21 +02:00
Sheldan
dc2479889a [AB-xxx] making regex checks ignore case 2021-05-23 23:17:29 +02:00
Sheldan
1fbd494590 [AB-xxx] fixing issues related to emote tracking 2021-05-23 22:27:52 +02:00
Sheldan
9dc1d73507 [AB-264] fixing profanity filter not considering message edits 2021-05-23 17:10:54 +02:00
Sheldan
23af59ab9d [AB-261] fixing invite filter only acting in the message received event 2021-05-23 16:45:36 +02:00
Sheldan
13a6e1fdca [AB-263] adding stricter not null checks to database, disabling updates/inserts for created and updated columns to only rely on triggers 2021-05-23 14:17:03 +02:00
Sheldan
04a7cfafd7 [AB-84] adding profanity filter with different feature modes
react command: adding additional mapping for character r
2021-05-16 23:44:50 +02:00
Sheldan
eca9e6ebf7 [AB-258] improving handling of request failures for urban dictionary 2021-05-12 15:38:57 +02:00
Sheldan
d4edbb0d94 [maven-release-plugin] prepare for next development iteration 2021-05-12 00:51:17 +02:00
951 changed files with 23498 additions and 5813 deletions

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020 Sheldan
Copyright (c) 2022 Sheldan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

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.2.1_262
* [JDA](https://github.com/DV8FromTheWorld/JDA/) The Discord API Wrapper in the version 4.3.0_315
* [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

@@ -0,0 +1,54 @@
<?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>anti-raid</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.11</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>anti-raid-impl</artifactId>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>core-int</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>anti-raid-int</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<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>
</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,10 @@
package dev.sheldan.abstracto.antiraid.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource("classpath:antiRaid-config.properties")
public class AntiRaidProperties {
}

View File

@@ -0,0 +1,36 @@
package dev.sheldan.abstracto.antiraid.listener;
import dev.sheldan.abstracto.antiraid.config.AntiRaidFeatureDefinition;
import dev.sheldan.abstracto.antiraid.service.MassPingService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class MassPingMessageListener implements AsyncMessageReceivedListener {
@Autowired
private MassPingService massPingService;
@Override
public DefaultListenerResult execute(MessageReceivedModel model) {
Message message = model.getMessage();
if(message.getAuthor().isBot() || message.isWebhookMessage() || !message.isFromGuild() || !message.isFromType(ChannelType.TEXT)) {
return DefaultListenerResult.IGNORED;
}
massPingService.processMessage(message);
return DefaultListenerResult.PROCESSED;
}
@Override
public FeatureDefinition getFeature() {
return AntiRaidFeatureDefinition.ANTI_RAID;
}
}

View File

@@ -0,0 +1,113 @@
package dev.sheldan.abstracto.antiraid.service;
import dev.sheldan.abstracto.antiraid.config.AntiRaidPostTarget;
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.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.moderation.service.MuteService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class MassPingServiceBean implements MassPingService {
private static final String MASS_PING_MUTE_NOTIFICATION_TEMPLATE_KEY = "massPing_mute_notification";
private static final String LEVEL_CONDITION_NAME = "HAS_LEVEL";
private static final String LEVEL_CONDITION_USER_ID_PARAMETER = "userId";
private static final String LEVEL_CONDITION_LEVEL_PARAMETER = "level";
private static final String LEVEL_CONDITION_SERVER_PARAMETER = "serverId";
@Autowired
private ConfigService configService;
@Autowired
private MuteService muteService;
@Autowired
private TemplateService templateService;
@Autowired
private MassPingServiceBean self;
@Autowired
private PostTargetService postTargetService;
@Autowired
private ConditionService conditionService;
@Value("${abstracto.massPing.maxAllowedMentions}")
private Integer maxAllowedMentions;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Override
public CompletableFuture<Void> processMessage(Message message) {
if(message.getMentionedMembers().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())
.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));
} 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());
return CompletableFuture.completedFuture(null);
}
} else {
return CompletableFuture.completedFuture(null);
}
}
private boolean allowedToMassMention(Message message, Integer level) {
log.info("Checking if member {} is allowed to mention a lot of users in server {}.", message.getAuthor().getIdLong(), message.getGuild().getIdLong());
Map<String, Object> parameters = new HashMap<>();
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(message.getMember());
parameters.put(LEVEL_CONDITION_USER_ID_PARAMETER, userInAServer.getUserInServerId());
parameters.put(LEVEL_CONDITION_LEVEL_PARAMETER, level);
parameters.put(LEVEL_CONDITION_SERVER_PARAMETER, message.getGuild().getIdLong());
ConditionContextInstance contextInstance = ConditionContextInstance
.builder()
.conditionName(LEVEL_CONDITION_NAME)
.parameters(parameters)
.build();
SystemCondition.Result result = conditionService.checkConditions(contextInstance);
return SystemCondition.Result.consideredSuccessful(result);
}
@Transactional
public CompletableFuture<Void> sendMassPingMuteNotification(Message message) {
Member member = message.getMember();
MassPingNotificationModel model = MassPingNotificationModel
.builder()
.messageLink(message.getJumpUrl())
.mentionCount(message.getMentionedMembers().size())
.messageContent(message.getContentRaw())
.memberDisplay(MemberDisplay.fromMember(member))
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(MASS_PING_MUTE_NOTIFICATION_TEMPLATE_KEY, model, member.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, AntiRaidPostTarget.MASS_PING_LOG, member.getGuild().getIdLong()));
}
}

View File

@@ -0,0 +1,9 @@
abstracto.featureFlags.antiRaid.featureName=antiRaid
abstracto.featureFlags.antiRaid.enabled=false
abstracto.postTargets.massPingLog.name=massPingLog
abstracto.massPing.maxAllowedMentions=5
abstracto.systemConfigs.massPingMinLevel.name=massPingMinLevel
abstracto.systemConfigs.massPingMinLevel.longValue=15

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

View File

@@ -0,0 +1,9 @@
<?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" >
</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.3.3/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,31 @@
<?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>anti-raid</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.3.11</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>anti-raid-int</artifactId>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>core-int</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>moderation-int</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,39 @@
package dev.sheldan.abstracto.antiraid.config;
import dev.sheldan.abstracto.antiraid.service.MassPingService;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.PostTargetEnum;
import dev.sheldan.abstracto.moderation.config.feature.MutingFeatureConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class AntiRaidFeatureConfig implements FeatureConfig {
@Autowired
private MutingFeatureConfig mutingFeatureConfig;
@Override
public FeatureDefinition getFeature() {
return AntiRaidFeatureDefinition.ANTI_RAID;
}
@Override
public List<PostTargetEnum> getRequiredPostTargets() {
return Arrays.asList(AntiRaidPostTarget.MASS_PING_LOG);
}
@Override
public List<String> getRequiredSystemConfigKeys() {
return Arrays.asList(MassPingService.MAX_AFFECTED_LEVEL_KEY);
}
@Override
public List<FeatureConfig> getRequiredFeatures() {
return Arrays.asList(mutingFeatureConfig);
}
}

View File

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

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.antiraid.config;
import dev.sheldan.abstracto.core.config.PostTargetEnum;
import lombok.Getter;
@Getter
public enum AntiRaidPostTarget implements PostTargetEnum {
MASS_PING_LOG("massPingLog");
private String key;
AntiRaidPostTarget(String key) {
this.key = key;
}
}

View File

@@ -0,0 +1,16 @@
package dev.sheldan.abstracto.antiraid.model;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class MassPingNotificationModel {
private MemberDisplay memberDisplay;
private String messageContent;
private String messageLink;
private Integer mentionCount;
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.antiraid.service;
import net.dv8tion.jda.api.entities.Message;
import java.util.concurrent.CompletableFuture;
public interface MassPingService {
String MAX_AFFECTED_LEVEL_KEY = "massPingMinLevel";
CompletableFuture<Void> processMessage(Message message);
}

View File

@@ -0,0 +1,17 @@
<?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.3.11</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>anti-raid</artifactId>
<packaging>pom</packaging>
<modules>
<module>anti-raid-int</module>
<module>anti-raid-impl</module>
</modules>
</project>

View File

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

View File

@@ -16,6 +16,7 @@ import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* Command used to activate an {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace place}
@@ -30,23 +31,24 @@ public class ActivateAssignableRolePlace extends AbstractConditionableCommand {
private ServerManagementService serverManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
AServer server = serverManagementService.loadServer(commandContext.getGuild());
service.activateAssignableRolePlace(server, name);
return CommandResult.fromSuccess();
return service.activateAssignableRolePlace(server, name)
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName);
Parameter placeName = Parameter.builder().name("name").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(placeName);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("activateAssignableRolePlace")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)

View File

@@ -1,7 +1,8 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRoleConditionType;
import dev.sheldan.abstracto.assignableroles.service.AssignableRoleConditionService;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
@@ -9,52 +10,44 @@ 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.models.FullEmote;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import net.dv8tion.jda.api.entities.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
/**
* Command used to set the position of one individual {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRole assignableRole}
* within an {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace place}
*/
@Component
public class SetAssignableRolePosition extends AbstractConditionableCommand {
public class AddAssignableRoleCondition extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Autowired
private ServerManagementService serverManagementService;
private AssignableRoleConditionService assignableRoleConditionService;
@Override
public CommandResult execute(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
FullEmote emote = (FullEmote) parameters.get(1);
Integer newPosition = (Integer) parameters.get(2);
AServer server = serverManagementService.loadServer(commandContext.getGuild());
service.setEmoteToPosition(server, name, emote, newPosition);
Role role = (Role) parameters.get(1);
AssignableRoleConditionType configKey = (AssignableRoleConditionType) parameters.get(2);
String parameterValue = (String) parameters.get(3);
assignableRoleConditionService.createAssignableRoleCondition(name, role, configKey, parameterValue);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter emote = Parameter.builder().name("emote").type(FullEmote.class).templated(true).build();
Parameter newPosition = Parameter.builder().name("newPosition").type(Integer.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName, emote, newPosition);
Parameter placeName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter role = Parameter.builder().name("role").type(Role.class).templated(true).build();
Parameter conditionKey = Parameter.builder().name("conditionKey").type(AssignableRoleConditionType.class).templated(true).build();
Parameter conditionValue = Parameter.builder().name("conditionParameter").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(placeName, role, conditionKey, conditionValue);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("setAssignableRolePosition")
.name("addAssignableRoleCondition")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();

View File

@@ -1,7 +1,6 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.exception.AssignableRoleAlreadyDefinedException;
import dev.sheldan.abstracto.assignableroles.exception.AssignableRoleNotUsableException;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
@@ -12,10 +11,10 @@ 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.models.FullEmote;
import dev.sheldan.abstracto.core.models.FullRole;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.RoleService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import net.dv8tion.jda.api.entities.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -43,27 +42,31 @@ public class AddRoleToAssignableRolePost extends AbstractConditionableCommand {
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
FullEmote emote = (FullEmote) parameters.get(1);
String description = (String) parameters.get(2);
FullRole role = (FullRole) parameters.get(3);
Role role = (Role) parameters.get(1);
String description = null;
if (parameters.size() > 2) {
description = (String) parameters.get(2);
}
FullEmote emote = null;
if(parameters.size() > 3) {
emote = (FullEmote) parameters.get(3);
}
AServer server = serverManagementService.loadServer(commandContext.getGuild());
if(service.hasAssignableRolePlaceEmote(server, name, emote.getFakeEmote())) {
throw new AssignableRoleAlreadyDefinedException(emote, name);
// already used check via role and assignable role place name
if(!roleService.canBotInteractWithRole(role)) {
throw new AssignableRoleNotUsableException(role);
}
if(!roleService.canBotInteractWithRole(role.getRole())) {
throw new AssignableRoleNotUsableException(role, commandContext.getGuild());
}
return service.addRoleToAssignableRolePlace(server, name, role.getRole(), emote, description)
return service.addRoleToAssignableRolePlace(server, name, role, emote, description)
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter emote = Parameter.builder().name("emote").type(FullEmote.class).templated(true).build();
Parameter description = Parameter.builder().name("description").type(String.class).templated(true).build();
Parameter role = Parameter.builder().name("role").type(FullRole.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName, emote, description, role);
Parameter placeName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter role = Parameter.builder().name("role").type(Role.class).templated(true).build();
Parameter rolePostName = Parameter.builder().name("displayText").type(String.class).templated(true).build();
Parameter emote = Parameter.builder().name("emote").type(FullEmote.class).optional(true).templated(true).build();
List<Parameter> parameters = Arrays.asList(placeName, role, rolePostName, emote);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("addRoleToAssignableRolePlace")

View File

@@ -39,7 +39,7 @@ public class ChangeAssignableRolePlaceConfig extends AbstractConditionableComman
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
AssignableRolePlaceParameterKey configKey = (AssignableRolePlaceParameterKey) parameters.get(1);
Object parameterValue = parameters.get(2);
String parameterValue = (String) parameters.get(2);
AServer server = serverManagementService.loadServer(commandContext.getGuild());
return service.changeConfiguration(server, name, configKey, parameterValue)
.thenApply(aVoid -> CommandResult.fromSuccess());
@@ -49,7 +49,7 @@ public class ChangeAssignableRolePlaceConfig extends AbstractConditionableComman
public CommandConfiguration getConfiguration() {
Parameter assignableRolePlaceName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter parameterKey = Parameter.builder().name("key").type(AssignableRolePlaceParameterKey.class).templated(true).build();
Parameter parameterValue = Parameter.builder().name("value").type(Object.class).templated(true).build();
Parameter parameterValue = Parameter.builder().name("value").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(assignableRolePlaceName, parameterKey, parameterValue);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();

View File

@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
@@ -12,6 +13,7 @@ import dev.sheldan.abstracto.core.command.config.validator.MaxStringLengthValida
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.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;
@@ -40,8 +42,15 @@ public class CreateAssignableRolePost extends AbstractConditionableCommand {
String name = (String) parameters.get(0);
TextChannel channel = (TextChannel) parameters.get(1);
String text = (String) parameters.get(2);
AssignableRolePlaceType type = AssignableRolePlaceType.DEFAULT;
if(parameters.size() > 3) {
type = (AssignableRolePlaceType) parameters.get(3);
}
if(!channel.getGuild().equals(commandContext.getGuild())) {
throw new EntityGuildMismatchException();
}
AChannel chosenChannel = channelManagementService.loadChannel(channel.getIdLong());
service.createAssignableRolePlace(name, chosenChannel, text);
service.createAssignableRolePlace(name, chosenChannel, text, type);
return CommandResult.fromSuccess();
}
@@ -50,10 +59,11 @@ public class CreateAssignableRolePost extends AbstractConditionableCommand {
List<ParameterValidator> rolePlaceNameValidator = Arrays.asList(MaxStringLengthValidator.max(AssignableRolePlace.ASSIGNABLE_PLACE_NAME_LIMIT));
Parameter rolePostName = Parameter.builder().name("name").validators(rolePlaceNameValidator).type(String.class).templated(true).build();
Parameter channel = Parameter.builder().name("channel").type(TextChannel.class).templated(true).build();
Parameter type = Parameter.builder().name("type").type(AssignableRolePlaceType.class).templated(true).optional(true).build();
List<ParameterValidator> rolePlaceDescriptionValidator = Arrays.asList(MaxStringLengthValidator.max(AssignableRolePlace.ASSIGNABLE_PLACE_NAME_LIMIT));
Parameter text = Parameter.builder().name("text").validators(rolePlaceDescriptionValidator).type(String.class).remainder(true).optional(true).templated(true).build();
Parameter text = Parameter.builder().name("text").validators(rolePlaceDescriptionValidator).type(String.class).templated(true).build();
List<String> aliases = Arrays.asList("crRPl", "crAssRoPl");
List<Parameter> parameters = Arrays.asList(rolePostName, channel, text);
List<Parameter> parameters = Arrays.asList(rolePostName, channel, text, type);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("createAssignableRolePlace")

View File

@@ -16,6 +16,7 @@ import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* Command used to deactive an {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace place}
@@ -30,12 +31,12 @@ public class DeactivateAssignableRolePlace extends AbstractConditionableCommand
private ServerManagementService serverManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
AServer server = serverManagementService.loadServer(commandContext.getGuild());
service.deactivateAssignableRolePlace(server, name);
return CommandResult.fromSuccess();
return service.deactivateAssignableRolePlace(server, name)
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
@@ -47,6 +48,7 @@ public class DeactivateAssignableRolePlace extends AbstractConditionableCommand
.name("deactivateAssignableRolePlace")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)

View File

@@ -50,6 +50,7 @@ public class DeleteAssignableRolePlace extends AbstractConditionableCommand {
.templated(true)
.causesReaction(true)
.async(true)
.requiresConfirmation(true)
.supportsEmbedException(true)
.parameters(parameters)
.help(helpInfo)

View File

@@ -9,6 +9,8 @@ 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.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 org.springframework.beans.factory.annotation.Autowired;
@@ -16,6 +18,7 @@ import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* Command used to move an {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace place}
@@ -31,12 +34,16 @@ public class MoveAssignableRolePlace extends AbstractConditionableCommand {
private ServerManagementService serverManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
TextChannel newChannel = (TextChannel) parameters.get(1);
placeManagementService.moveAssignableRolePlace(name, newChannel);
return CommandResult.fromSuccess();
if(!newChannel.getGuild().equals(commandContext.getGuild())) {
throw new EntityGuildMismatchException();
}
AServer server = serverManagementService.loadServer(commandContext.getGuild());
return placeManagementService.moveAssignableRolePlace(server, name, newChannel)
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
@@ -50,6 +57,7 @@ public class MoveAssignableRolePlace extends AbstractConditionableCommand {
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.async(true)
.supportsEmbedException(true)
.parameters(parameters)
.help(helpInfo)

View File

@@ -1,7 +1,8 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRoleConditionType;
import dev.sheldan.abstracto.assignableroles.service.AssignableRoleConditionService;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
@@ -9,49 +10,42 @@ 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.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import net.dv8tion.jda.api.entities.Role;
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;
/**
* Command used to show how an {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace place}
* would look like in the current {@link net.dv8tion.jda.api.entities.TextChannel channel}
*/
@Component
public class TestAssignableRolePlace extends AbstractConditionableCommand {
public class RemoveAssignableRoleCondition extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Autowired
private ServerManagementService serverManagementService;
private AssignableRoleConditionService assignableRoleConditionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
public CommandResult execute(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
AServer server = serverManagementService.loadServer(commandContext.getGuild());
return service.testAssignableRolePlace(server, name, commandContext.getChannel())
.thenApply(aVoid -> CommandResult.fromIgnored());
Role role = (Role) parameters.get(1);
AssignableRoleConditionType configKey = (AssignableRoleConditionType) parameters.get(2);
assignableRoleConditionService.deleteAssignableRoleCondition(name, role, configKey);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName);
Parameter placeName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter role = Parameter.builder().name("role").type(Role.class).templated(true).build();
Parameter conditionKey = Parameter.builder().name("conditionKey").type(AssignableRoleConditionType.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(placeName, role, conditionKey);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("testAssignableRolePlace")
.name("removeAssignableRoleCondition")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();

View File

@@ -9,7 +9,7 @@ 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.models.FullEmote;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -36,17 +36,17 @@ public class RemoveRoleFromAssignableRolePlace extends AbstractConditionableComm
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
FullEmote emote = (FullEmote) parameters.get(1);
ARole role = (ARole) parameters.get(1);
AServer server = serverManagementService.loadServer(commandContext.getGuild());
return service.removeRoleFromAssignableRolePlace(server, name, emote)
return service.removeRoleFromAssignableRolePlace(server, name, role)
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter emote = Parameter.builder().name("emote").type(FullEmote.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName, emote);
Parameter role = Parameter.builder().name("role").type(ARole.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName, role);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("removeRoleFromAssignableRolePlace")

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.model.template.AssignableRolePlaceConfig;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
@@ -9,8 +10,9 @@ 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.models.database.AServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -24,21 +26,24 @@ import java.util.concurrent.CompletableFuture;
@Component
public class ShowAssignableRolePlaceConfig extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private ChannelService channelService;
public static final String ASSIGNABLE_ROLES_CONFIG_POST_TEMPLATE_KEY = "assignable_roles_config_post";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
AServer server = serverManagementService.loadServer(commandContext.getGuild());
// TODO refactor to return something to be posted in this command here instead of relying it to be posted somewhere else
return service.showAssignablePlaceConfig(server, name, commandContext.getChannel())
.thenApply(unused -> CommandResult.fromIgnored());
AssignableRolePlaceConfig config = service.getAssignableRolePlaceConfig(commandContext.getGuild(), name);
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(ASSIGNABLE_ROLES_CONFIG_POST_TEMPLATE_KEY, config, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
@@ -51,7 +56,7 @@ public class ShowAssignableRolePlaceConfig extends AbstractConditionableCommand
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.async(true)
.causesReaction(true)
.causesReaction(false)
.supportsEmbedException(true)
.parameters(parameters)
.help(helpInfo)

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.model.template.AssignablePlaceOverview;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
@@ -9,7 +10,9 @@ 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.models.database.AServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -28,11 +31,16 @@ public class ShowAssignableRolePlaces extends AbstractConditionableCommand {
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private ChannelService channelService;
public static final String ASSIGNABLE_ROLE_PLACES_OVERVIEW_TEMPLATE_KEY = "assignable_role_places_overview";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
AServer server = serverManagementService.loadServer(commandContext.getGuild());
return service.showAllAssignableRolePlaces(server, commandContext.getChannel())
.thenApply(aVoid -> CommandResult.fromIgnored());
AssignablePlaceOverview model = service.getAssignableRolePlaceOverview(commandContext.getGuild());
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(ASSIGNABLE_ROLE_PLACES_OVERVIEW_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
@@ -42,7 +50,6 @@ public class ShowAssignableRolePlaces extends AbstractConditionableCommand {
.name("showAssignableRolePlaces")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.async(true)
.supportsEmbedException(true)
.help(helpInfo)

View File

@@ -1,81 +0,0 @@
package dev.sheldan.abstracto.assignableroles.command;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.exception.EmoteNotInAssignableRolePlaceException;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
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.models.FullEmote;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
/**
* Command used to swap the positions of two {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRole roles}
* within one {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace place}
*/
@Component
public class SwapAssignableRolePosition extends AbstractConditionableCommand {
@Autowired
private AssignableRolePlaceService service;
@Autowired
private EmoteService emoteService;
@Autowired
private ServerManagementService serverManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
FullEmote firstEmote = (FullEmote) parameters.get(1);
FullEmote secondEmote = (FullEmote) parameters.get(2);
AServer server = serverManagementService.loadServer(commandContext.getGuild());
if(emoteService.compareAEmote(firstEmote.getFakeEmote(), secondEmote.getFakeEmote())) {
return CommandResult.fromError("You cannot swap the same emote");
}
if(!service.hasAssignableRolePlaceEmote(server, name, firstEmote.getFakeEmote())) {
throw new EmoteNotInAssignableRolePlaceException(firstEmote, name);
}
if(!service.hasAssignableRolePlaceEmote(server, name, firstEmote.getFakeEmote())) {
throw new EmoteNotInAssignableRolePlaceException(secondEmote, name);
}
service.swapPositions(server, name, firstEmote, secondEmote);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter rolePostName = Parameter.builder().name("name").type(String.class).templated(true).build();
Parameter firstEmote = Parameter.builder().name("firstEmote").type(FullEmote.class).templated(true).build();
Parameter secondEmote = Parameter.builder().name("secondEmote").type(FullEmote.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(rolePostName, firstEmote, secondEmote);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("swapAssignableRolePosition")
.module(AssignableRoleModuleDefinition.ASSIGNABLE_ROLES)
.templated(true)
.causesReaction(true)
.supportsEmbedException(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return AssignableRoleFeatureDefinition.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,91 @@
package dev.sheldan.abstracto.assignableroles.condition;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRoleConditionType;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRoleMinLevelModel;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRolePlaceConditionModel;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRoleMinLevelResult;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRoleCondition;
import dev.sheldan.abstracto.assignableroles.model.template.condition.AssignableRoleConditionDisplay;
import dev.sheldan.abstracto.assignableroles.model.template.condition.AssignableRoleMinLevelDisplay;
import dev.sheldan.abstracto.core.models.ConditionContextInstance;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.display.RoleDisplay;
import dev.sheldan.abstracto.core.service.ConditionService;
import dev.sheldan.abstracto.core.service.SystemCondition;
import net.dv8tion.jda.api.entities.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
public class AssignableRoleMinimumLevelConditionImpl implements AssignableRoleConditionEvaluator {
private final String conditionName = "HAS_LEVEL";
private final String userIdParameter = "userId";
private final String levelParameter = "level";
private final String serverParameter = "serverId";
@Autowired
private ConditionService conditionService;
@Override
public boolean fulfillsCondition(AssignableRoleCondition conditionDefinition, AUserInAServer aUserInAServer) {
Integer level = parseLevel(conditionDefinition);
Map<String, Object> parameters = new HashMap<>();
parameters.put(userIdParameter, aUserInAServer.getUserInServerId());
parameters.put(levelParameter, level);
parameters.put(serverParameter, aUserInAServer.getServerReference().getId());
ConditionContextInstance contextInstance = ConditionContextInstance
.builder()
.conditionName(conditionName)
.parameters(parameters)
.build();
SystemCondition.Result result = conditionService.checkConditions(contextInstance);
return SystemCondition.Result.consideredSuccessful(result);
}
@Override
public boolean usableValue(String value) {
try {
parseLevelValue(value);
return true;
} catch (Exception exception) {
return false;
}
}
private Integer parseLevel(AssignableRoleCondition conditionDefinition) {
return parseLevelValue(conditionDefinition.getConditionValue());
}
private int parseLevelValue(String value) {
return Integer.parseInt(value);
}
@Override
public AssignableRolePlaceConditionModel createNotificationModel(AssignableRoleCondition conditionDefinition, Role role) {
Integer level = parseLevel(conditionDefinition);
AssignableRoleMinLevelModel model = AssignableRoleMinLevelModel
.builder()
.minLevel(level)
.roleDisplay(RoleDisplay.fromRole(role))
.build();
return AssignableRoleMinLevelResult
.builder()
.model(model)
.build();
}
@Override
public AssignableRoleConditionDisplay getConditionDisplay(AssignableRoleCondition conditionDefinition) {
return new AssignableRoleMinLevelDisplay(Integer.parseInt(conditionDefinition.getConditionValue()));
}
@Override
public boolean handlesCondition(AssignableRoleConditionType type) {
return AssignableRoleConditionType.MIN_LEVEL.equals(type);
}
}

View File

@@ -1,47 +0,0 @@
package dev.sheldan.abstracto.assignableroles.listener;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlacePost;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlacePostManagementService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageDeletedListener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageDeletedModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
@Slf4j
public class AssignablePostDeletedListener implements AsyncMessageDeletedListener {
@Autowired
private AssignableRolePlacePostManagementService service;
@Override
public FeatureDefinition getFeature() {
return AssignableRoleFeatureDefinition.ASSIGNABLE_ROLES;
}
/**
* This method deletes one individual {@link AssignableRolePlacePost post}, because its message has been deleted
* @param model The {@link MessageDeletedModel message} containing the {@link CachedMessage cachedMessage} which was deleted
*/
@Override
public DefaultListenerResult execute(MessageDeletedModel model) {
Optional<AssignableRolePlacePost> messageOptional = service.findByMessageIdOptional(model.getCachedMessage().getMessageId());
messageOptional.ifPresent(post -> {
AssignableRolePlace assignablePlace = post.getAssignablePlace();
log.info("Post {} has been deleted in server {} in channel {}, we are removing a post from place {}.",
post.getId(), model.getServerId(), model.getCachedMessage().getChannelId(), assignablePlace.getKey());
post.getAssignableRoles().forEach(assignableRole -> assignableRole.setAssignableRolePlacePost(null));
assignablePlace.getMessagePosts().remove(post);
});
return DefaultListenerResult.PROCESSED;
}
}

View File

@@ -1,175 +0,0 @@
package dev.sheldan.abstracto.assignableroles.listener;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlacePost;
import dev.sheldan.abstracto.assignableroles.model.database.AssignedRoleUser;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceService;
import dev.sheldan.abstracto.assignableroles.service.AssignableRoleServiceBean;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRoleManagementService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlaceManagementService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlacePostManagementService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignedRoleUserManagementService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionAddedListener;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedReaction;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.ReactionAddedModel;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.ReactionService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.MessageReaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class AssignablePostReactionAdded implements AsyncReactionAddedListener {
@Autowired
private AssignableRolePlacePostManagementService service;
@Autowired
private AssignableRoleServiceBean assignableRoleServiceBean;
@Autowired
private EmoteService emoteService;
@Autowired
private AssignableRolePlaceService assignableRolePlaceService;
@Autowired
private AssignedRoleUserManagementService assignedRoleUserManagementService;
@Autowired
private AssignableRolePlaceManagementService assignableRolePlaceManagementService;
@Autowired
private AssignablePostReactionAdded self;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private AssignableRoleManagementService assignableRoleManagementService;
@Autowired
private ReactionService reactionService;
@Autowired
private MemberService memberService;
/**
* Iterates over all {@link AssignableRole assignableRoles} of the post and checks which {@link AssignableRole assignableRole}
* is identified by the added {@link MessageReaction reaction}. If there is no valid reaction, the {@link net.dv8tion.jda.api.entities.MessageReaction reaction}
* will be removed again. In case the {@link AssignableRolePlace place} is configured to have unique roles, this will remove the existing
* {@link net.dv8tion.jda.api.entities.MessageReaction reaction} and the assigned {@link net.dv8tion.jda.api.entities.Role role}.
* Afterwards the appropriate {@link net.dv8tion.jda.api.entities.Role role} will be added and the update
* will be stored in the database.
* @param assignablePlacePost The {@link AssignableRolePlacePost post} onto which the {@link MessageReaction reaction} was added to
* @param model The {@link ReactionAddedModel model} containing information about who added which reaction where
*/
private void addAppropriateRoles(AssignableRolePlacePost assignablePlacePost, ReactionAddedModel model) {
boolean validReaction = false;
AssignableRolePlace assignableRolePlace = assignablePlacePost.getAssignablePlace();
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (AssignableRole assignableRole : assignablePlacePost.getAssignableRoles()) {
log.debug("Checking emote {} if it was reaction for assignable role place.", assignableRole.getEmote().getId());
if (emoteService.isReactionEmoteAEmote(model.getReaction().getReactionEmote(), assignableRole.getEmote())) {
if(assignableRolePlace.getUniqueRoles()) {
log.debug("Assignable role place {} has unique roles configured. Removing existing reactions and roles.", assignableRolePlace.getId());
Optional<AssignedRoleUser> byUserInServer = assignedRoleUserManagementService.findByUserInServerOptional(model.getUserReacting());
byUserInServer.ifPresent(user -> futures.add(assignableRolePlaceService.removeExistingReactionsAndRoles(assignableRolePlace, user)));
}
Long assignableRoleId = assignableRole.getId();
log.info("User added {} reaction {} and gets assignable role {} in server {}.", model.getUserReacting().getUserId(), assignableRole.getEmote().getId(), assignableRoleId, model.getServerId());
CompletableFuture<Void> roleAdditionFuture = assignableRoleServiceBean.assignAssignableRoleToUser(assignableRoleId, model.getUserReacting());
futures.add(CompletableFuture.allOf(roleAdditionFuture));
validReaction = true;
break;
}
}
if(!validReaction) {
log.debug("Reaction was not found in the configuration of assignable role place {}, removing reaction.", assignableRolePlace.getId());
futures.add(reactionService.removeReactionFromMessage(model.getReaction(), model.getMessage(), model.getMemberReacting().getUser()));
}
Long assignableRolePlaceId = assignableRolePlace.getId();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(aVoid ->
self.updateStoredAssignableRoles(assignableRolePlaceId, model.getUserReacting(), model.getReaction())
).exceptionally(throwable -> {
log.error("Failed to add role or remove emote for assignable role place {}.", assignableRolePlaceId, throwable);
return null;
});
}
/**
* Persists the {@link AssignableRole role} changes for the user who added a reaction in the database
* @param assignableRolePlaceId The ID of the {@link AssignableRolePlace place}
* @param serverUser The {@link ServerUser serverUser} who added the {@link net.dv8tion.jda.api.entities.MessageReaction reaction}
* @param reaction The {@link CachedReaction reaction} wich was added
*/
@Transactional
public void updateStoredAssignableRoles(Long assignableRolePlaceId, ServerUser serverUser, MessageReaction reaction) {
AssignableRolePlace place = assignableRolePlaceManagementService.findByPlaceId(assignableRolePlaceId);
AUserInAServer userInAServer = userInServerManagementService.loadOrCreateUser(serverUser);
if(place.getUniqueRoles()) {
log.debug("Assignable role place {} has unique roles. Deleting all existing references.", assignableRolePlaceId);
assignableRoleServiceBean.clearAllRolesOfUserInPlace(place, userInAServer);
}
AssignableRole role = assignableRoleManagementService.getRoleForReactionEmote(reaction.getReactionEmote(), place);
log.info("Adding role to assignable role {} to user {} in server {}.", role.getId(), userInAServer.getUserReference().getId(), userInAServer.getServerReference().getId());
assignableRoleServiceBean.addRoleToUser(role.getId(), userInAServer);
}
@Override
public FeatureDefinition getFeature() {
return AssignableRoleFeatureDefinition.ASSIGNABLE_ROLES;
}
/**
* Determines if the {@link net.dv8tion.jda.api.entities.Message message} a reaction was added to, belongs to a
* {@link AssignableRolePlacePost post}.
* If the {@link AssignableRolePlacePost post} belong to an inactive {@link AssignableRolePlace place} this method
* will automatically remove the reaction, self reactions are ignored. Otherwise the logic according to the configuration
* of the {@link AssignableRolePlace place} will be executed.
* @param model The {@link ReactionAddedModel message} which contains information about the added reaction
*/
@Override
public DefaultListenerResult execute(ReactionAddedModel model) {
MessageReaction reaction = model.getReaction();
Optional<AssignableRolePlacePost> messageOptional = service.findByMessageIdOptional(model.getMessage().getMessageId());
if(messageOptional.isPresent()) {
AssignableRolePlacePost assignablePlacePost = messageOptional.get();
if(reaction.isSelf()) {
log.info("Ignoring self reaction on assignable role post in server {}.", model.getServerId());
return DefaultListenerResult.IGNORED;
}
Long assignableRolePlacePostId = assignablePlacePost.getId();
if(assignablePlacePost.getAssignablePlace().getActive()) {
log.info("User {} added reaction to assignable role place {} in server {}. Handling added event.", model.getUserReacting().getUserId(), assignablePlacePost.getId(), model.getServerId());
addAppropriateRoles(assignablePlacePost, model);
} else {
reactionService.removeReactionFromMessage(model.getReaction(), model.getMessage()).exceptionally(throwable -> {
log.error("Failed to remove reaction on place post {} because place is inactive.", assignableRolePlacePostId, throwable);
return null;
});
log.debug("Reaction for assignable place {} in sever {} was added, but place is inactive.", assignablePlacePost.getAssignablePlace().getKey(), model.getServerId());
}
}
return DefaultListenerResult.PROCESSED;
}
}

View File

@@ -1,81 +0,0 @@
package dev.sheldan.abstracto.assignableroles.listener;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlacePost;
import dev.sheldan.abstracto.assignableroles.service.AssignableRoleService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlacePostManagementService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionRemovedListener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.ReactionRemovedModel;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.RoleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
@Slf4j
public class AssignablePostReactionRemoved implements AsyncReactionRemovedListener {
@Autowired
private AssignableRolePlacePostManagementService service;
@Autowired
private EmoteService emoteService;
@Autowired
private RoleService roleService;
@Autowired
private AssignableRoleService assignableRoleService;
@Override
public FeatureDefinition getFeature() {
return AssignableRoleFeatureDefinition.ASSIGNABLE_ROLES;
}
/**
* Determines if the {@link net.dv8tion.jda.api.entities.Message message} a reaction was removed from, belongs to a
* {@link AssignableRolePlacePost post}.
* If the {@link AssignableRolePlacePost post} belong to an inactive {@link AssignableRolePlace place} this method ignores the removal.
* Otherwise the logic according to the configuration
* of the {@link AssignableRolePlace place} will be executed.
* @param model The {@link ReactionRemovedModel model} containing the information which reaction was placed where
*/
@Override
public DefaultListenerResult execute(ReactionRemovedModel model) {
CachedMessage message = model.getMessage();
Optional<AssignableRolePlacePost> messageOptional = service.findByMessageIdOptional(message.getMessageId());
if(messageOptional.isPresent()) {
AssignableRolePlacePost assignablePlacePost = messageOptional.get();
if(assignablePlacePost.getAssignablePlace().getActive()) {
assignablePlacePost.getAssignableRoles().forEach(assignableRole -> {
if(emoteService.isReactionEmoteAEmote(model.getReaction().getReactionEmote(), assignableRole.getEmote())) {
Long assignableRoleId = assignableRole.getId();
log.info("Removing assignable role {} for user {} in server {} from assignable role place {}.", assignableRoleId,
model.getUserRemoving().getUserId(), model.getServerId(), assignablePlacePost.getAssignablePlace().getId());
assignableRoleService.fullyRemoveAssignableRoleFromUser(assignableRole, model.getUserRemoving()).exceptionally(throwable -> {
log.error("Failed to remove assignable role {} from user {} in server {}.", assignableRoleId, model.getUserRemoving().getUserId(), model.getServerId(), throwable);
return null;
});
}
});
return DefaultListenerResult.PROCESSED;
} else {
log.debug("Reaction for assignable place {} in sever {} was added, but place is inactive.", assignablePlacePost.getAssignablePlace().getKey(), model.getServerId());
return DefaultListenerResult.PROCESSED;
}
} else {
return DefaultListenerResult.IGNORED;
}
}
}

View File

@@ -0,0 +1,234 @@
package dev.sheldan.abstracto.assignableroles.listener;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.exception.AssignableRoleNotFoundException;
import dev.sheldan.abstracto.assignableroles.exception.BoosterAssignableRolePlaceMemberNotBoostingException;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRoleConditionResult;
import dev.sheldan.abstracto.assignableroles.model.AssignableRolePlacePayload;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRolePlaceConditionModel;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.assignableroles.model.database.AssignedRoleUser;
import dev.sheldan.abstracto.assignableroles.model.template.AssignableRoleSuccessNotificationModel;
import dev.sheldan.abstracto.assignableroles.service.AssignableRoleConditionServiceBean;
import dev.sheldan.abstracto.assignableroles.service.AssignableRolePlaceServiceBean;
import dev.sheldan.abstracto.assignableroles.service.AssignableRoleService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlaceManagementService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignedRoleUserManagementServiceBean;
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.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.listener.ButtonClickedListenerModel;
import dev.sheldan.abstracto.core.service.RoleService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.events.interaction.ButtonClickEvent;
import net.dv8tion.jda.api.interactions.components.ButtonInteraction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Component
@Slf4j
public class AssignableRoleButtonClickedListener implements ButtonClickedListener {
@Autowired
private AssignableRolePlaceManagementService assignableRolePlaceManagementService;
@Autowired
private RoleService roleService;
@Autowired
private AssignableRoleButtonClickedListener self;
@Autowired
private InteractionService interactionService;
@Autowired
private AssignableRoleService assignableRoleService;
@Autowired
private AssignedRoleUserManagementServiceBean assignedRoleUserManagementServiceBean;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private AssignableRoleConditionServiceBean assignableRoleConditionServiceBean;
@Override
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
ButtonClickEvent event = model.getEvent();
Member member = event.getMember();
if(event.getGuild() != null && member != null) {
AssignableRolePlacePayload payload = (AssignableRolePlacePayload) model.getDeserializedPayload();
AssignableRolePlace place = assignableRolePlaceManagementService.findByPlaceId(payload.getPlaceId());
Guild guild = event.getGuild();
List<Role> removedRoles = new ArrayList<>();
Role roleById = guild.getRoleById(payload.getRoleId());
Optional<AssignableRole> assignableRoleOptional = place
.getAssignableRoles()
.stream()
.filter(assignableRole -> assignableRole.getRole().getId().equals(payload.getRoleId()))
.findFirst();
if(!assignableRoleOptional.isPresent()) {
throw new AssignableRoleNotFoundException(payload.getRoleId());
}
if(roleById != null) {
boolean memberHasRole = member
.getRoles()
.stream()
.anyMatch(memberRole -> memberRole.getIdLong() == payload.getRoleId());
if(!memberHasRole) {
if(place.getType().equals(AssignableRolePlaceType.BOOSTER) && member.getTimeBoosted() == null) {
assignableRoleService.assignableRoleConditionFailure();
throw new BoosterAssignableRolePlaceMemberNotBoostingException();
}
AssignableRole assignableRole = assignableRoleOptional.get();
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
if(!assignableRole.getConditions().isEmpty()) {
log.debug("Evaluating {} conditions for assignable role {}.", assignableRole.getConditions().size(), assignableRole.getId());
AssignableRoleConditionResult conditionResult =
assignableRoleConditionServiceBean.evaluateConditions(assignableRole.getConditions(), aUserInAServer, roleById);
if(!conditionResult.getFulfilled()) {
log.info("One condition failed to be fulfilled - notifying user.");
self.notifyUserAboutConditionFail(model, event.getInteraction(), conditionResult.getModel());
assignableRoleService.assignableRoleConditionFailure();
return ButtonClickedListenerResult.ACKNOWLEDGED;
}
}
CompletableFuture<Void> removalFuture;
if(place.getUniqueRoles()) {
Optional<AssignedRoleUser> assignedRoleUserOptional = assignedRoleUserManagementServiceBean.findByUserInServerOptional(aUserInAServer);
if(assignedRoleUserOptional.isPresent()) {
AssignedRoleUser roleUser = assignedRoleUserOptional.get();
List<Role> rolesToRemove = roleUser
.getRoles()
.stream()
.filter(roleOfUser -> roleOfUser.getAssignablePlace().equals(place))
.map(roleOfUser -> guild.getRoleById(roleOfUser.getRole().getId()))
.filter(Objects::nonNull)
.collect(Collectors.toList());
log.info("Removing {} because of unique role configuration in place {}.", rolesToRemove.size(), place.getId());
removedRoles.addAll(rolesToRemove);
List<CompletableFuture<Void>> removalFutures = new ArrayList<>();
rolesToRemove.forEach(roleToRemove -> removalFutures.add(roleService.removeRoleFromUserAsync(member, roleToRemove)));
removalFuture = new CompletableFutureList<>(removalFutures).getMainFuture();
} else {
removalFuture = CompletableFuture.completedFuture(null);
}
} else {
removalFuture = CompletableFuture.completedFuture(null);
}
CompletableFuture<Void> roleAdditionFuture = assignableRoleService.assignAssignableRoleToUser(roleById, member);
CompletableFuture.allOf(removalFuture, roleAdditionFuture).whenComplete((unused, throwable) -> {
if(throwable != null) {
log.error("Failed to either add or remove roles for assignable role place {} in server {}.", payload.getPlaceId(), guild.getIdLong());
}
if(!roleAdditionFuture.isCompletedExceptionally()) {
log.info("Added role {} to member {} in server {} for assignable role interaction {} on component {}.",
roleById.getId(), member.getId(), guild.getIdLong(), event.getInteraction().getId(), event.getComponentId());
self.notifyUser(model, true, roleById, event.getInteraction(), removedRoles).thenAccept(unused1 -> {
log.info("Persisting adding assignable role update for user {} in server {} of role {}.", member.getIdLong(), guild.getIdLong(), roleById.getId());
self.persistAssignableUser(member, payload, false);
});
}
}).exceptionally(throwable -> {
log.error("Failed to perform role change in assignable role place.", throwable);
return null;
});
} else {
assignableRoleService.removeAssignableRoleFromUser(roleById, member)
.thenAccept(unused -> {
self.notifyUser(model, false, roleById, event.getInteraction(), new ArrayList<>());
log.info("Removed role {} from member {} in server {} for assignable role interaction {} on component {}.",
roleById.getId(), member.getId(), guild.getIdLong(), event.getInteraction().getId(), event.getComponentId());
}).thenAccept(unused -> {
log.info("Persisting remove assignable role update for user {} in server {} of role {}.", member.getIdLong(), guild.getIdLong(), roleById.getId());
self.persistAssignableUser(member, payload, true);
});
}
} else {
log.warn("Role {} is not available to be assigned in assignable role place {} in server {}. Component {} failed.",
payload.getRoleId(), payload.getPlaceId(), guild.getIdLong(), event.getComponentId());
throw new AssignableRoleNotFoundException(payload.getRoleId());
}
}
return ButtonClickedListenerResult.ACKNOWLEDGED;
}
@Transactional
public void persistAssignableUser(Member member, AssignableRolePlacePayload payload, boolean removeRole){
AssignableRolePlace place = assignableRolePlaceManagementService.findByPlaceId(payload.getPlaceId());
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
if(place.getUniqueRoles() && !removeRole) {
assignableRoleService.clearAllRolesOfUserInPlace(place, aUserInAServer);
}
Optional<AssignableRole> assignableRoleOptional = place
.getAssignableRoles()
.stream()
.filter(assignableRole -> assignableRole.getRole().getId().equals(payload.getRoleId()))
.findFirst();
if(assignableRoleOptional.isPresent()) {
if(removeRole) {
assignableRoleService.removeRoleFromUser(assignableRoleOptional.get(), aUserInAServer);
} else {
assignableRoleService.addRoleToUser(assignableRoleOptional.get(), aUserInAServer);
}
}
}
@Transactional
public CompletableFuture<Void> notifyUser(ButtonClickedListenerModel model, boolean roleAdded, Role role, ButtonInteraction buttonInteraction, List<Role> removedRoles) {
log.info("Notifying user {} in server {} in channel {} about role change with role {}.",
buttonInteraction.getUser().getIdLong(), buttonInteraction.getGuild().getIdLong(), buttonInteraction.getChannel().getIdLong(), role.getId());
AssignableRoleSuccessNotificationModel notificationModel = AssignableRoleSuccessNotificationModel
.builder()
.added(roleAdded)
.removedRoles(removedRoles)
.role(role)
.build();
return FutureUtils.toSingleFutureGeneric(
interactionService.sendMessageToInteraction("assignable_role_success_notification", notificationModel, buttonInteraction.getHook())) ;
}
@Transactional
public CompletableFuture<Void> notifyUserAboutConditionFail(ButtonClickedListenerModel model, ButtonInteraction buttonInteraction,
AssignableRolePlaceConditionModel conditionModel) {
log.info("Notifying user {} in server {} in channel {} about failed condition.", buttonInteraction.getUser().getIdLong(),
buttonInteraction.getGuild().getIdLong(), buttonInteraction.getChannel().getIdLong());
return FutureUtils.toSingleFutureGeneric(
interactionService.sendMessageToInteraction("assignable_role_condition_notification", conditionModel, buttonInteraction.getHook())) ;
}
@Override
public Boolean handlesEvent(ButtonClickedListenerModel model) {
return AssignableRolePlaceServiceBean.ASSIGNABLE_ROLE_COMPONENT_ORIGIN.equals(model.getOrigin());
}
@Override
public FeatureDefinition getFeature() {
return AssignableRoleFeatureDefinition.ASSIGNABLE_ROLES;
}
@Override
public Integer getPriority() {
return ListenerPriority.MEDIUM;
}
}

View File

@@ -0,0 +1,112 @@
package dev.sheldan.abstracto.assignableroles.listener;
import dev.sheldan.abstracto.assignableroles.config.AssignableRoleFeatureDefinition;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.assignableroles.model.database.AssignedRoleUser;
import dev.sheldan.abstracto.assignableroles.service.AssignableRoleService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRoleManagementService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignedRoleUserManagementService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMemberBoostTimeUpdateListener;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.listener.BoostTimeUpdatedModel;
import dev.sheldan.abstracto.core.service.RoleService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Component
@Slf4j
public class AssignableRolePlaceBoostTimeUpdateListener implements AsyncMemberBoostTimeUpdateListener {
@Autowired
private AssignedRoleUserManagementService assignedRoleUserManagementService;
@Autowired
private AssignableRoleManagementService assignableRoleManagementService;
@Autowired
private RoleService roleService;
@Autowired
private AssignableRoleService assignableRoleService;
@Autowired
private AssignableRolePlaceBoostTimeUpdateListener self;
@Override
public DefaultListenerResult execute(BoostTimeUpdatedModel model) {
Member member = model.getMember();
if(member.getTimeBoosted() == null) {
removeAssignedBoosterRoles(member);
return DefaultListenerResult.PROCESSED;
}
return DefaultListenerResult.IGNORED;
}
private void removeAssignedBoosterRoles(Member member) {
log.info("Member {} in server {} stopped boosting.", member.getIdLong(), member.getGuild().getIdLong());
ServerUser serverUser = ServerUser.fromMember(member);
Optional<AssignedRoleUser> assignedRoleUserOptional = assignedRoleUserManagementService.findByUserInServerOptional(serverUser);
if(assignedRoleUserOptional.isPresent()) {
AssignedRoleUser assignedRoleUser = assignedRoleUserOptional.get();
List<AssignableRole> boosterRoles = assignableRoleManagementService.getAssignableRolesFromAssignableUserWithPlaceType(assignedRoleUser, AssignableRolePlaceType.BOOSTER);
if(!boosterRoles.isEmpty()) {
log.info("Removing {} assignable role mappings.", boosterRoles.size());
Guild guild = member.getGuild();
List<Role> actualRolesToDelete = boosterRoles
.stream()
.map(assignableRole -> guild.getRoleById(assignableRole.getRole().getId()))
.filter(Objects::nonNull)
.collect(Collectors.toList());
log.debug("Which translated to {} roles in reality.", actualRolesToDelete.size());
List<CompletableFuture<Void>> list = new ArrayList<>();
actualRolesToDelete.forEach(role -> list.add(roleService.removeRoleFromUserAsync(member, role)));
FutureUtils.toSingleFutureGeneric(list)
.thenAccept(unused -> self.clearPersistedBoosterAssignableRoles(member))
.exceptionally(throwable -> {
log.warn("One or more roles might have failed to remove. ", throwable);
self.clearPersistedBoosterAssignableRoles(member);
return null;
});
} else {
log.info("Member {} in server {} did not have boost roles - doing nothing.", member.getIdLong(), member.getGuild().getIdLong());
}
} else {
log.info("Member (ID {}) in server (ID: {}), who was not tracked via assignable roles, stopped boosting - doing nothing.",
member.getIdLong(), member.getGuild().getIdLong());
}
}
@Transactional
public void clearPersistedBoosterAssignableRoles(Member member) {
ServerUser serverUser = ServerUser.fromMember(member);
Optional<AssignedRoleUser> assignedRoleUserOptional = assignedRoleUserManagementService.findByUserInServerOptional(serverUser);
if(assignedRoleUserOptional.isPresent()) {
AssignedRoleUser assignedRoleUser = assignedRoleUserOptional.get();
List<AssignableRole> boosterRoles = assignableRoleManagementService.getAssignableRolesFromAssignableUserWithPlaceType(assignedRoleUser, AssignableRolePlaceType.BOOSTER);
assignableRoleService.removeAssignableRolesFromAssignableRoleUser(boosterRoles, assignedRoleUser);
} else {
log.warn("No assigned role user found for member {} in server {}.", member.getIdLong(), member.getGuild().getIdLong());
}
}
@Override
public FeatureDefinition getFeature() {
return AssignableRoleFeatureDefinition.ASSIGNABLE_ROLES;
}
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.abstracto.assignableroles.model;
import dev.sheldan.abstracto.core.models.template.button.ButtonPayload;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class AssignableRolePlacePayload implements ButtonPayload {
private Long placeId;
private Long roleId;
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.abstracto.assignableroles.repository;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRoleConditionType;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRoleCondition;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface AssignableRoleConditionRepository extends JpaRepository<AssignableRoleCondition, Long> {
Optional<AssignableRoleCondition> findByAssignableRoleAndType(AssignableRole assignableRole, AssignableRoleConditionType type);
}

View File

@@ -1,12 +0,0 @@
package dev.sheldan.abstracto.assignableroles.repository;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlacePost;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* Repository to manage the access to the table managed by {@link AssignableRolePlacePost posts}
*/
@Repository
public interface AssignableRolePlacePostRepository extends JpaRepository<AssignableRolePlacePost, Long> {
}

View File

@@ -1,12 +1,17 @@
package dev.sheldan.abstracto.assignableroles.repository;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.assignableroles.model.database.AssignedRoleUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* Repository to manage the access to the table managed by {@link AssignableRole assignableRole}
*/
@Repository
public interface AssignableRoleRepository extends JpaRepository<AssignableRole, Long> {
List<AssignableRole> findByAssignedUsersContainingAndAssignablePlace_Type(AssignedRoleUser roleUser, AssignableRolePlaceType type);
}

View File

@@ -0,0 +1,132 @@
package dev.sheldan.abstracto.assignableroles.service;
import dev.sheldan.abstracto.assignableroles.condition.AssignableRoleConditionEvaluator;
import dev.sheldan.abstracto.assignableroles.exception.AssignableRoleConditionAlreadyExistsException;
import dev.sheldan.abstracto.assignableroles.exception.AssignableRoleConditionValueNotUsableException;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRoleConditionResult;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRoleConditionType;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRoleCondition;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.exception.AssignableRoleConditionDoesNotExistException;
import dev.sheldan.abstracto.assignableroles.model.template.condition.AssignableRoleConditionDisplay;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRoleConditionManagementService;
import dev.sheldan.abstracto.assignableroles.service.management.AssignableRolePlaceManagementService;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
@Component
@Slf4j
public class AssignableRoleConditionServiceBean implements AssignableRoleConditionService {
@Autowired
private List<AssignableRoleConditionEvaluator> assignableRoleConditionEvaluators;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private AssignableRolePlaceManagementService assignableRolePlaceManagementService;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private AssignableRoleService assignableRoleService;
@Autowired
private AssignableRoleConditionManagementService assignableRoleConditionManagementService;
@Override
public AssignableRoleConditionResult evaluateConditions(List<AssignableRoleCondition> conditions, Member member, Role role) {
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
return evaluateConditions(conditions, aUserInAServer, role);
}
@Override
public AssignableRoleConditionResult evaluateConditions(List<AssignableRoleCondition> conditions, AUserInAServer aUserInAServer, Role role) {
log.debug("Evaluating {} conditions for role {}.", conditions.size(), role.getId());
for (AssignableRoleCondition condition : conditions) {
if(assignableRoleConditionEvaluators != null) {
Optional<AssignableRoleConditionEvaluator> evaluatorOptional = findEvaluatorForCondition(condition.getType());
if(evaluatorOptional.isPresent()) {
AssignableRoleConditionEvaluator evaluator = evaluatorOptional.get();
log.debug("Evaluating condition {} with evaluator {}.", condition.getType(), evaluator.getClass());
if(!evaluator.fulfillsCondition(condition, aUserInAServer)) {
log.info("Condition {} failed for role {} in server {}.", condition.getType(), role.getId(), aUserInAServer.getServerReference().getId());
return AssignableRoleConditionResult.fromFail(condition.getType(), evaluator.createNotificationModel(condition, role));
}
}
}
}
return AssignableRoleConditionResult.fromSuccess();
}
private Optional<AssignableRoleConditionEvaluator> findEvaluatorForCondition(AssignableRoleConditionType type) {
return assignableRoleConditionEvaluators
.stream()
.filter(assignableRoleConditionEvaluator -> assignableRoleConditionEvaluator.handlesCondition(type))
.findFirst();
}
@Override
public AssignableRoleCondition createAssignableRoleCondition(String placeName, Role role, AssignableRoleConditionType type, String value) {
AServer server = serverManagementService.loadServer(role.getGuild());
AssignableRolePlace place = assignableRolePlaceManagementService.findByServerAndKey(server, placeName);
AssignableRole assignableRole = assignableRoleService.getAssignableRoleInPlace(place, role);
Optional<AssignableRoleConditionEvaluator> evaluatorOptional = findEvaluatorForCondition(type);
if(!evaluatorOptional.isPresent()) {
throw new AssignableRoleConditionDoesNotExistException();
}
evaluatorOptional.ifPresent(evaluator -> {
boolean valueUsable = evaluator.usableValue(value);
if(!valueUsable) {
throw new AssignableRoleConditionValueNotUsableException();
}
});
if(assignableRoleConditionManagementService.findAssignableRoleCondition(assignableRole, type).isPresent()) {
throw new AssignableRoleConditionAlreadyExistsException();
}
log.info("Creating new condition for role {} in place {} in server {}.", place.getId(), role.getId(), role.getGuild().getIdLong());
return assignableRoleConditionManagementService.createAssignableRoleCondition(assignableRole, type, value);
}
@Override
public void deleteAssignableRoleCondition(String placeName, Role role, AssignableRoleConditionType type) {
AServer server = serverManagementService.loadServer(role.getGuild());
AssignableRolePlace place = assignableRolePlaceManagementService.findByServerAndKey(server, placeName);
AssignableRole assignableRole = assignableRoleService.getAssignableRoleInPlace(place, role);
Optional<AssignableRoleCondition> existingCondition = assignableRoleConditionManagementService.findAssignableRoleCondition(assignableRole, type);
if(!existingCondition.isPresent()) {
throw new AssignableRoleConditionDoesNotExistException();
}
log.info("Deleting assignable role condition on place {} for role {} in server {}.", place.getId(), role.getId(), role.getGuild().getIdLong());
existingCondition.ifPresent(condition -> assignableRoleConditionManagementService.deleteAssignableRoleCondition(condition));
}
@Override
public List<AssignableRoleConditionDisplay> getConditionDisplays(List<AssignableRoleCondition> conditions) {
return conditions.stream().map(condition -> {
Optional<AssignableRoleConditionEvaluator> evaluatorOptional = findEvaluatorForCondition(condition.getType());
if(evaluatorOptional.isPresent()) {
AssignableRoleConditionEvaluator evaluator = evaluatorOptional.get();
return evaluator.getConditionDisplay(condition);
}
return null;
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
}

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.assignableroles.service;
import dev.sheldan.abstracto.assignableroles.exception.AssignableRoleNotFoundException;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignedRoleUser;
@@ -9,19 +10,22 @@ import dev.sheldan.abstracto.assignableroles.service.management.AssignedRoleUser
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.metric.service.MetricTag;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.RoleService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@@ -68,44 +72,70 @@ public class AssignableRoleServiceBean implements AssignableRoleService {
.tagList(Arrays.asList(MetricTag.getTag(ACTION, "removed")))
.build();
private static final CounterMetric ASSIGNABLE_ROLES_CONDITION_FAILED =
CounterMetric
.builder()
.name(ASSIGNABLE_ROLES_METRIC)
.tagList(Arrays.asList(MetricTag.getTag(ACTION, "condition")))
.build();
@Override
public CompletableFuture<Void> assignAssignableRoleToUser(Long assignableRoleId, Member member) {
metricService.incrementCounter(ASSIGNABLE_ROLES_ASSIGNED);
AssignableRole role = assignableRoleManagementServiceBean.getByAssignableRoleId(assignableRoleId);
log.info("Assigning role {} to member {} in server {}.", assignableRoleId, member.getId(), member.getGuild().getId());
return roleService.addRoleToMemberFuture(member, role.getRole());
metricService.incrementCounter(ASSIGNABLE_ROLES_ASSIGNED);
return roleService.addRoleToMemberAsync(member, role.getRole());
}
@Override
public CompletableFuture<Void> assignAssignableRoleToUser(Long assignableRoleId, ServerUser serverUser) {
return memberService.retrieveMemberInServer(serverUser).thenCompose(member -> assignAssignableRoleToUser(assignableRoleId, member));
public CompletableFuture<Void> assignAssignableRoleToUser(Role role, Member member) {
return assignRoleToUser(role.getIdLong(), member);
}
@Override
public void assignableRoleConditionFailure() {
metricService.incrementCounter(ASSIGNABLE_ROLES_CONDITION_FAILED);
}
private CompletableFuture<Void> assignRoleToUser(Long roleId, Member member) {
metricService.incrementCounter(ASSIGNABLE_ROLES_ASSIGNED);
return roleService.addRoleToMemberAsync(member, roleId);
}
@Override
public void clearAllRolesOfUserInPlace(AssignableRolePlace place, AUserInAServer userInAServer) {
AssignedRoleUser user = assignedRoleUserManagementServiceBean.findByUserInServer(userInAServer);
log.info("Clearing all {} assignable roles in place {} for user {} in server {}.",
user.getRoles().size(), place.getId(), userInAServer.getUserReference().getId(), userInAServer.getServerReference().getId());
user.getRoles().forEach(assignableRole -> {
if(assignableRole.getAssignablePlace().equals(place)) {
assignableRole.getAssignedUsers().remove(user);
}
Optional<AssignedRoleUser> userOptional = assignedRoleUserManagementServiceBean.findByUserInServerOptional(userInAServer);
userOptional.ifPresent(assignedRoleUser -> {
log.info("Clearing all {} assignable roles in place {} for user {} in server {}.",
assignedRoleUser.getRoles().size(), place.getId(), userInAServer.getUserReference().getId(), userInAServer.getServerReference().getId());
assignedRoleUser.getRoles().forEach(assignableRole -> {
if(assignableRole.getAssignablePlace().equals(place)) {
assignableRole.getAssignedUsers().remove(assignedRoleUser);
}
});
assignedRoleUser.getRoles().removeIf(assignableRole -> assignableRole.getAssignablePlace().equals(place));
});
user.getRoles().removeIf(assignableRole -> assignableRole.getAssignablePlace().equals(place));
}
@Override
public CompletableFuture<Void> fullyAssignAssignableRoleToUser(Long assignableRoleId, Member toAdd) {
return this.assignAssignableRoleToUser(assignableRoleId, toAdd).thenAccept(aVoid ->
self.addRoleToUser(assignableRoleId, toAdd)
);
if(!userOptional.isPresent()) {
log.info("User {} was not yet stored as an assignable role user in server {} - nothing to clear.",
userInAServer.getUserReference().getId(), place.getServer().getId());
}
}
@Override
public CompletableFuture<Void> removeAssignableRoleFromUser(AssignableRole assignableRole, Member member) {
log.info("Removing assignable role {} from user {} in server {}.", assignableRole.getId(), member.getId(), member.getGuild().getId());
return removeRoleFromUser(assignableRole.getRole().getId(), member);
}
@Override
public CompletableFuture<Void> removeAssignableRoleFromUser(Role role, Member member) {
return removeRoleFromUser(role.getIdLong(), member);
}
private CompletableFuture<Void> removeRoleFromUser(Long roleId, Member member) {
metricService.incrementCounter(ASSIGNABLE_ROLES_REMOVED);
return roleService.removeRoleFromMemberAsync(member, assignableRole.getRole());
return roleService.removeRoleFromMemberAsync(member, roleId);
}
@Override
@@ -115,30 +145,6 @@ public class AssignableRoleServiceBean implements AssignableRoleService {
return self.removeAssignableRoleFromUser(role, member);
}
@Override
public CompletableFuture<Void> removeAssignableRoleFromUser(AssignableRole assignableRole, AUserInAServer aUserInAServer) {
Long assignableRoleId = assignableRole.getId();
return memberService.getMemberInServerAsync(aUserInAServer).thenCompose(member ->
self.removeAssignableRoleFromUser(assignableRoleId, member)
);
}
@Override
public CompletableFuture<Void> fullyRemoveAssignableRoleFromUser(AssignableRole assignableRole, ServerUser serverUser) {
Long assignableRoleId = assignableRole.getId();
return memberService.retrieveMemberInServer(serverUser).thenCompose(member ->
this.removeAssignableRoleFromUser(assignableRole, member)
.thenAccept(aVoid -> self.persistRoleRemovalFromUser(assignableRoleId, member))
);
}
@Transactional
@Override
public void addRoleToUser(Long assignableRoleId, AUserInAServer aUserInAServer) {
AssignableRole role = assignableRoleManagementServiceBean.getByAssignableRoleId(assignableRoleId);
addRoleToUser(role, aUserInAServer);
}
@Override
public void addRoleToUser(AssignableRole assignableRole, AUserInAServer aUserInAServer) {
log.info("Persisting storing adding assignable role {} to user {} in server {}.",
@@ -153,44 +159,37 @@ public class AssignableRoleServiceBean implements AssignableRoleService {
assignedRoleUserManagementServiceBean.removeAssignedRoleFromUser(assignableRole, aUserInAServer);
}
/**
* Adds the given {@link AssignableRole assignableRole} identified by the ID to the given {@link Member member}
* @param assignableRoleId The ID of the {@link AssignableRole} to be added to the {@link Member member}
* @param member The {@link Member member} to add the {@link AssignableRole role} to
*/
@Transactional
public void addRoleToUser(Long assignableRoleId, Member member) {
AssignableRole role = assignableRoleManagementServiceBean.getByAssignableRoleId(assignableRoleId);
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
addRoleToUser(role, aUserInAServer);
@Override
public AssignableRole getAssignableRoleInPlace(AssignableRolePlace place, Role role) {
return getAssignableRoleInPlace(place, role.getIdLong());
}
/**
* Removes the given {@link AssignableRole assignableRole} identified by the ID from the given {@link AUserInAServer user}
* @param assignableRoleId The ID of the {@link AssignableRole} to be removed from the {@link Member member}
* @param aUserInAServer The {@link AUserInAServer user} to remove the {@link AssignableRole role} from
*/
@Transactional
public void removeRoleFromUser(Long assignableRoleId, AUserInAServer aUserInAServer) {
AssignableRole role = assignableRoleManagementServiceBean.getByAssignableRoleId(assignableRoleId);
removeRoleFromUser(role, aUserInAServer);
@Override
public AssignableRole getAssignableRoleInPlace(AssignableRolePlace place, ARole role) {
return getAssignableRoleInPlace(place, role.getId());
}
/**
* Stores the removal of an {@link AssignableRole assignableRole} from a {@link Member member} in the database
* @param assignableRoleId The ID of the {@link AssignableRole} to be removed from the {@link Member member}
* @param member The {@link Member member} which should get the {@link AssignableRole role} removed
*/
@Transactional
public void persistRoleRemovalFromUser(Long assignableRoleId, Member member) {
AssignableRole role = assignableRoleManagementServiceBean.getByAssignableRoleId(assignableRoleId);
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
removeRoleFromUser(role, aUserInAServer);
@Override
public AssignableRole getAssignableRoleInPlace(AssignableRolePlace place, Long roleId) {
for (AssignableRole assignableRole : place.getAssignableRoles()) {
if (assignableRole.getRole().getId().equals(roleId)) {
return assignableRole;
}
}
throw new AssignableRoleNotFoundException(roleId);
}
@Override
public void removeAssignableRolesFromAssignableRoleUser(List<AssignableRole> roles, AssignedRoleUser roleUser) {
log.info("Removing {} assignable roles from user {} in server {}.", roles.size(), roleUser.getUser().getUserReference().getId(),
roleUser.getUser().getServerReference().getId());
roles.forEach(assignableRole -> assignedRoleUserManagementServiceBean.removeAssignedRoleFromUser(assignableRole, roleUser));
}
@PostConstruct
public void postConstruct() {
metricService.registerCounter(ASSIGNABLE_ROLES_ASSIGNED, "Assignable roles assigned.");
metricService.registerCounter(ASSIGNABLE_ROLES_REMOVED, "Assignable roles removed.");
metricService.registerCounter(ASSIGNABLE_ROLES_CONDITION_FAILED, "Assignable roles failed to assign because of condition.");
}
}

View File

@@ -0,0 +1,43 @@
package dev.sheldan.abstracto.assignableroles.service.management;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRoleConditionType;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRoleCondition;
import dev.sheldan.abstracto.assignableroles.repository.AssignableRoleConditionRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
@Slf4j
public class AssignableRoleConditionManagementServiceBean implements AssignableRoleConditionManagementService {
@Autowired
private AssignableRoleConditionRepository repository;
@Override
public AssignableRoleCondition createAssignableRoleCondition(AssignableRole assignableRole, AssignableRoleConditionType type, String value) {
AssignableRoleCondition condition = AssignableRoleCondition
.builder()
.assignableRole(assignableRole)
.type(type)
.conditionValue(value)
.build();
log.info("Creating condition of type {} for assignable role {}", assignableRole.getId(), type);
assignableRole.getConditions().add(condition);
return repository.save(condition);
}
@Override
public void deleteAssignableRoleCondition(AssignableRoleCondition condition) {
log.info("Deleting condition {}.", condition.getId());
repository.delete(condition);
}
@Override
public Optional<AssignableRoleCondition> findAssignableRoleCondition(AssignableRole role, AssignableRoleConditionType type) {
return repository.findByAssignableRoleAndType(role, type);
}
}

View File

@@ -1,22 +1,24 @@
package dev.sheldan.abstracto.assignableroles.service.management;
import dev.sheldan.abstracto.assignableroles.exception.AssignableRolePlaceNotFoundException;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRole;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlacePost;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.assignableroles.model.database.AssignedRoleUser;
import dev.sheldan.abstracto.assignableroles.repository.AssignableRoleRepository;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.cache.CachedEmote;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.FullEmote;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.ComponentPayload;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.MessageReaction;
import net.dv8tion.jda.api.entities.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@Slf4j
public class AssignableRoleManagementServiceBean implements AssignableRoleManagementService {
@@ -30,9 +32,6 @@ public class AssignableRoleManagementServiceBean implements AssignableRoleManage
@Autowired
private RoleManagementService roleManagementService;
@Autowired
private AssignableRolePlacePostManagementService postManagementService;
@Autowired
private AssignableRoleRepository repository;
@@ -40,72 +39,37 @@ public class AssignableRoleManagementServiceBean implements AssignableRoleManage
private EmoteService emoteService;
@Override
public AssignableRole addRoleToPlace(AssignableRolePlace place, AEmote emote, ARole role, String description, AssignableRolePlacePost post) {
Integer maxPosition = place.getAssignableRoles().stream().map(AssignableRole::getPosition).max(Integer::compareTo).orElse(0);
if(!place.getAssignableRoles().isEmpty()) {
maxPosition += 1;
}
public AssignableRole addRoleToPlace(FullEmote emote, Role role, String description, AssignableRolePlace place, ComponentPayload componentPayload) {
ARole arole = roleManagementService.findRole(role.getIdLong());
AssignableRole roleToAdd = AssignableRole
.builder()
.assignablePlace(place)
.emote(emote)
.role(role)
.requiredLevel(0)
.emoteMarkdown(emote != null ? emote.getEmoteRepr() : null)
.role(arole)
.componentPayload(componentPayload)
.server(place.getServer())
.position(maxPosition)
.description(description)
.assignableRolePlacePost(post)
.build();
place.getAssignableRoles().add(roleToAdd);
log.info("Adding role {} to assignable role place {}. There are now {} roles.", role.getId(), place.getId(), place.getAssignableRoles().size());
return roleToAdd;
}
@Override
public AssignableRole addRoleToPlace(Long placeId, Integer emoteId, Long roleId, String description, Long messageId) {
AssignableRolePlace place = rolePlaceManagementService.findByPlaceIdOptional(placeId).orElseThrow(() -> new AssignableRolePlaceNotFoundException(placeId));
AEmote emote = emoteManagementService.loadEmote(emoteId);
ARole role = roleManagementService.findRole(roleId);
AssignableRolePlacePost post = postManagementService.findByMessageId(messageId);
AssignableRole assignableRole = addRoleToPlace(place, emote, role, description, post);
post.getAssignableRoles().add(assignableRole);
return assignableRole;
}
@Override
public AssignableRole addRoleToPlace(Long placeId, Integer emoteId, Long roleId, String description) {
AssignableRolePlace place = rolePlaceManagementService.findByPlaceIdOptional(placeId).orElseThrow(() -> new AssignableRolePlaceNotFoundException(placeId));
AEmote emote = emoteManagementService.loadEmote(emoteId);
ARole role = roleManagementService.findRole(roleId);
return addRoleToPlace(place, emote, role, description, null);
}
@Override
public AssignableRole getByAssignableRoleId(Long assignableRoleId) {
return repository.findById(assignableRoleId).orElseThrow(() -> new AbstractoRunTimeException("Assignable role not found"));
}
@Override
public AssignableRole getRoleForReactionEmote(CachedEmote cachedEmote, AssignableRolePlace assignableRolePlace) {
for (AssignableRolePlacePost post : assignableRolePlace.getMessagePosts()) {
for (AssignableRole assignableRole : post.getAssignableRoles()) {
if (emoteService.compareCachedEmoteWithAEmote(cachedEmote, assignableRole.getEmote())) {
return assignableRole;
}
}
}
throw new AbstractoRunTimeException("Role for reaction was not found.");
public void deleteAssignableRole(AssignableRole assignableRole) {
assignableRole.getAssignablePlace().getAssignableRoles().remove(assignableRole);
assignableRole.setAssignablePlace(null);
repository.delete(assignableRole);
}
@Override
public AssignableRole getRoleForReactionEmote(MessageReaction.ReactionEmote cachedEmote, AssignableRolePlace assignableRolePlace) {
for (AssignableRolePlacePost post : assignableRolePlace.getMessagePosts()) {
for (AssignableRole assignableRole : post.getAssignableRoles()) {
if (emoteService.isReactionEmoteAEmote(cachedEmote, assignableRole.getEmote())) {
return assignableRole;
}
}
}
throw new AbstractoRunTimeException("Role for reaction was not found.");
public List<AssignableRole> getAssignableRolesFromAssignableUserWithPlaceType(AssignedRoleUser user, AssignableRolePlaceType type) {
return repository.findByAssignedUsersContainingAndAssignablePlace_Type(user, type);
}
}

View File

@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.assignableroles.service.management;
import dev.sheldan.abstracto.assignableroles.exception.AssignableRolePlaceNotFoundException;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlaceType;
import dev.sheldan.abstracto.assignableroles.repository.AssignableRolePlaceRepository;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
@@ -20,12 +21,18 @@ public class AssignableRolePlaceManagementServiceBean implements AssignableRoleP
private AssignableRolePlaceRepository repository;
@Override
public AssignableRolePlace createPlace(String name, AChannel channel, String text) {
public AssignableRolePlace createPlace(String name, AChannel channel, String text, AssignableRolePlaceType type) {
boolean unique = false;
if(type.equals(AssignableRolePlaceType.BOOSTER)) {
unique = true;
}
AssignableRolePlace place = AssignableRolePlace
.builder()
.channel(channel)
.server(channel.getServer())
.text(text)
.uniqueRoles(unique)
.type(type)
.key(name)
.build();
log.info("Creating assignable role place in channel {} on server {}.", channel.getId(), channel.getServer().getId());
@@ -39,8 +46,7 @@ public class AssignableRolePlaceManagementServiceBean implements AssignableRoleP
@Override
public AssignableRolePlace findByServerAndKey(AServer server, String name) {
// todo use other exception or adapt exception
return repository.findByServerAndKey(server, name).orElseThrow(() -> new AssignableRolePlaceNotFoundException(0L));
return repository.findByServerAndKey(server, name).orElseThrow(AssignableRolePlaceNotFoundException::new);
}
@Override
@@ -50,7 +56,7 @@ public class AssignableRolePlaceManagementServiceBean implements AssignableRoleP
@Override
public AssignableRolePlace findByPlaceId(Long id) {
return findByPlaceIdOptional(id).orElseThrow(() -> new AssignableRolePlaceNotFoundException(id));
return findByPlaceIdOptional(id).orElseThrow(AssignableRolePlaceNotFoundException::new);
}
@Override

View File

@@ -1,41 +0,0 @@
package dev.sheldan.abstracto.assignableroles.service.management;
import dev.sheldan.abstracto.assignableroles.exception.AssignableRolePlacePostNotFoundException;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlacePost;
import dev.sheldan.abstracto.assignableroles.repository.AssignableRolePlacePostRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
public class AssignableRolePlacePostManagementServiceBean implements AssignableRolePlacePostManagementService {
@Autowired
private AssignableRolePlacePostRepository repository;
@Override
public Optional<AssignableRolePlacePost> findByMessageIdOptional(Long messageId) {
return repository.findById(messageId);
}
@Override
public AssignableRolePlacePost findByMessageId(Long messageId) {
return findByMessageIdOptional(messageId).orElseThrow(() -> new AssignableRolePlacePostNotFoundException(messageId));
}
@Override
public AssignableRolePlacePost createAssignableRolePlacePost(AssignableRolePlace updatedPlace, Long messageId) {
AssignableRolePlacePost post = AssignableRolePlacePost
.builder()
.id(messageId)
.usedChannel(updatedPlace.getChannel())
.server(updatedPlace.getServer())
.assignablePlace(updatedPlace)
.build();
updatedPlace.getMessagePosts().add(post);
return post;
}
}

View File

@@ -11,6 +11,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
@Component
@@ -43,6 +44,18 @@ public class AssignedRoleUserManagementServiceBean implements AssignedRoleUserMa
removeAssignedRoleFromUser(assignableRole, user);
}
@Override
public void removeAssignedRoleFromUsers(AssignableRole assignableRole, List<AssignedRoleUser> users) {
log.info("Clearing all assignable role {} for {} users.", assignableRole.getId(), users.size());
assignableRole.getAssignedUsers().removeAll(users);
users.forEach(roleUser -> roleUser.getRoles().remove(assignableRole));
}
@Override
public void removeAssignedRoleFromUsers(AssignableRole assignableRole) {
removeAssignedRoleFromUsers(assignableRole, assignableRole.getAssignedUsers());
}
@Override
public void removeAssignedRoleFromUser(AssignableRole assignableRole, AssignedRoleUser user) {
assignableRole.getAssignedUsers().remove(user);

View File

@@ -1,2 +1,2 @@
abstracto.featureFlags.emoteTracking.featureName=emoteTracking
abstracto.featureFlags.emoteTracking.enabled=false
abstracto.featureFlags.assignableRole.featureName=assignableRole
abstracto.featureFlags.assignableRole.enabled=false

View File

@@ -6,83 +6,78 @@
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="experienceModule" value="(SELECT id FROM module WHERE name = 'assignableRoles')"/>
<property name="experienceFeature" value="(SELECT id FROM feature WHERE key = 'assignableRole')"/>
<property name="assignableRoleModule" value="(SELECT id FROM module WHERE name = 'assignableRoles')"/>
<property name="assignableRoleFeature" value="(SELECT id FROM feature WHERE key = 'assignableRole')"/>
<changeSet author="Sheldan" id="assignable_roles-commands">
<insert tableName="command">
<column name="name" value="addRoleToAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="module_id" valueComputed="${assignableRoleModule}"/>
<column name="feature_id" valueComputed="${assignableRoleFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="setupAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="module_id" valueComputed="${assignableRoleModule}"/>
<column name="feature_id" valueComputed="${assignableRoleFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="activateAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="module_id" valueComputed="${assignableRoleModule}"/>
<column name="feature_id" valueComputed="${assignableRoleFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="deactivateAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="module_id" valueComputed="${assignableRoleModule}"/>
<column name="feature_id" valueComputed="${assignableRoleFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="removeRoleFromAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="module_id" valueComputed="${assignableRoleModule}"/>
<column name="feature_id" valueComputed="${assignableRoleFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="changeAssignableRolePlaceConfig"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="setAssignableRolePosition"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="swapAssignableRolePosition"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="testAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="module_id" valueComputed="${assignableRoleModule}"/>
<column name="feature_id" valueComputed="${assignableRoleFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="deleteAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="module_id" valueComputed="${assignableRoleModule}"/>
<column name="feature_id" valueComputed="${assignableRoleFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="createAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="module_id" valueComputed="${assignableRoleModule}"/>
<column name="feature_id" valueComputed="${assignableRoleFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="showAssignableRolePlaceConfig"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="module_id" valueComputed="${assignableRoleModule}"/>
<column name="feature_id" valueComputed="${assignableRoleFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="editAssignableRolePlaceText"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="module_id" valueComputed="${assignableRoleModule}"/>
<column name="feature_id" valueComputed="${assignableRoleFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="moveAssignableRolePlace"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="module_id" valueComputed="${assignableRoleModule}"/>
<column name="feature_id" valueComputed="${assignableRoleFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="showAssignableRolePlaces"/>
<column name="module_id" valueComputed="${experienceModule}"/>
<column name="feature_id" valueComputed="${experienceFeature}"/>
<column name="module_id" valueComputed="${assignableRoleModule}"/>
<column name="feature_id" valueComputed="${assignableRoleFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="addAssignableRoleCondition"/>
<column name="module_id" valueComputed="${assignableRoleModule}"/>
<column name="feature_id" valueComputed="${assignableRoleFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="removeAssignableRoleCondition"/>
<column name="module_id" valueComputed="${assignableRoleModule}"/>
<column name="feature_id" valueComputed="${assignableRoleFeature}"/>
</insert>
</changeSet>

View File

@@ -11,8 +11,8 @@
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="assignable_role_pkey"/>
</column>
<column name="emote_id" type="INTEGER">
<constraints nullable="false"/>
<column name="emote_markdown" type="VARCHAR(100)">
<constraints nullable="true"/>
</column>
<column name="role_id" type="BIGINT">
<constraints nullable="false"/>
@@ -23,24 +23,22 @@
<column name="server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="place_post_id" type="BIGINT">
<constraints nullable="true"/>
</column>
<column name="description" type="VARCHAR(255)"/>
<column name="required_level" type="INTEGER"/>
<column name="position" type="INTEGER">
<constraints nullable="true"/>
<column name="description" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="component_id" type="VARCHAR(100)" />
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="true"/>
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="emote_id" baseTableName="assignable_role" constraintName="fk_assignable_role_emote" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="emote" validate="true"/>
<createIndex indexName="idx_assignable_role_role_id" tableName="assignable_role">
<column name="role_id"/>
</createIndex>
<addForeignKeyConstraint baseColumnNames="role_id" baseTableName="assignable_role" constraintName="fk_assignable_role_role" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="role" validate="true"/>
<addForeignKeyConstraint baseColumnNames="assignable_place_id" baseTableName="assignable_role" constraintName="fk_assignable_role_place" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="assignable_role_place" validate="true"/>
<addForeignKeyConstraint baseColumnNames="place_post_id" baseTableName="assignable_role" constraintName="fk_assignable_role_post" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="assignable_role_place_post" validate="true"/>
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="assignable_role" constraintName="fk_assignable_role_server" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
<addForeignKeyConstraint baseColumnNames="component_id" baseTableName="assignable_role" constraintName="fk_assignable_role_payload" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="component_payload" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS assignable_role_update_trigger ON assignable_role;
CREATE TRIGGER assignable_role_update_trigger BEFORE UPDATE ON assignable_role FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();

View File

@@ -0,0 +1,33 @@
<?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="assignable_role_condition-table">
<createTable tableName="assignable_role_condition">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="assignable_role_condition_pkey"/>
</column>
<column name="type" type="VARCHAR(100)">
<constraints nullable="false"/>
</column>
<column name="assignable_role_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="condition_value" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="assignable_role_id" baseTableName="assignable_role_condition" constraintName="fk_assignable_role_condition_assignable_role"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="assignable_role" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS assignable_role_condition_insert_trigger ON assignable_role_condition;
CREATE TRIGGER assignable_role_condition_insert_trigger BEFORE INSERT ON assignable_role_condition FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -18,23 +18,17 @@
<constraints nullable="false"/>
</column>
<column name="key" type="VARCHAR(255)">
<constraints nullable="true"/>
<constraints nullable="false"/>
</column>
<column name="text" type="VARCHAR(255)"/>
<column name="active" type="BOOLEAN">
<constraints nullable="true"/>
</column>
<column name="inline" type="BOOLEAN">
<constraints nullable="true"/>
<column name="text" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="unique_roles" type="BOOLEAN">
<constraints nullable="true"/>
</column>
<column name="auto_remove" type="BOOLEAN">
<constraints nullable="true"/>
<constraints nullable="false"/>
</column>
<column name="message_id" type="BIGINT" />
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="true"/>
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>

View File

@@ -1,41 +0,0 @@
<?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="assignable_role_place_post-table">
<createTable tableName="assignable_role_place_post">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="assignable_role_place_post_pkey"/>
</column>
<column name="assignable_place_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="channel_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="true"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="assignable_place_id" baseTableName="assignable_role_place_post" constraintName="fk_assignable_role_place_post_place_id" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="assignable_role_place" validate="true"/>
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="assignable_role_place_post" constraintName="fk_assignable_role_place_post_server" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
<addForeignKeyConstraint baseColumnNames="channel_id" baseTableName="assignable_role_place_post" constraintName="fk_assignable_role_place_post_channel" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="channel" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS assignable_role_place_post_update_trigger ON assignable_role_place_post;
CREATE TRIGGER assignable_role_place_post_update_trigger BEFORE UPDATE ON assignable_role_place_post FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS assignable_role_place_post_insert_trigger ON assignable_role_place_post;
CREATE TRIGGER assignable_role_place_post_insert_trigger BEFORE INSERT ON assignable_role_place_post FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -12,7 +12,7 @@
<constraints nullable="false" primaryKey="true" primaryKeyName="assigned_role_user_pkey"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="true"/>
<constraints nullable="false"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>

View File

@@ -7,7 +7,7 @@
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="assignable_role_place.xml" relativeToChangelogFile="true"/>
<include file="assignable_role_place_post.xml" relativeToChangelogFile="true"/>
<include file="assignable_role.xml" relativeToChangelogFile="true"/>
<include file="assignable_role_condition.xml" relativeToChangelogFile="true"/>
<include file="assigned_role_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="tables/tables.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,15 @@
<?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="assignable_role_place-add_type">
<addColumn tableName="assignable_role_place">
<column name="type" type="VARCHAR2(128)" defaultValue="DEFAULT"/>
</addColumn>
</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="assignable_role_place.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -7,4 +7,5 @@
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="1.0-assignableRoles/collection.xml" relativeToChangelogFile="true"/>
<include file="1.3.4/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

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

View File

@@ -0,0 +1,16 @@
package dev.sheldan.abstracto.assignableroles.condition;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRoleConditionType;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRolePlaceConditionModel;
import dev.sheldan.abstracto.assignableroles.model.database.AssignableRoleCondition;
import dev.sheldan.abstracto.assignableroles.model.template.condition.AssignableRoleConditionDisplay;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import net.dv8tion.jda.api.entities.Role;
public interface AssignableRoleConditionEvaluator {
boolean handlesCondition(AssignableRoleConditionType type);
boolean fulfillsCondition(AssignableRoleCondition conditionDefinition, AUserInAServer aUserInAServer);
boolean usableValue(String value);
AssignableRolePlaceConditionModel createNotificationModel(AssignableRoleCondition conditionDefinition, Role role);
AssignableRoleConditionDisplay getConditionDisplay(AssignableRoleCondition conditionDefinition);
}

View File

@@ -3,11 +3,7 @@ package dev.sheldan.abstracto.assignableroles.config;
import dev.sheldan.abstracto.core.command.execution.CommandParameterKey;
import lombok.Getter;
/**
* This enum is used to define the different key for which there exist properties on an {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace}.
* And is used for the command parameter when changing the value of an attribute on this place.
*/
@Getter
public enum AssignableRolePlaceParameterKey implements CommandParameterKey {
INLINE, UNIQUE, AUTOREMOVE, ACTIVE
UNIQUE
}

View File

@@ -2,8 +2,9 @@ package dev.sheldan.abstracto.assignableroles.exception;
import dev.sheldan.abstracto.assignableroles.model.exception.AssignableRoleAlreadyDefinedExceptionModel;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.FullEmote;
import dev.sheldan.abstracto.core.models.template.display.RoleDisplay;
import dev.sheldan.abstracto.core.templating.Templatable;
import net.dv8tion.jda.api.entities.Role;
/**
* Exception thrown in case the {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRole} has already been
@@ -13,14 +14,18 @@ import dev.sheldan.abstracto.core.templating.Templatable;
public class AssignableRoleAlreadyDefinedException extends AbstractoRunTimeException implements Templatable {
private final AssignableRoleAlreadyDefinedExceptionModel model;
public AssignableRoleAlreadyDefinedException(FullEmote emote, String placeName) {
public AssignableRoleAlreadyDefinedException(Role role, String placeName) {
super("Assignable role already assigned");
this.model = AssignableRoleAlreadyDefinedExceptionModel.builder().emote(emote).placeName(placeName).build();
this.model = AssignableRoleAlreadyDefinedExceptionModel
.builder()
.roleDisplay(RoleDisplay.fromRole(role))
.placeName(placeName)
.build();
}
@Override
public String getTemplateName() {
return "assignable_role_place_emote_already_defined_exception";
return "assignable_role_already_defined_exception";
}
@Override

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.assignableroles.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class AssignableRoleConditionAlreadyExistsException extends AbstractoTemplatableException {
@Override
public String getTemplateName() {
return "assignable_role_condition_already_present_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.assignableroles.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class AssignableRoleConditionDoesNotExistException extends AbstractoTemplatableException {
@Override
public String getTemplateName() {
return "assignable_role_condition_does_not_exist_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.assignableroles.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class AssignableRoleConditionValueNotUsableException extends AbstractoTemplatableException {
@Override
public String getTemplateName() {
return "assignable_role_condition_value_not_usable_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,27 @@
package dev.sheldan.abstracto.assignableroles.exception;
import dev.sheldan.abstracto.assignableroles.model.exception.AssignableRoleNotFoundExceptionModel;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class AssignableRoleNotFoundException extends AbstractoTemplatableException {
private final AssignableRoleNotFoundExceptionModel model;
public AssignableRoleNotFoundException(Long roleId) {
super("Role to assign is not available anymore.");
this.model = AssignableRoleNotFoundExceptionModel
.builder()
.roleId(roleId)
.build();
}
@Override
public String getTemplateName() {
return "assignable_role_not_found_exception";
}
@Override
public Object getTemplateModel() {
return this.model;
}
}

View File

@@ -2,9 +2,9 @@ package dev.sheldan.abstracto.assignableroles.exception;
import dev.sheldan.abstracto.assignableroles.model.exception.AssignableRoleNotUsableExceptionModel;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.FullRole;
import dev.sheldan.abstracto.core.models.template.display.RoleDisplay;
import dev.sheldan.abstracto.core.templating.Templatable;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Role;
/**
* Exception thrown in case the defined {@link net.dv8tion.jda.api.entities.Role role} cannot be interacted with by the bot,
@@ -14,14 +14,17 @@ import net.dv8tion.jda.api.entities.Guild;
public class AssignableRoleNotUsableException extends AbstractoRunTimeException implements Templatable {
private final AssignableRoleNotUsableExceptionModel model;
public AssignableRoleNotUsableException(FullRole role, Guild guild) {
public AssignableRoleNotUsableException(Role role) {
super("Role is not usable as assignable role");
this.model = AssignableRoleNotUsableExceptionModel.builder().role(role).guild(guild).build();
this.model = AssignableRoleNotUsableExceptionModel
.builder()
.roleDisplay(RoleDisplay.fromRole(role))
.build();
}
@Override
public String getTemplateName() {
return "assignable_role_place_role_not_usable_exception";
return "assignable_role_not_usable_exception";
}
@Override

View File

@@ -14,7 +14,10 @@ public class AssignableRolePlaceAlreadyExistsException extends AbstractoRunTimeE
public AssignableRolePlaceAlreadyExistsException(String name) {
super("Assignable role place already exists");
this.model = AssignableRolePlaceAlreadyExistsExceptionModel.builder().name(name).build();
this.model = AssignableRolePlaceAlreadyExistsExceptionModel
.builder()
.name(name)
.build();
}
@Override

View File

@@ -14,7 +14,11 @@ public class AssignableRolePlaceChannelDoesNotExistException extends AbstractoRu
public AssignableRolePlaceChannelDoesNotExistException(Long channelId, String placeName) {
super("Assignable role place channel does not exist");
this.model = AssignableRolePlaceChannelDoesNotExistExceptionModel.builder().channelId(channelId).placeName(placeName).build();
this.model = AssignableRolePlaceChannelDoesNotExistExceptionModel
.builder()
.channelId(channelId)
.placeName(placeName)
.build();
}
@Override

View File

@@ -0,0 +1,20 @@
package dev.sheldan.abstracto.assignableroles.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class AssignableRolePlaceIllegalConfigurationException extends AbstractoTemplatableException {
public AssignableRolePlaceIllegalConfigurationException() {
super("An illegal configuration key has been passed to configure the assignable role place config. Doing nothing.");
}
@Override
public String getTemplateName() {
return "assignable_role_place_illegal_configuration_key_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.abstracto.assignableroles.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class AssignableRolePlaceMaximumRolesException extends AbstractoTemplatableException {
public AssignableRolePlaceMaximumRolesException() {
super("The maximum amount of assignable roles have been reached.");
}
@Override
public String getTemplateName() {
return "assignable_role_place_maximum_roles_reached_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -1,20 +1,12 @@
package dev.sheldan.abstracto.assignableroles.exception;
import dev.sheldan.abstracto.assignableroles.model.exception.AssignableRolePlaceNotFoundExceptionModel;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.templating.Templatable;
/**
* Exception which is thrown in case a {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace place}
* defined by a {@link String key} does not exist
*/
public class AssignableRolePlaceNotFoundException extends AbstractoRunTimeException implements Templatable {
private final AssignableRolePlaceNotFoundExceptionModel model;
public AssignableRolePlaceNotFoundException(Long placeId) {
public AssignableRolePlaceNotFoundException() {
super("Assignable role place not found");
this.model = AssignableRolePlaceNotFoundExceptionModel.builder().placeId(placeId).build();
}
@Override
@@ -24,6 +16,6 @@ public class AssignableRolePlaceNotFoundException extends AbstractoRunTimeExcept
@Override
public Object getTemplateModel() {
return model;
return new Object();
}
}

View File

@@ -1,29 +0,0 @@
package dev.sheldan.abstracto.assignableroles.exception;
import dev.sheldan.abstracto.assignableroles.model.exception.AssignableRolePlacePostNotFoundExceptionModel;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.templating.Templatable;
/**
* Exception which is thrown, in case the {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlacePost post}
* was not found via its message ID
*/
public class AssignableRolePlacePostNotFoundException extends AbstractoRunTimeException implements Templatable {
private final AssignableRolePlacePostNotFoundExceptionModel model;
public AssignableRolePlacePostNotFoundException(Long messageId) {
super("Assignable place post not found.");
this.model = AssignableRolePlacePostNotFoundExceptionModel.builder().messageId(messageId).build();
}
@Override
public String getTemplateName() {
return "assignable_role_place_post_not_found_exception";
}
@Override
public Object getTemplateModel() {
return model;
}
}

View File

@@ -15,7 +15,10 @@ public class AssignedUserNotFoundException extends AbstractoRunTimeException imp
public AssignedUserNotFoundException(AUserInAServer userInAServer) {
super("Assigned user was not found");
this.model = AssignedUserNotFoundExceptionModel.builder().aUserInAServer(userInAServer).build();
this.model = AssignedUserNotFoundExceptionModel
.builder()
.userId(userInAServer.getUserReference().getId())
.build();
}
@Override

View File

@@ -0,0 +1,20 @@
package dev.sheldan.abstracto.assignableroles.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class BoosterAssignableRolePlaceMemberNotBoostingException extends AbstractoTemplatableException {
public BoosterAssignableRolePlaceMemberNotBoostingException() {
super("Clicking member does not boost");
}
@Override
public String getTemplateName() {
return "assignable_role_booster_place_member_not_boosting_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -1,31 +0,0 @@
package dev.sheldan.abstracto.assignableroles.exception;
import dev.sheldan.abstracto.assignableroles.model.exception.EmoteNotInAssignableRolePlaceExceptionModel;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.FullEmote;
import dev.sheldan.abstracto.core.templating.Templatable;
/**
* Exception which is thrown in case a given {@link dev.sheldan.abstracto.core.models.database.AEmote} was not found
* in the {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace}, when it was tried to switch
* or move the emotes around.
*/
public class EmoteNotInAssignableRolePlaceException extends AbstractoRunTimeException implements Templatable {
private final EmoteNotInAssignableRolePlaceExceptionModel model;
public EmoteNotInAssignableRolePlaceException(FullEmote emote, String placeName) {
super("Emote not found in assignable role place");
this.model = EmoteNotInAssignableRolePlaceExceptionModel.builder().emote(emote).placeName(placeName).build();
}
@Override
public String getTemplateName() {
return "emote_not_in_assignable_role_place_exception";
}
@Override
public Object getTemplateModel() {
return model;
}
}

View File

@@ -0,0 +1,38 @@
package dev.sheldan.abstracto.assignableroles.model.condition;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class AssignableRoleConditionResult {
private Boolean fulfilled;
private AssignableRoleConditionType causingCondition;
private AssignableRolePlaceConditionModel model;
public static AssignableRoleConditionResult fromFail(AssignableRoleConditionType cause, AssignableRolePlaceConditionModel model) {
return AssignableRoleConditionResult
.builder()
.causingCondition(cause)
.model(model)
.fulfilled(false)
.build();
}
public static AssignableRoleConditionResult fromFail(AssignableRoleConditionType cause) {
return AssignableRoleConditionResult
.builder()
.causingCondition(cause)
.fulfilled(false)
.build();
}
public static AssignableRoleConditionResult fromSuccess() {
return AssignableRoleConditionResult
.builder()
.fulfilled(true)
.build();
}
}

View File

@@ -0,0 +1,7 @@
package dev.sheldan.abstracto.assignableroles.model.condition;
import dev.sheldan.abstracto.core.command.execution.CommandParameterKey;
public enum AssignableRoleConditionType implements CommandParameterKey {
MIN_LEVEL
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.abstracto.assignableroles.model.condition;
import dev.sheldan.abstracto.core.models.template.display.RoleDisplay;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class AssignableRoleMinLevelModel {
private Integer minLevel;
private RoleDisplay roleDisplay;
}

View File

@@ -0,0 +1,22 @@
package dev.sheldan.abstracto.assignableroles.model.condition;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class AssignableRoleMinLevelResult implements AssignableRolePlaceConditionModel {
private AssignableRoleMinLevelModel model;
@Override
public String getTemplateName() {
return "assignable_role_condition_min_level";
}
@Override
public Object getTemplateModel() {
return model;
}
}

View File

@@ -0,0 +1,6 @@
package dev.sheldan.abstracto.assignableroles.model.condition;
import dev.sheldan.abstracto.core.templating.Templatable;
public interface AssignableRolePlaceConditionModel extends Templatable {
}

View File

@@ -3,6 +3,7 @@ package dev.sheldan.abstracto.assignableroles.model.database;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.ComponentPayload;
import lombok.*;
import javax.persistence.*;
@@ -29,15 +30,12 @@ public class AssignableRole implements Serializable {
* The unique ID of this {@link AssignableRole assignableRole}
*/
@Id
@Column(name = "id", nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* The {@link AEmote emote} this role is associated with
*/
@OneToOne(fetch = FetchType.LAZY, orphanRemoval = true)
@JoinColumn(name = "emote_id")
private AEmote emote;
@Column(name = "emote_markdown")
private String emoteMarkdown;
/**
* The {@link ARole} which given via this {@link AssignableRole assignableRole}
@@ -60,14 +58,6 @@ public class AssignableRole implements Serializable {
@JoinColumn(name = "assignable_place_id", nullable = false)
private AssignableRolePlace assignablePlace;
/**
* The {@link AssignableRolePlacePost} this assignable role is currently available as a reaction.
* This is necessary, to easier find the reaction which are valid, in case a reaction is added to a post
*/
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "place_post_id")
private AssignableRolePlacePost assignableRolePlacePost;
/**
* The {@link AssignedRoleUser users} which currently have this role assigned via this mechanism.
* This is necessary to enforce the unique property of {@link AssignableRolePlace}, in which you only may chose one
@@ -78,33 +68,33 @@ public class AssignableRole implements Serializable {
private List<AssignedRoleUser> assignedUsers = new ArrayList<>();
/**
* The description which is shown in the embeds of the posts of the {@link AssignableRolePlace}
* The display text which is used for the button
*/
@Column(name = "description")
@Column(name = "description", nullable = false)
private String description;
/**
* The level in experience which is required in order to receive this {@link AssignableRole}
*/
@Column(name = "required_level")
private Integer requiredLevel;
/**
* The position of this assignable role within the {@link AssignableRole}. This is required in order to show them
* the same order as the descriptions in the fields and also to move them around and switch positions
*/
@Column(name = "position")
private Integer position;
@OneToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "component_id", nullable = false)
private ComponentPayload componentPayload;
/**
* The {@link Instant} this entity was created
*/
@Column(name = "created")
@Column(name = "created", nullable = false, insertable = false, updatable = false)
private Instant created;
/**
* The {@link Instant} this entity was updated
*/
@Column(name = "updated")
@Column(name = "updated", insertable = false, updatable = false)
private Instant updated;
@OneToMany(
fetch = FetchType.LAZY,
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
orphanRemoval = true,
mappedBy = "assignableRole"
)
@Builder.Default
private List<AssignableRoleCondition> conditions = new ArrayList<>();
}

View File

@@ -0,0 +1,34 @@
package dev.sheldan.abstracto.assignableroles.model.database;
import dev.sheldan.abstracto.assignableroles.model.condition.AssignableRoleConditionType;
import lombok.*;
import javax.persistence.*;
@Entity
@Table(name = "assignable_role_condition")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode
public class AssignableRoleCondition {
@Id
@Column(name = "id", nullable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Getter
@Enumerated(EnumType.STRING)
@Column(name = "type")
private AssignableRoleConditionType type;
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "assignable_role_id", nullable = false)
private AssignableRole assignableRole;
@Column(name = "condition_value")
private String conditionValue;
}

View File

@@ -10,11 +10,6 @@ import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
/**
* The place entity holding the {@link AssignableRole roles} and {@link AssignableRolePlacePost posts} together.
* This is also the entity holding all the configuration for the place and is identified by a key as a String, which is unique
* for each server. This place holds the {@link AChannel} in which the
*/
@Entity
@Table(name = "assignable_role_place")
@Builder
@@ -33,14 +28,11 @@ public class AssignableRolePlace implements Serializable {
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
@Column(name = "id", nullable = false)
private Long id;
/**
* The channel in which the {@link AssignableRolePlacePost posts} for this place should be created
*/
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name="channel_id")
@JoinColumn(name="channel_id", nullable = false)
private AChannel channel;
/**
@@ -53,22 +45,9 @@ public class AssignableRolePlace implements Serializable {
/**
* The key this place is associated with via commands. Unique per server.
*/
@Column(name = "key")
@Column(name = "key", nullable = false)
private String key;
/**
* The {@link AssignableRolePlacePost posts} which were created when this place was setup. Is empty in the beginning
* and actively maintained in case a post is deleted.
*/
@OneToMany(
fetch = FetchType.LAZY,
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
orphanRemoval = true,
mappedBy = "assignablePlace"
)
@Builder.Default
private List<AssignableRolePlacePost> messagePosts = new ArrayList<>();
/**
* A List containing the {@link AssignableRole} which are associated with this place
*/
@@ -81,50 +60,33 @@ public class AssignableRolePlace implements Serializable {
@Builder.Default
private List<AssignableRole> assignableRoles = new ArrayList<>();
/**
* The text which is displayed in the first description area of the created {@link AssignableRolePlacePost}
*/
@Column(name = "text")
@Column(name = "message_id")
private Long messageId;
@Column(name = "text", nullable = false)
private String text;
/**
* Whether or not the reactions placed onto the posts should be acted upon
*/
@Builder.Default
@Column(name = "active")
private Boolean active = true;
/**
* Whether or not the fields containing the descriptions should be inline
*/
@Builder.Default
@Column(name = "inline")
private Boolean inline = false;
/**
* Whether or not it should be restricted, that a {@link AssignedRoleUser} should only have one role of this place
*/
@Builder.Default
@Column(name = "unique_roles")
@Column(name = "unique_roles", nullable = false)
private Boolean uniqueRoles = false;
/**
* Whether or not the added reactions should be removed automatically
*/
@Builder.Default
@Column(name = "auto_remove")
private Boolean autoRemove = false;
/**
* The {@link Instant} this entity was created
*/
@Column(name = "created")
@Column(name = "created", nullable = false, insertable = false, updatable = false)
private Instant created;
/**
* The {@link Instant} this entity was updated
*/
@Column(name = "updated")
@Column(name = "updated", insertable = false, updatable = false)
private Instant updated;
@Enumerated(EnumType.STRING)
@Column(name = "type")
private AssignableRolePlaceType type;
}

View File

@@ -1,78 +0,0 @@
package dev.sheldan.abstracto.assignableroles.model.database;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import lombok.*;
import javax.persistence.*;
import java.io.Serializable;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
/**
* One individual {@link net.dv8tion.jda.api.entities.Message message} which was sent when setting up an {@link AssignableRolePlace place}
* and contains the embeds and the reactions were placed onto it.
*/
@Entity
@Table(name = "assignable_role_place_post")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode
public class AssignableRolePlacePost implements Serializable {
/**
* The ID of the {@link net.dv8tion.jda.api.entities.Message message} which represents this post with the reactions.
*/
@Id
@Column(name = "id")
private Long id;
/**
* The actual {@link AChannel channel} in which the post ended up in
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "channel_id")
private AChannel usedChannel;
/**
* The {@link AServer server} in which this place post is posted
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "server_id", nullable = false)
private AServer server;
/**
* The {@link AssignableRolePlace place} this post is associated with
*/
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "assignable_place_id", nullable = false)
private AssignableRolePlace assignablePlace;
/**
* The actual {@link AssignableRole assignableRoles} which are associated with this post, and whose respective {@link dev.sheldan.abstracto.core.models.database.AEmote}
* have been placed as reactions
*/
@OneToMany(
fetch = FetchType.LAZY,
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
mappedBy = "assignableRolePlacePost")
@Builder.Default
private List<AssignableRole> assignableRoles = new ArrayList<>();
/**
* The {@link Instant} this entity was created
*/
@Column(name = "created")
private Instant created;
/**
* The {@link Instant} this entity was updated
*/
@Column(name = "updated")
private Instant updated;
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.assignableroles.model.database;
import dev.sheldan.abstracto.core.command.execution.CommandParameterKey;
import lombok.Getter;
@Getter
public enum AssignableRolePlaceType implements CommandParameterKey {
DEFAULT, BOOSTER
}

View File

@@ -27,7 +27,7 @@ public class AssignedRoleUser implements Serializable {
* The ID of the associated {@link AUserInAServer userInAServer}
*/
@Id
@Column(name = "id")
@Column(name = "id", nullable = false)
private Long id;
@OneToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@@ -48,13 +48,13 @@ public class AssignedRoleUser implements Serializable {
/**
* The {@link Instant} this entity was created
*/
@Column(name = "created")
@Column(name = "created", nullable = false, insertable = false, updatable = false)
private Instant created;
/**
* The {@link Instant} this entity was updated
*/
@Column(name = "updated")
@Column(name = "updated", insertable = false, updatable = false)
private Instant updated;
}

View File

@@ -1,6 +1,6 @@
package dev.sheldan.abstracto.assignableroles.model.exception;
import dev.sheldan.abstracto.core.models.FullEmote;
import dev.sheldan.abstracto.core.models.template.display.RoleDisplay;
import lombok.Builder;
import lombok.Getter;
@@ -12,13 +12,6 @@ import java.io.Serializable;
@Getter
@Builder
public class AssignableRoleAlreadyDefinedExceptionModel implements Serializable {
/**
* The {@link FullEmote emote} which was tried to be added to a {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace place}
*/
private final FullEmote emote;
/**
* The key of the {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace} for which it was tried to add a
* {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRole role}
*/
private final RoleDisplay roleDisplay;
private final String placeName;
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.assignableroles.model.exception;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
@Getter
@Setter
@Builder
public class AssignableRoleNotFoundExceptionModel implements Serializable {
private Long roleId;
private String displayText;
}

View File

@@ -1,9 +1,8 @@
package dev.sheldan.abstracto.assignableroles.model.exception;
import dev.sheldan.abstracto.core.models.FullRole;
import dev.sheldan.abstracto.core.models.template.display.RoleDisplay;
import lombok.Builder;
import lombok.Getter;
import net.dv8tion.jda.api.entities.Guild;
import java.io.Serializable;
@@ -13,12 +12,5 @@ import java.io.Serializable;
@Getter
@Builder
public class AssignableRoleNotUsableExceptionModel implements Serializable {
/**
* The {@link FullRole role} which is not usable as an {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRole role}
*/
private final FullRole role;
/**
* The {@link Guild server} in which it was not possible to use the {@link net.dv8tion.jda.api.entities.Role role}
*/
private final transient Guild guild;
private final RoleDisplay roleDisplay;
}

View File

@@ -1,18 +0,0 @@
package dev.sheldan.abstracto.assignableroles.model.exception;
import lombok.Builder;
import lombok.Getter;
import java.io.Serializable;
/**
* Contains the model for {@link dev.sheldan.abstracto.assignableroles.exception.AssignableRolePlaceNotFoundException}
*/
@Getter
@Builder
public class AssignableRolePlaceNotFoundExceptionModel implements Serializable {
/**
* The ID of the {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlace place} which was not found
*/
private final Long placeId;
}

View File

@@ -1,18 +0,0 @@
package dev.sheldan.abstracto.assignableroles.model.exception;
import lombok.Builder;
import lombok.Getter;
import java.io.Serializable;
/**
* Contains the model for {@link dev.sheldan.abstracto.assignableroles.exception.AssignableRolePlacePostNotFoundException}
*/
@Getter
@Builder
public class AssignableRolePlacePostNotFoundExceptionModel implements Serializable {
/**
* The ID of the {@link dev.sheldan.abstracto.assignableroles.model.database.AssignableRolePlacePost post} which was not found in the database
*/
private final Long messageId;
}

View File

@@ -1,6 +1,5 @@
package dev.sheldan.abstracto.assignableroles.model.exception;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.Builder;
import lombok.Getter;
@@ -12,8 +11,5 @@ import java.io.Serializable;
@Getter
@Builder
public class AssignedUserNotFoundExceptionModel implements Serializable {
/**
* The instance of the {@link AUserInAServer userInAServer} for which the assigned user was not found
*/
private final AUserInAServer aUserInAServer;
private final Long userId;
}

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