Compare commits

...

107 Commits

Author SHA1 Message Date
release-bot
afeef80878 [maven-release-plugin] prepare release v1.5.51 2024-11-22 21:35:43 +00:00
Sheldan
453378f0b6 [AB-xxx] enabling localization by adding the localization as a system config key
fixing methods which did not provide the server id to template rendering
refactoring channel service method to remove a duplicate
fixing template loading method to not throw an exception when template was not found
2024-11-22 21:54:13 +01:00
Sheldan
0e95ddf198 [AB-xxx] fixing edge case in which a server is duplicated for modmail prompt, because an available server has modmail enabled and uses another available server as appeal server 2024-11-06 22:06:47 +01:00
Sheldan
90816649e2 [AB-xxx] adding information whether a modmail thread comes from an appeal to storage and to modmail notification 2024-11-06 21:46:19 +01:00
release-bot
5b90d429c2 Commit from GitHub Actions (Publishes a new version of abstracto) 2024-11-05 22:25:28 +00:00
release-bot
7b1774b73e [maven-release-plugin] prepare for next development iteration 2024-11-05 22:15:13 +00:00
release-bot
618155a464 [maven-release-plugin] prepare release v1.5.50 2024-11-05 22:15:12 +00:00
Sheldan
feabe6426e [AB-xxx] fixing version after failed release 2024-11-05 23:12:45 +01:00
Sheldan
2dc21ce996 [AB-xxx] fixing typescript linter warning 2024-11-05 23:11:42 +01:00
release-bot
5643a41fd8 [maven-release-plugin] prepare for next development iteration 2024-11-05 22:00:25 +00:00
release-bot
08d0561998 [maven-release-plugin] prepare release v1.5.49 2024-11-05 22:00:24 +00:00
Sheldan
879c1b0173 [AB-xxx] displaying more information on the external leaderboard 2024-11-05 22:57:51 +01:00
Sheldan
d5bf70f586 [AB-xxx] fixing variable naming 2024-11-04 23:14:16 +01:00
release-bot
4525fbc861 Commit from GitHub Actions (Publishes a new version of abstracto) 2024-10-29 21:53:24 +00:00
release-bot
4671a78ff9 [maven-release-plugin] prepare for next development iteration 2024-10-29 21:43:24 +00:00
release-bot
c4174562c6 [maven-release-plugin] prepare release v1.5.48 2024-10-29 21:43:22 +00:00
Sheldan
bfaa5d6140 [AB-xxx] making lovecalc deterministic (simple approach) 2024-10-29 22:36:04 +01:00
release-bot
d2e8398fa4 Commit from GitHub Actions (Publishes a new version of abstracto) 2024-09-29 19:56:51 +00:00
release-bot
c67209925a [maven-release-plugin] prepare for next development iteration 2024-09-29 19:46:50 +00:00
release-bot
f3ac7895eb [maven-release-plugin] prepare release v1.5.47 2024-09-29 19:46:48 +00:00
Sheldan
6d893e39bb [AB-xxx] downgrading to java 8 for liquibase 2024-09-29 21:44:28 +02:00
release-bot
94140104de Commit from GitHub Actions (Publishes a new version of abstracto) 2024-09-29 19:23:42 +00:00
release-bot
6d80423244 [maven-release-plugin] prepare for next development iteration 2024-09-29 19:13:23 +00:00
release-bot
27040e506a [maven-release-plugin] prepare release v1.5.46 2024-09-29 19:13:22 +00:00
Sheldan
3bbf5a2391 [AB-xxx] restructuring docker file for liquibase deployment to abide to the upload limits per individual file 2024-09-29 21:11:04 +02:00
release-bot
11cb3b9ee1 [maven-release-plugin] prepare for next development iteration 2024-09-29 09:36:43 +00:00
release-bot
dccf314e53 [maven-release-plugin] prepare release v1.5.45 2024-09-29 09:36:42 +00:00
Sheldan
a0daeee3a1 [AB-xxx] removing discord webhook due to deprecated github action 2024-09-29 11:30:28 +02:00
Sheldan
1b7c383ced [AB-xxx] allowing reminders to not have a text
code cleanup
2024-09-29 11:22:47 +02:00
release-bot
087dd266cc Commit from GitHub Actions (Publishes a new version of abstracto) 2024-08-06 20:59:49 +00:00
release-bot
beda17f672 [maven-release-plugin] prepare for next development iteration 2024-08-06 20:50:04 +00:00
release-bot
5c234295aa [maven-release-plugin] prepare release v1.5.44 2024-08-06 20:50:02 +00:00
Sheldan
9cfe4bf353 [AB-xxx] commit for release 2024-08-06 22:47:31 +02:00
release-bot
75c45541c9 [maven-release-plugin] prepare for next development iteration 2024-08-06 20:30:22 +00:00
release-bot
11ac33ad4a [maven-release-plugin] prepare release v1.5.43 2024-08-06 20:30:20 +00:00
Sheldan
a37a0f87a0 [AB-xxx] commit for release 2024-08-02 23:30:18 +02:00
release-bot
69353d32db [maven-release-plugin] prepare for next development iteration 2024-08-02 20:59:38 +00:00
release-bot
b973c4660c [maven-release-plugin] prepare release v1.5.42 2024-08-02 20:59:36 +00:00
Sheldan
34692d22eb [AB-xxx] fixing year 2024-08-02 22:57:15 +02:00
release-bot
33268cded6 Commit from GitHub Actions (Publishes a new version of abstracto) 2024-08-02 20:44:20 +00:00
release-bot
40d66df9b0 [maven-release-plugin] prepare for next development iteration 2024-08-02 20:40:14 +00:00
release-bot
92508d7a1d [maven-release-plugin] prepare release v1.5.41 2024-08-02 20:40:12 +00:00
Sheldan
ccbf6147e9 [AB-xxx] changing github actions image version to a concrete version and upgrading to docker compose 2024-08-02 22:36:04 +02:00
release-bot
a3545c4af0 [maven-release-plugin] prepare for next development iteration 2024-08-02 20:25:03 +00:00
release-bot
fa187f8817 [maven-release-plugin] prepare release v1.5.40 2024-08-02 20:25:00 +00:00
Sheldan
c39b7ebeec [AB-xxx] increasing version due to failed release 2024-08-02 22:22:35 +02:00
release-bot
5cbad801ff [maven-release-plugin] prepare for next development iteration 2024-08-02 20:09:33 +00:00
release-bot
826bee1f81 [maven-release-plugin] prepare release v1.5.39 2024-08-02 20:09:26 +00:00
Sheldan
99bf9a9be0 [AB-xxx] fixed send message action sending for every level change 2024-07-31 00:11:57 +02:00
Sheldan
5b5e4973a7 [AB-117] increasing length of supported reminder text 2024-07-30 21:24:44 +02:00
Sheldan
65e956827c [AB-xxx] adding generic input format exception handling
adding level_action to send a message to a channel once a level is reached
2024-07-27 19:51:49 +02:00
release-bot
b258a8bc54 Commit from GitHub Actions (Publishes a new version of abstracto) 2024-06-13 16:33:09 +00:00
release-bot
9864b7d875 [maven-release-plugin] prepare for next development iteration 2024-06-13 16:21:34 +00:00
release-bot
27466b7333 [maven-release-plugin] prepare release v1.5.38 2024-06-13 16:21:31 +00:00
Sheldan
bfb8969d1f [AB-xxx] fixing passing wrong ID in contact slash command 2024-06-13 18:18:51 +02:00
Sheldan
0097ff801a [AB-xxx] changing hikari max live time 2024-06-12 20:07:55 +02:00
Sheldan
b6a188c04d [AB-xxx] using the suggestion message as the thread starter for suggestion threads 2024-06-03 22:24:35 +02:00
release-bot
2fa1adde02 Commit from GitHub Actions (Publishes a new version of abstracto) 2024-05-30 23:29:17 +00:00
release-bot
cb8b64cc01 [maven-release-plugin] prepare for next development iteration 2024-05-30 23:19:23 +00:00
release-bot
3b3dd0dbb7 [maven-release-plugin] prepare release v1.5.37 2024-05-30 23:19:22 +00:00
Sheldan
388fead2a6 [AB-xxx] typo change
java doc update
removal of todo
2024-05-31 01:14:26 +02:00
Sheldan
336c3d0bd8 [AB-xxx] adding modmail support for ban appeals
refactoring modmail to use user objects instead of member in various places
2024-05-31 01:14:26 +02:00
Sheldan
4991ad8f1c [AB-xxx] making intents, cache policies, and cache flags configurable 2024-05-18 18:22:33 +02:00
Sheldan
c5136a1808 [AB-xxx] removing example implementation repository from readme 2024-05-17 17:34:33 +02:00
release-bot
446d882eec Commit from GitHub Actions (Publishes a new version of abstracto) 2024-05-05 22:24:18 +00:00
release-bot
b3e207a967 [maven-release-plugin] prepare for next development iteration 2024-05-05 22:12:03 +00:00
release-bot
5c25345cf8 [maven-release-plugin] prepare release v1.5.36 2024-05-05 22:12:01 +00:00
Sheldan
a01a5055a0 [AB-xxx] refactoring handling of bans and mutes: commands actively log, the reason for this is that the command is the only place who actually knows how executed the command. the event itself only sees the bot performing the action
adding event based logging of kicks
2024-05-06 00:07:24 +02:00
Sheldan
234aae3783 [AB-xxx] reworking ban logging to use audit log instead of actively logging or using the banned event
partially fixing broken infraction handling
adding CompletableFutureMap to handle futures easier
updating user display object to also hold name
replaced some references to UserObjects in models with UserDisplay objects
2024-05-05 01:58:26 +02:00
Sheldan
ca45137cc6 [AB-xxx] reworking mute logging to use audit log events instead of active logging and member update events 2024-05-04 20:35:56 +02:00
Sheldan
bc3d16b40e [AB-xxx] fixing using the wrong user for unmute notifications 2024-05-03 18:24:10 +02:00
Sheldan
66e212d30c [AB-xxx] updating documentation 2024-05-03 18:07:29 +02:00
Sheldan
b69811479f [AB-xxx] adding more documentation in the moderation area
removing not used feature modes from configuration
2024-04-19 23:57:33 +02:00
Sheldan
43c5d041ef [AB-xxx] adding/updating documentation for core and experience module
updating asciidoctor plugin version
adding check to not allow duplicate level action configurations
limiting experience level up notification toggle command to be only available if the feature mode is enabled
2024-04-18 23:09:56 +02:00
Sheldan
dfe9792330 [AB-xxx] fixing combined parameters not providing the appropriate option types for slash commands 2024-04-13 00:20:02 +02:00
Sheldan
250df57bd0 [AB-xxx] adding tests for command management services
some code improvements
2024-04-07 14:59:22 +02:00
Sheldan
02b8ed2b5d [AB-xxx] adding unit test for server controller
refactoring parameter parsing tests to use assertj
2024-04-07 11:49:06 +02:00
Sheldan
71c1445439 [AB-112] adding command parameter alternatives to bonk/pat to use the message author the command was replied to 2024-04-05 17:30:06 +02:00
release-bot
d86299cdf6 Commit from GitHub Actions (Publishes a new version of abstracto) 2024-04-05 13:22:47 +00:00
release-bot
1b86fba3e0 [maven-release-plugin] prepare for next development iteration 2024-04-05 13:08:51 +00:00
release-bot
3ee7c92cdd [maven-release-plugin] prepare release v1.5.35 2024-04-05 13:08:49 +00:00
Sheldan
6c6cd130aa [AB-xxx] changing types of ids to be string instead for javascript purposes 2024-04-05 15:06:10 +02:00
release-bot
65a1d44069 Commit from GitHub Actions (Publishes a new version of abstracto) 2024-04-05 12:43:58 +00:00
release-bot
11312a5e27 [maven-release-plugin] prepare for next development iteration 2024-04-05 12:29:23 +00:00
release-bot
6b13958ac0 [maven-release-plugin] prepare release v1.5.34 2024-04-05 12:29:20 +00:00
Sheldan
3142daafd3 [AB-xxx] renaming leaderboard url property 2024-04-05 01:58:43 +02:00
Sheldan
4bef78f847 [AB-xxx] adding link to the leaderboard to the leaderboard command response 2024-04-05 01:40:06 +02:00
Sheldan
82be86e086 [AB-xxx] adding locking mechanism for role assignments to work around discord lack of role update locking 2024-04-04 22:54:18 +02:00
Sheldan
bff505ef25 [AB-xxx] fixing not using the ban reason for moderation actions 2024-03-27 23:17:06 +01:00
release-bot
533f5671c2 [maven-release-plugin] prepare for next development iteration 2024-03-27 21:26:56 +00:00
release-bot
8c7547b485 [maven-release-plugin] prepare release v1.5.33 2024-03-27 21:26:54 +00:00
Sheldan
741c194bb8 [AB-xxx] changing styling for smaller screens for member display to truncate the name
prepare for release
2024-03-27 22:24:38 +01:00
release-bot
d2bdfd8dac [maven-release-plugin] prepare for next development iteration 2024-03-26 22:54:50 +00:00
release-bot
36c67fbe20 [maven-release-plugin] prepare release v1.5.32 2024-03-26 22:54:47 +00:00
Sheldan
8fd1aede2a [AB-xxx] changing style of leaderboard table
preparing for release
2024-03-26 23:48:22 +01:00
release-bot
287ae1f0b1 [maven-release-plugin] prepare for next development iteration 2024-03-26 21:40:02 +00:00
release-bot
903361cb58 [maven-release-plugin] prepare release v1.5.31 2024-03-26 21:39:59 +00:00
Sheldan
c8cf412a4a [AB-xxx] changing style of leaderboard table
preparing for release
2024-03-26 22:37:47 +01:00
release-bot
affc249012 [maven-release-plugin] prepare for next development iteration 2024-03-26 21:17:03 +00:00
release-bot
653671ea79 [maven-release-plugin] prepare release v1.5.30 2024-03-26 21:17:00 +00:00
Sheldan
7185908682 [AB-xxx] prepare for release 2024-03-26 22:13:42 +01:00
Sheldan
675da8d5d8 [AB-xxx] adding rank to leaderboard page
changing design of leaderboard page
fixing role not having an id
2024-03-26 22:13:18 +01:00
release-bot
e91becee0d [maven-release-plugin] prepare for next development iteration 2024-03-26 01:10:22 +00:00
release-bot
18732efe75 [maven-release-plugin] prepare release v1.5.29 2024-03-26 01:10:20 +00:00
Sheldan
63897fd914 [AB-xxx] prepare for release 2024-03-26 02:08:06 +01:00
Sheldan
9b865af43b [AB-xxx] fixing not serving static files 2024-03-26 02:07:25 +01:00
release-bot
b78275734c [maven-release-plugin] prepare for next development iteration 2024-03-26 00:36:30 +00:00
327 changed files with 3668 additions and 1996 deletions

2
.env
View File

@@ -1,2 +1,2 @@
REGISTRY_PREFIX=harbor.sheldan.dev/abstracto/
VERSION=1.5.28
VERSION=1.5.50

View File

@@ -16,7 +16,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3
@@ -37,15 +37,4 @@ jobs:
- name: Build ui application
working-directory: ./ui/experience-tracking
run: npm run build
- uses: actions/setup-ruby@v1
- name: Send Webhook Notification
if: always()
env:
JOB_STATUS: ${{ job.status }}
WEBHOOK_URL: ${{ secrets.WEBHOOK_URL }}
HOOK_OS_NAME: ${{ runner.os }}
WORKFLOW_NAME: ${{ github.workflow }}
run: |
git clone https://github.com/DiscordHooks/github-actions-discord-webhook.git webhook
bash webhook/send.sh $JOB_STATUS $WEBHOOK_URL
shell: bash

View File

@@ -5,7 +5,7 @@ permissions:
contents: write
jobs:
publish:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
@@ -64,7 +64,7 @@ jobs:
with:
path: .env
- name: Build and push Docker containers
run: docker-compose build && docker-compose push
run: docker compose build && docker compose push
env:
REGISTRY_PREFIX: ${{ steps.dotenv.outputs.registry_prefix }}
VERSION: ${{ steps.dotenv.outputs.version }}

2
.gitignore vendored
View File

@@ -14,9 +14,7 @@ target/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2023 Sheldan
Copyright (c) 2024 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

@@ -8,7 +8,6 @@ Abstracto represents a framework to be used as a basis for a Discord bot. It use
and provides an extensive tool set to create new commands and a wide range of commands out of the box.
This repository does not provide the full functionality in order to start a discord bot, because it requires a Main class.
An example implementation of this bot can be seen [here](https://github.com/Sheldan/Crimson). This repository contains the required configuration in order to run a bot and example customizations.
## Technologies

View File

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

View File

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

View File

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

View File

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

View File

@@ -42,7 +42,7 @@ public class ShowAssignableRolePlaceConfig extends AbstractConditionableCommand
List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0);
AssignableRolePlaceConfig config = service.getAssignableRolePlaceConfig(commandContext.getGuild(), name);
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(ASSIGNABLE_ROLES_CONFIG_POST_TEMPLATE_KEY, config, commandContext.getChannel()))
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannel(ASSIGNABLE_ROLES_CONFIG_POST_TEMPLATE_KEY, config, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}

View File

@@ -39,7 +39,7 @@ public class ShowAssignableRolePlaces extends AbstractConditionableCommand {
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
AssignablePlaceOverview model = service.getAssignableRolePlaceOverview(commandContext.getGuild());
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(ASSIGNABLE_ROLE_PLACES_OVERVIEW_TEMPLATE_KEY, model, commandContext.getChannel()))
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannel(ASSIGNABLE_ROLE_PLACES_OVERVIEW_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>custom-command</artifactId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>custom-command</artifactId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -53,7 +53,7 @@ public class Choose extends AbstractConditionableCommand {
.builder()
.chosenValue(choice)
.build();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(CHOOSE_RESPONSE_TEMPLATE_KEY, responseModel, commandContext.getChannel()))
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannel(CHOOSE_RESPONSE_TEMPLATE_KEY, responseModel, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.entertainment.command;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CombinedParameterEntry;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
@@ -65,7 +66,7 @@ public class Mock extends AbstractConditionableCommand {
.originalText(messageText)
.mockingText(mockingText)
.build();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(MOCK_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannel(MOCK_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}
@@ -86,7 +87,7 @@ public class Mock extends AbstractConditionableCommand {
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
Map<String, Object> parameterAlternatives = new HashMap<>();
parameterAlternatives.put(ADDITIONAL_TYPES_KEY, Arrays.asList(Message.class, String.class));
parameterAlternatives.put(ADDITIONAL_TYPES_KEY, Arrays.asList(CombinedParameterEntry.messageParameter(Message.class), CombinedParameterEntry.parameter(String.class)));
Parameter messageParameter = Parameter
.builder()

View File

@@ -61,7 +61,7 @@ public class PressFCommand extends AbstractConditionableCommand {
Long defaultDurationSeconds = configService.getLongValueOrConfigDefault(PRESS_F_DEFAULT_DURATION_SECONDS, commandContext.getGuild().getIdLong());
Duration duration = Duration.ofSeconds(defaultDurationSeconds);
PressFPromptModel pressFModel = entertainmentService.getPressFModel(text);
List<CompletableFuture<Message>> messages = channelService.sendEmbedTemplateInMessageChannelList(RESPONSE_TEMPLATE, pressFModel, commandContext.getChannel());
List<CompletableFuture<Message>> messages = channelService.sendEmbedTemplateInMessageChannel(RESPONSE_TEMPLATE, pressFModel, commandContext.getChannel());
return FutureUtils.toSingleFutureGeneric(messages)
.thenAccept(unused -> entertainmentService.persistPressF(text, duration, commandContext.getAuthor(),
pressFModel.getPressFComponentId(), commandContext.getChannel(), messages.get(0).join().getIdLong()))

View File

@@ -69,7 +69,7 @@ public class Roll extends AbstractConditionableCommand {
.builder()
.rolled(rolled)
.build();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(ROLL_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannel(ROLL_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
}

View File

@@ -45,7 +45,7 @@ public class Roulette extends AbstractConditionableCommand {
.builder()
.result(rouletteResult)
.build();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(ROULETTE_RESPONSE_TEMPLATE_KEY, responseModel, commandContext.getChannel()))
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannel(ROULETTE_RESPONSE_TEMPLATE_KEY, responseModel, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
}

View File

@@ -65,7 +65,7 @@ public class TransferCredits extends AbstractConditionableCommand {
.targetMember(MemberDisplay.fromMember(targetMember))
.credits(amount)
.build();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(TRANSFER_CREDITS_RESPONSE, responseModel, commandContext.getChannel()))
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannel(TRANSFER_CREDITS_RESPONSE, responseModel, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}

View File

@@ -103,7 +103,7 @@ public class Mines extends AbstractConditionableCommand {
board.setUserId(event.getMember().getIdLong());
board.setServerId(serverId);
board.setCredits(credit);
MessageToSend messageToSend = templateService.renderEmbedTemplate(MINE_BOARD_TEMPLATE_KEY, board);
MessageToSend messageToSend = templateService.renderEmbedTemplate(MINE_BOARD_TEMPLATE_KEY, board, serverId);
return interactionService.replyMessageToSend(messageToSend, event)
.thenCompose(interactionHook -> interactionHook.retrieveOriginal().submit())
.thenApply(message -> {
@@ -151,7 +151,7 @@ public class Mines extends AbstractConditionableCommand {
board.setUserId(commandContext.getAuthor().getIdLong());
board.setServerId(serverId);
board.setCredits(credit);
MessageToSend messageToSend = templateService.renderEmbedTemplate(MINE_BOARD_TEMPLATE_KEY, board);
MessageToSend messageToSend = templateService.renderEmbedTemplate(MINE_BOARD_TEMPLATE_KEY, board, serverId);
List<CompletableFuture<Message>> futures = channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel());
return FutureUtils.toSingleFutureGeneric(futures)
.thenAccept(unused -> gameService.persistMineBoardMessage(board, futures.get(0).join()))

View File

@@ -54,7 +54,7 @@ public class MinesButtonClickedListener implements ButtonClickedListener {
}
gameService.uncoverBoard(mineBoard);
}
MessageToSend messageToSend = templateService.renderEmbedTemplate(Mines.MINE_BOARD_TEMPLATE_KEY, mineBoard);
MessageToSend messageToSend = templateService.renderEmbedTemplate(Mines.MINE_BOARD_TEMPLATE_KEY, mineBoard, model.getServerId());
interactionService.editOriginal(messageToSend, model.getEvent().getHook()).thenAccept(message -> {
gameService.updateMineBoard(mineBoard);
log.info("Updated original mineboard for board {}.", mineBoard.getBoardId());

View File

@@ -105,7 +105,9 @@ public class EntertainmentServiceBean implements EntertainmentService {
@Override
public Integer getLoveCalcValue(String firstPart, String secondPart) {
return secureRandom.nextInt(100);
String fullInput = firstPart.toLowerCase() + secondPart.toLowerCase();
Random random = new Random(fullInput.hashCode());
return random.nextInt(100);
}
@Override
@@ -191,8 +193,8 @@ public class EntertainmentServiceBean implements EntertainmentService {
.text(pressF.getText())
.messageId(pressF.getMessageId())
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(PRESS_F_RESULT_TEMPLATE_KEY, model);
Long serverId = pressF.getServer().getId();
MessageToSend messageToSend = templateService.renderEmbedTemplate(PRESS_F_RESULT_TEMPLATE_KEY, model, serverId);
Long channelId = pressF.getPressFChannel().getId();
Long messageId = pressF.getMessageId();
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageEmbedToSendToAChannel(messageToSend, pressF.getPressFChannel()))

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking</artifactId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -56,7 +56,7 @@ public class ExperienceConfigController {
} else {
roleDisplay = RoleDisplay
.builder()
.id(levelRole.getRoleId())
.id(String.valueOf(levelRole.getRoleId()))
.build();
}
return ExperienceRoleDisplay

View File

@@ -8,6 +8,7 @@ import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.experience.model.api.UserExperienceDisplay;
import dev.sheldan.abstracto.experience.model.database.AExperienceRole;
import dev.sheldan.abstracto.experience.model.database.AUserExperience;
import dev.sheldan.abstracto.experience.service.ExperienceLevelService;
import dev.sheldan.abstracto.experience.service.management.UserExperienceManagementService;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
@@ -37,6 +38,9 @@ public class LeaderboardController {
@Autowired
private GuildService guildService;
@Autowired
private ExperienceLevelService experienceLevelService;
@GetMapping(value = "/leaderboards/{serverId}", produces = "application/json")
public Page<UserExperienceDisplay> getLeaderboard(@PathVariable("serverId") Long serverId,
@PageableDefault(value = 25, page = 0)
@@ -44,16 +48,19 @@ public class LeaderboardController {
Pageable pageable) {
AServer server = serverManagementService.loadServer(serverId);
Guild guild = guildService.getGuildById(serverId);
return userExperienceManagementService.loadAllUsersPaginated(server, pageable)
.map(userExperience -> convertFromUser(guild, userExperience));
Page<AUserExperience> allElements = userExperienceManagementService.loadAllUsersPaginated(server, pageable);
return allElements
.map(userExperience -> convertFromUser(guild, userExperience, pageable, allElements));
}
private UserExperienceDisplay convertFromUser(Guild guild, AUserExperience aUserExperience) {
private UserExperienceDisplay convertFromUser(Guild guild, AUserExperience aUserExperience, Pageable pageable, Page<AUserExperience> page) {
Long userId = aUserExperience.getUser().getUserReference().getId();
Member member = guild.getMember(UserSnowflake.fromId(userId));
AExperienceRole experienceRole = aUserExperience.getCurrentExperienceRole();
UserDisplay userDisplay = null;
RoleDisplay roleDisplay = null;
Long experienceNeededToNextLevel = experienceLevelService.calculateExperienceToNextLevel(aUserExperience.getCurrentLevel().getLevel(), aUserExperience.getExperience());
Long nextLevelExperience = experienceLevelService.calculateNextLevel(aUserExperience.getCurrentLevel().getLevel()).getExperienceNeeded();
if(experienceRole != null) {
Role role = guild.getRoleById(experienceRole.getRole().getId());
if(role != null) {
@@ -65,12 +72,21 @@ public class LeaderboardController {
if(member != null) {
userDisplay = UserDisplay.fromMember(member);
}
Long currentExpNeeded = aUserExperience.getCurrentLevel().getExperienceNeeded();
Long experienceWithinLevel = aUserExperience.getExperience() - currentExpNeeded;
Long experienceNeededForCurrentLevel = nextLevelExperience - currentExpNeeded;
return UserExperienceDisplay
.builder()
.id(userId)
.id(String.valueOf(userId))
.messages(aUserExperience.getMessageCount())
.level(aUserExperience.getLevelOrDefault())
.rank((int) pageable.getOffset() + page.getContent().indexOf(aUserExperience) + 1)
.experience(aUserExperience.getExperience())
.experienceToNextLevel(experienceNeededToNextLevel)
.currentLevelExperienceNeeded(experienceNeededForCurrentLevel)
.experienceOnCurrentLevel(experienceWithinLevel)
.percentage(((float) experienceWithinLevel / experienceNeededForCurrentLevel) * 100)
.nextLevelExperienceNeeded(nextLevelExperience)
.role(roleDisplay)
.member(userDisplay)
.build();

View File

@@ -18,6 +18,7 @@ import dev.sheldan.abstracto.core.service.management.UserInServerManagementServi
import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.config.ExperienceFeatureMode;
import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames;
import dev.sheldan.abstracto.experience.exception.LevelActionAlreadyExistsException;
import dev.sheldan.abstracto.experience.exception.LevelActionNotFoundException;
import dev.sheldan.abstracto.experience.listener.LevelActionListener;
import dev.sheldan.abstracto.experience.model.LevelActionPayload;
@@ -99,6 +100,9 @@ public class AddLevelAction extends AbstractConditionableCommand {
return userExperienceManagementService.saveUser(user);
});
}
if(levelActionManagementService.getLevelAction(actionName, level, server, userExperience).isPresent()) {
throw new LevelActionAlreadyExistsException();
}
levelActionManagementService.createLevelAction(level, server, actionName, userExperience, payload);
return interactionService.replyEmbed(RESPONSE_TEMPLATE, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());

View File

@@ -7,12 +7,14 @@ 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.config.FeatureMode;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.config.ExperienceFeatureMode;
import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames;
import dev.sheldan.abstracto.experience.service.AUserExperienceService;
import net.dv8tion.jda.api.entities.Member;
@@ -100,4 +102,10 @@ public class ExpLevelUpNotification extends AbstractConditionableCommand {
.help(helpInfo)
.build();
}
@Override
public List<FeatureMode> getFeatureModeLimitations() {
return Arrays.asList(ExperienceFeatureMode.LEVEL_UP_NOTIFICATION);
}
}

View File

@@ -28,7 +28,9 @@ import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
@@ -70,6 +72,9 @@ public class LeaderBoardCommand extends AbstractConditionableCommand {
@Autowired
private InteractionService interactionService;
@Value("${abstracto.experience.leaderboard.externalUrl}")
private String leaderboardExternalURL;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
@@ -91,11 +96,18 @@ public class LeaderBoardCommand extends AbstractConditionableCommand {
LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(aUserInAServer);
CompletableFuture<List<LeaderBoardEntryModel>> userRankFuture = converter.fromLeaderBoardEntry(Arrays.asList(userRank));
futures.add(userRankFuture);
String leaderboardUrl;
if(!StringUtils.isBlank(leaderboardExternalURL)) {
leaderboardUrl = String.format("%s/experience/leaderboards/%s", leaderboardExternalURL, actorUser.getGuild().getIdLong());
} else {
leaderboardUrl = null;
}
return FutureUtils.toSingleFuture(futures).thenCompose(aVoid -> {
List<LeaderBoardEntryModel> finalModels = completableFutures.join();
LeaderBoardModel leaderBoardModel = LeaderBoardModel
.builder()
.userExperiences(finalModels)
.leaderboardUrl(leaderboardUrl)
.userExecuting(userRankFuture.join().get(0))
.build();
return CompletableFuture.completedFuture(templateService.renderEmbedTemplate(LEADER_BOARD_POST_EMBED_TEMPLATE, leaderBoardModel, actorUser.getGuild().getIdLong()));

View File

@@ -107,13 +107,13 @@ public class Rank extends AbstractConditionableCommand {
Long currentExpNeeded = experienceObj.getCurrentLevel().getExperienceNeeded();
Long experienceNeededToNextLevel = experienceLevelService.calculateExperienceToNextLevel(experienceObj.getCurrentLevel().getLevel(), experienceObj.getExperience());
Long nextLevelExperience = experienceLevelService.calculateNextLevel(experienceObj.getCurrentLevel().getLevel()).getExperienceNeeded();
Long levelExperience = nextLevelExperience - currentExpNeeded;
Long inLevelExperience = experienceObj.getExperience() - currentExpNeeded;
Long experienceNeededForCurrentLevel = nextLevelExperience - currentExpNeeded;
Long experienceWithinLevel = experienceObj.getExperience() - currentExpNeeded;
rankModel.setExperienceForCurrentLevel(currentExpNeeded);
rankModel.setCurrentLevelPercentage(((float) inLevelExperience / levelExperience) * 100);
rankModel.setLevelExperience(levelExperience);
rankModel.setCurrentLevelPercentage(((float) experienceWithinLevel / experienceNeededForCurrentLevel) * 100);
rankModel.setLevelExperience(experienceNeededForCurrentLevel);
rankModel.setExperienceToNextLevel(experienceNeededToNextLevel);
rankModel.setInLevelExperience(inLevelExperience);
rankModel.setInLevelExperience(experienceWithinLevel);
rankModel.setNextLevelExperience(nextLevelExperience);
return templateService.renderEmbedTemplate(RANK_POST_EMBED_TEMPLATE, rankModel, toRender.getGuild().getIdLong());
}

View File

@@ -48,7 +48,7 @@ public class AddMemberToChannelLevelAction implements LevelActionListener {
}
@Override
public boolean shouldExecute(AUserExperience aUserExperience, LevelAction levelAction) {
public boolean shouldExecute(AUserExperience aUserExperience, Integer oldLevel, LevelAction levelAction) {
return aUserExperience.getLevelOrDefault() >= levelAction.getLevel().getLevel();
}

View File

@@ -48,7 +48,7 @@ public class AddRoleLevelAction implements LevelActionListener {
}
@Override
public boolean shouldExecute(AUserExperience aUserExperience, LevelAction levelAction) {
public boolean shouldExecute(AUserExperience aUserExperience, Integer oldLevel, LevelAction levelAction) {
return aUserExperience.getLevelOrDefault() >= levelAction.getLevel().getLevel();
}

View File

@@ -48,7 +48,7 @@ public class RemoveMemberFromChannelLevelAction implements LevelActionListener {
}
@Override
public boolean shouldExecute(AUserExperience aUserExperience, LevelAction levelAction) {
public boolean shouldExecute(AUserExperience aUserExperience, Integer oldLevel, LevelAction levelAction) {
return aUserExperience.getLevelOrDefault() >= levelAction.getLevel().getLevel();
}

View File

@@ -49,7 +49,7 @@ public class RemoveRoleLevelAction implements LevelActionListener {
}
@Override
public boolean shouldExecute(AUserExperience aUserExperience, LevelAction levelAction) {
public boolean shouldExecute(AUserExperience aUserExperience, Integer oldLevel, LevelAction levelAction) {
return aUserExperience.getLevelOrDefault() >= levelAction.getLevel().getLevel();
}

View File

@@ -0,0 +1,106 @@
package dev.sheldan.abstracto.experience.listener;
import com.google.gson.Gson;
import dev.sheldan.abstracto.core.exception.InputFormatException;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MemberService;
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.core.utils.ParseUtils;
import dev.sheldan.abstracto.experience.model.LevelActionPayload;
import dev.sheldan.abstracto.experience.model.SendMessageToChannelLevelActionMessageModel;
import dev.sheldan.abstracto.experience.model.SendMessageToChannelLevelActionPayload;
import dev.sheldan.abstracto.experience.model.database.AUserExperience;
import dev.sheldan.abstracto.experience.model.database.LevelAction;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class SendMessageToChannelLevelAction implements LevelActionListener {
public static final String ACTION_NAME = "send_message_to_channel_above_level";
private static final String LEVEL_ACTION_SEND_MESSAGE_TEMPLATE_KEY = "levelAction_sendMessageToChannel_template";
@Autowired
private Gson gson;
@Autowired
private ChannelService channelService;
@Autowired
private TemplateService templateService;
@Autowired
private MemberService memberService;
@Override
public String getName() {
return ACTION_NAME;
}
@Override
public void apply(AUserExperience userExperience, LevelAction levelAction, MemberActionModification container) {
SendMessageToChannelLevelActionPayload payload = (SendMessageToChannelLevelActionPayload) levelAction.getLoadedPayload();
SendMessageToChannelLevelActionMessageModel.SendMessageToChannelLevelActionMessageModelBuilder messageModelBuilder = SendMessageToChannelLevelActionMessageModel
.builder()
.level(userExperience.getLevelOrDefault())
.templateKey(payload.getTemplateKey())
.experience(userExperience.getExperience());
ServerUser serverUser = ServerUser.fromAUserInAServer(userExperience.getUser());
memberService.getMemberInServerAsync(serverUser).thenAccept(member -> {
messageModelBuilder.memberDisplay(MemberDisplay.fromMember(member));
SendMessageToChannelLevelActionMessageModel model = messageModelBuilder.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(LEVEL_ACTION_SEND_MESSAGE_TEMPLATE_KEY, model, serverUser.getServerId());
GuildMessageChannel targetChannel = channelService.getMessageChannelFromServer(serverUser.getServerId(), payload.getChannelId());
FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, targetChannel)).thenAccept(unused -> {
log.info("Send message to channel action sent a message to channel {} for user {} in server {}.", payload.getChannelId(), serverUser.getUserId(), serverUser.getServerId());
}).exceptionally(throwable -> {
log.warn("Send message to channel action failed to send a message to channel {} for user {} in server {}.", payload.getChannelId(), serverUser.getUserId(), serverUser.getServerId(), throwable);
return null;
});
}).exceptionally(throwable -> {
log.warn("Failed to load member {} in server {} for send message level action towards channel {}.", serverUser.getUserId(), serverUser.getServerId(), payload.getChannelId());
return null;
});
}
@Override
public boolean shouldExecute(AUserExperience aUserExperience, Integer oldLevel, LevelAction levelAction) {
if(!oldLevel.equals(aUserExperience.getLevelOrDefault())) { // this means the user changed level now, this is the path from gaining a lot of experience
boolean jumpedLevelToMatch = oldLevel < levelAction.getLevel().getLevel() && aUserExperience.getLevelOrDefault() >= levelAction.getLevel().getLevel();
// this boolean means that the user did NOT have the action earlier, but does now (and more than that)
return jumpedLevelToMatch || aUserExperience.getLevelOrDefault().equals(levelAction.getLevel().getLevel()); // or the user matches the level _exactly_, this is the path from normally gaining experience
} else {
// This case is useful for re-joining, because this means, that the user did _not_ change level, and already is somewhere way above
return aUserExperience.getLevelOrDefault() >= levelAction.getLevel().getLevel();
}
}
@Override
public void prepareAction(LevelAction levelAction) {
levelAction.setLoadedPayload(gson.fromJson(levelAction.getPayload(), SendMessageToChannelLevelActionPayload.class));
}
@Override
public LevelActionPayload createPayload(Guild guild, String input) {
if(!input.contains(";")) {
throw new InputFormatException(input, "<#channel>;template_key");
}
String channelPart = input.substring(0, input.indexOf(";"));
GuildChannel channel = ParseUtils.parseGuildChannelFromText(channelPart, guild);
String templateKey = input.substring(input.indexOf(";") + 1);
return SendMessageToChannelLevelActionPayload
.builder()
.channelId(channel.getIdLong())
.templateKey(templateKey)
.build();
}
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.abstracto.experience.model;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class SendMessageToChannelLevelActionMessageModel implements LevelActionPayload {
private MemberDisplay memberDisplay;
private Integer level;
private Long experience;
private String templateKey;
}

View File

@@ -0,0 +1,11 @@
package dev.sheldan.abstracto.experience.model;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class SendMessageToChannelLevelActionPayload implements LevelActionPayload {
private Long channelId;
private String templateKey;
}

View File

@@ -10,9 +10,15 @@ import lombok.Getter;
@Builder
public class UserExperienceDisplay {
private UserDisplay member;
private Long id;
private String id;
private Integer rank;
private Integer level;
private Long experience;
private Long messages;
private RoleDisplay role;
private Long experienceToNextLevel;
private Long experienceOnCurrentLevel;
private Long currentLevelExperienceNeeded;
private Float percentage;
private Long nextLevelExperienceNeeded;
}

View File

@@ -337,8 +337,8 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
.builder()
.build();
boolean userChangesLevel = !Objects.equals(newLevel.getLevel(), aUserExperience.getCurrentLevel().getLevel());
Integer oldLevel = aUserExperience.getCurrentLevel() != null ? aUserExperience.getCurrentLevel().getLevel() : 0;
if(userChangesLevel) {
Integer oldLevel = aUserExperience.getCurrentLevel() != null ? aUserExperience.getCurrentLevel().getLevel() : 0;
log.info("User {} in server {} changed level. New {}, Old {}.", member.getIdLong(),
member.getGuild().getIdLong(), newLevel.getLevel(),
oldLevel);
@@ -361,7 +361,7 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
.newRole(oldRoleId != null ? RoleDisplay.fromRole(oldRoleId) : null)
.newRole(newRoleId != null ? RoleDisplay.fromRole(newRoleId) : null)
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate("experience_level_up_notification", model);
MessageToSend messageToSend = templateService.renderEmbedTemplate("experience_level_up_notification", model, serverId);
FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, message.getChannel())).thenAccept(unused -> {
log.info("Sent level up notification to user {} in server {} in channel {}.", member.getIdLong(), serverId, message.getChannel().getIdLong());
}).exceptionally(throwable -> {
@@ -373,7 +373,7 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
}
aUserExperience.setMessageCount(aUserExperience.getMessageCount() + 1L);
if(userChangesLevel && featureModeService.featureModeActive(ExperienceFeatureDefinition.EXPERIENCE, server, ExperienceFeatureMode.LEVEL_ACTION)) {
levelActionService.applyLevelActionsToUser(aUserExperience)
levelActionService.applyLevelActionsToUser(aUserExperience, oldLevel)
.thenAccept(unused -> {
log.info("Executed level actions for user {}.", userInServerId);
})

View File

@@ -49,6 +49,11 @@ public class LevelActionServiceBean implements LevelActionService {
@Override
public CompletableFuture<Void> applyLevelActionsToUser(AUserExperience user) {
return applyLevelActionsToUser(user, user.getLevelOrDefault());
}
@Override
public CompletableFuture<Void> applyLevelActionsToUser(AUserExperience user, Integer oldLevel) {
if(levelActions == null || levelActions.isEmpty()) {
return CompletableFuture.completedFuture(null);
}
@@ -60,8 +65,16 @@ public class LevelActionServiceBean implements LevelActionService {
Map<Integer, List<LevelAction>> actionConfigMap = new HashMap<>();
Map<String, LevelActionListener> actionStringListenerMap = levelActions
.stream()
.collect(Collectors.toMap(a -> a.getName().toLowerCase(), Function.identity()));
levelActionsOfUserInServer.forEach(levelAction -> {
if(levelAction.getLevel().getLevel() > user.getLevelOrDefault()) {
LevelActionListener listener = actionStringListenerMap.get(levelAction.getAction());
if(listener == null) { // if for some reason the config is still in the database, but we don't have code for it anymore
return;
}
if(!listener.shouldExecute(user, oldLevel, levelAction)) {
return;
}
if(actionConfigMap.containsKey(levelAction.getLevel().getLevel())) {
@@ -73,9 +86,6 @@ public class LevelActionServiceBean implements LevelActionService {
}
});
Map<String, LevelActionListener> actionStringListenerMap = levelActions
.stream()
.collect(Collectors.toMap(a -> a.getName().toLowerCase(), Function.identity()));
List<Integer> levels = actionConfigMap
.keySet()

View File

@@ -17,4 +17,6 @@ abstracto.featureModes.levelUpNotification.enabled=false
abstracto.featureModes.levelAction.featureName=experience
abstracto.featureModes.levelAction.mode=levelAction
abstracto.featureModes.levelAction.enabled=false
abstracto.featureModes.levelAction.enabled=false
abstracto.experience.leaderboard.externalUrl=${FRONTEND_BASE:}

View File

@@ -0,0 +1,84 @@
package dev.sheldan.abstracto.experience.listener;
import dev.sheldan.abstracto.experience.model.database.AExperienceLevel;
import dev.sheldan.abstracto.experience.model.database.AUserExperience;
import dev.sheldan.abstracto.experience.model.database.LevelAction;
import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class AddRoleLevelActionTest {
@InjectMocks
private AddRoleLevelAction action;
@Mock
private AUserExperience exp;
@Mock
private LevelAction levelAction;
@Mock
private AExperienceLevel level;
private final Integer LOW_LEVEL = 1;
private final Integer MID_LEVEL = 2;
private final Integer HIGH_LEVEL = 3;
@Before
public void setup() {
when(levelAction.getLevel()).thenReturn(level);
}
@Test // rejoin too low case
public void noLevelChangeActionNotReached() {
executeTest(LOW_LEVEL, LOW_LEVEL, HIGH_LEVEL, false);
}
@Test // re-join exact case
public void noLevelChangeActionReached() {
executeTest(LOW_LEVEL, LOW_LEVEL, LOW_LEVEL, true);
}
@Test // normal leveling, higher action
public void levelChangeActionNotReached() {
executeTest(LOW_LEVEL, MID_LEVEL, HIGH_LEVEL, false);
}
@Test // normal leveling
public void levelChangeActionReached() {
executeTest(LOW_LEVEL, MID_LEVEL, MID_LEVEL, true);
}
@Test // a case for this is a large experience jump
public void levelChangeActionOverJumped() {
executeTest(LOW_LEVEL, HIGH_LEVEL, MID_LEVEL, true);
}
@Test // a case for this is a re-join
public void noLevelChangeActionOverJumped() {
executeTest(HIGH_LEVEL, HIGH_LEVEL, LOW_LEVEL, true);
}
@Test // we dont want to re-execute previous actions (previous = lower level)
public void levelChangeActionEqualsPrevious() {
executeTest(LOW_LEVEL, MID_LEVEL, LOW_LEVEL, true);
}
@Test // we dont want to re-execute previous actions (previous = way lower level)
public void levelChangeActionBelow() {
executeTest(MID_LEVEL, HIGH_LEVEL, LOW_LEVEL, true);
}
private void executeTest(Integer oldLevel, Integer currentLevel, Integer actionLevel, boolean expected) {
when(exp.getLevelOrDefault()).thenReturn(currentLevel);
when(level.getLevel()).thenReturn(actionLevel);
Assertions.assertThat(action.shouldExecute(exp, oldLevel, levelAction)).isEqualTo(expected);
}
}

View File

@@ -0,0 +1,86 @@
package dev.sheldan.abstracto.experience.listener;
import dev.sheldan.abstracto.experience.model.database.AExperienceLevel;
import dev.sheldan.abstracto.experience.model.database.AUserExperience;
import dev.sheldan.abstracto.experience.model.database.LevelAction;
import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class RemoveMemberFromChannelLevelActionTest {
@InjectMocks
private SendMessageToChannelLevelAction action;
@Mock
private AUserExperience exp;
@Mock
private LevelAction levelAction;
@Mock
private AExperienceLevel level;
private final Integer LOW_LEVEL = 1;
private final Integer MID_LEVEL = 2;
private final Integer HIGH_LEVEL = 3;
@Before
public void setup() {
when(levelAction.getLevel()).thenReturn(level);
}
@Test // rejoin too low case
public void noLevelChangeActionNotReached() {
executeTest(LOW_LEVEL, LOW_LEVEL, HIGH_LEVEL, false);
}
@Test // re-join exact case
public void noLevelChangeActionReached() {
executeTest(LOW_LEVEL, LOW_LEVEL, LOW_LEVEL, true);
}
@Test // normal leveling, higher action
public void levelChangeActionNotReached() {
executeTest(LOW_LEVEL, MID_LEVEL, HIGH_LEVEL, false);
}
@Test // normal leveling
public void levelChangeActionReached() {
executeTest(LOW_LEVEL, MID_LEVEL, MID_LEVEL, true);
}
@Test // a case for this is a large experience jump
public void levelChangeActionOverJumped() {
executeTest(LOW_LEVEL, HIGH_LEVEL, MID_LEVEL, true);
}
@Test // a case for this is a re-join
public void noLevelChangeActionOverJumped() {
executeTest(HIGH_LEVEL, HIGH_LEVEL, LOW_LEVEL, true);
}
@Test // we dont want to re-execute previous actions (previous = lower level)
public void levelChangeActionEqualsPrevious() {
executeTest(LOW_LEVEL, MID_LEVEL, LOW_LEVEL, false);
}
@Test // we dont want to re-execute previous actions (previous = way lower level)
public void levelChangeActionBelow() {
executeTest(MID_LEVEL, HIGH_LEVEL, LOW_LEVEL, false);
}
private void executeTest(Integer oldLevel, Integer currentLevel, Integer actionLevel, boolean expected) {
when(exp.getLevelOrDefault()).thenReturn(currentLevel);
when(level.getLevel()).thenReturn(actionLevel);
Assertions.assertThat(action.shouldExecute(exp, oldLevel, levelAction)).isEqualTo(expected);
}
}

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking</artifactId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.experience.exception;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.templating.Templatable;
public class LevelActionAlreadyExistsException extends AbstractoRunTimeException implements Templatable {
public LevelActionAlreadyExistsException() {
super("Level action already exists.");
}
@Override
public String getTemplateName() {
return "level_action_already_exists_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -4,15 +4,13 @@ import dev.sheldan.abstracto.experience.model.LevelActionPayload;
import dev.sheldan.abstracto.experience.model.database.AUserExperience;
import dev.sheldan.abstracto.experience.model.database.LevelAction;
import net.dv8tion.jda.api.entities.Guild;
import org.springframework.stereotype.Component;
@Component
public interface LevelActionListener {
String getName();
void apply(AUserExperience userExperience, LevelAction levelAction, MemberActionModification container);
boolean shouldExecute(AUserExperience aUserExperience, LevelAction levelAction);
boolean shouldExecute(AUserExperience aUserExperience, Integer oldLevel, LevelAction levelAction);
void prepareAction(LevelAction levelAction);

View File

@@ -24,4 +24,5 @@ public class LeaderBoardModel extends SlimUserInitiatedServerContext {
* The {@link LeaderBoardEntryModel} containing the leaderboard information executing the command.
*/
private LeaderBoardEntryModel userExecuting;
private String leaderboardUrl;
}

View File

@@ -12,6 +12,7 @@ import java.util.concurrent.CompletableFuture;
public interface LevelActionService {
CompletableFuture<Void> applyLevelActionsToUser(AUserExperience user);
CompletableFuture<Void> applyLevelActionsToUser(AUserExperience user, Integer oldLevel);
List<String> getAvailableLevelActions();
Optional<LevelActionListener> getLevelActionListenerForName(String name);

View File

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

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>giveaway</artifactId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<artifactId>giveaway-impl</artifactId>

View File

@@ -130,7 +130,7 @@ public class GiveawayServiceBean implements GiveawayService {
giveawayMessageModel.setJoinedUserCount(giveaway.getParticipants().size() + 1L);
Long giveawayId = giveaway.getGiveawayId().getId();
log.info("Adding giveaway participating of user {} to giveaway {} in server {}.", member.getIdLong(), giveawayId, member.getGuild().getIdLong());
MessageToSend messageToSend = templateService.renderEmbedTemplate(GIVEAWAY_MESSAGE_TEMPLATE_KEY, giveawayMessageModel);
MessageToSend messageToSend = templateService.renderEmbedTemplate(GIVEAWAY_MESSAGE_TEMPLATE_KEY, giveawayMessageModel, member.getGuild().getIdLong());
return channelService.editEmbedMessageInAChannel(messageToSend.getEmbeds().get(0), messageChannel, giveaway.getMessageId())
.thenAccept(message -> {
self.persistAddedParticipant(member, giveawayId);
@@ -183,13 +183,13 @@ public class GiveawayServiceBean implements GiveawayService {
.winners(winnerDisplays)
.build();
log.info("Sending result message for giveaway {} in server {}.", giveawayId, serverId);
MessageToSend messageToSend = templateService.renderEmbedTemplate(GIVEAWAY_RESULT_MESSAGE_TEMPLATE_KEY, resultModel);
MessageToSend messageToSend = templateService.renderEmbedTemplate(GIVEAWAY_RESULT_MESSAGE_TEMPLATE_KEY, resultModel, serverId);
List<CompletableFuture<Message>> resultFutures = channelService.sendMessageEmbedToSendToAChannel(messageToSend, giveaway.getGiveawayChannel());
GiveawayMessageModel giveawayMessageModel = GiveawayMessageModel.fromGiveaway(giveaway);
giveawayMessageModel.setWinners(winnerDisplays);
giveawayMessageModel.setEnded(true);
MessageToSend giveawayMessageToSend = templateService.renderEmbedTemplate(GIVEAWAY_MESSAGE_TEMPLATE_KEY, giveawayMessageModel);
MessageToSend giveawayMessageToSend = templateService.renderEmbedTemplate(GIVEAWAY_MESSAGE_TEMPLATE_KEY, giveawayMessageModel, serverId);
log.info("Updating original giveaway message for giveaway {} in server {}.", giveawayId, serverId);
GuildMessageChannel messageChannel = channelService.getMessageChannelFromServer(giveaway.getServer().getId(), giveaway.getGiveawayChannel().getId());
CompletableFuture<Message> giveawayUpdateFuture = channelService.editMessageInAChannelFuture(giveawayMessageToSend, messageChannel, giveaway.getMessageId());
@@ -208,7 +208,7 @@ public class GiveawayServiceBean implements GiveawayService {
GiveawayMessageModel giveawayMessageModel = GiveawayMessageModel.fromGiveaway(giveaway);
giveawayMessageModel.setCancelled(true);
schedulerService.stopTrigger(giveaway.getReminderTriggerKey());
MessageToSend giveawayMessageToSend = templateService.renderEmbedTemplate(GIVEAWAY_MESSAGE_TEMPLATE_KEY, giveawayMessageModel);
MessageToSend giveawayMessageToSend = templateService.renderEmbedTemplate(GIVEAWAY_MESSAGE_TEMPLATE_KEY, giveawayMessageModel, serverId);
GuildMessageChannel messageChannel = channelService.getMessageChannelFromServer(giveaway.getServer().getId(), giveaway.getGiveawayChannel().getId());
log.debug("Updating original giveaway message to consider cancellation for giveaway {} in server {}.", giveawayId, serverId);

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>giveaway</artifactId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<artifactId>giveaway-int</artifactId>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<artifactId>giveaway</artifactId>

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>image-generation</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<artifactId>image-generation-impl</artifactId>

View File

@@ -58,7 +58,7 @@ public class AmongusText extends AbstractConditionableCommand {
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
String text = (String) commandContext.getParameters().getParameters().get(0);
File amongusTextImage = imageGenerationService.getAmongusTextImage(text);
MessageToSend messageToSend = templateService.renderEmbedTemplate(AMONGUS_TEXT_EMBED_TEMPLATE_KEY, new Object());
MessageToSend messageToSend = templateService.renderEmbedTemplate(AMONGUS_TEXT_EMBED_TEMPLATE_KEY, new Object(), commandContext.getGuild().getIdLong());
// template support does not support binary files
AttachedFile file = AttachedFile
.builder()
@@ -76,7 +76,7 @@ public class AmongusText extends AbstractConditionableCommand {
event.deferReply().queue();
String text = slashCommandParameterService.getCommandOption(TEXT_PARAMETER_KEY, event, String.class);
File amongusTextImage = imageGenerationService.getAmongusTextImage(text);
MessageToSend messageToSend = templateService.renderEmbedTemplate(AMONGUS_TEXT_EMBED_TEMPLATE_KEY, new Object());
MessageToSend messageToSend = templateService.renderEmbedTemplate(AMONGUS_TEXT_EMBED_TEMPLATE_KEY, new Object(), event.getGuild().getIdLong());
// template support does not support binary files
AttachedFile file = AttachedFile
.builder()

View File

@@ -2,11 +2,13 @@ package dev.sheldan.abstracto.imagegeneration.command;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CombinedParameterEntry;
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.command.handler.parameter.CombinedParameter;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
@@ -21,16 +23,18 @@ import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationFeatureDefini
import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationSlashCommandNames;
import dev.sheldan.abstracto.imagegeneration.service.ImageGenerationService;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import static dev.sheldan.abstracto.core.command.config.Parameter.ADDITIONAL_TYPES_KEY;
@Component
public class Bonk extends AbstractConditionableCommand {
public static final String MEMBER_PARAMETER_KEY = "member";
@@ -65,10 +69,14 @@ public class Bonk extends AbstractConditionableCommand {
if(parameters.isEmpty()) {
member = commandContext.getAuthor();
} else {
member = (Member) parameters.get(0);
if(parameters.get(0) instanceof Message) {
member = ((Message) parameters.get(0)).getMember();
} else {
member = (Member) parameters.get(0);
}
}
File bonkGifFile = imageGenerationService.getBonkGif(member.getEffectiveAvatar().getUrl(imageSize));
MessageToSend messageToSend = templateService.renderEmbedTemplate(BONK_EMBED_TEMPLATE_KEY, new Object());
MessageToSend messageToSend = templateService.renderEmbedTemplate(BONK_EMBED_TEMPLATE_KEY, new Object(), commandContext.getGuild().getIdLong());
// template support does not support binary files
AttachedFile file = AttachedFile
.builder()
@@ -91,7 +99,7 @@ public class Bonk extends AbstractConditionableCommand {
targetMember = event.getMember();
}
File bonkGifFile = imageGenerationService.getBonkGif(targetMember.getEffectiveAvatar().getUrl(imageSize));
MessageToSend messageToSend = templateService.renderEmbedTemplate(BONK_EMBED_TEMPLATE_KEY, new Object());
MessageToSend messageToSend = templateService.renderEmbedTemplate(BONK_EMBED_TEMPLATE_KEY, new Object(), event.getGuild().getIdLong());
// template support does not support binary files
AttachedFile file = AttachedFile
.builder()
@@ -107,10 +115,13 @@ public class Bonk extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
Map<String, Object> parameterAlternatives = new HashMap<>();
parameterAlternatives.put(ADDITIONAL_TYPES_KEY, Arrays.asList(CombinedParameterEntry.messageParameter(Message.class), CombinedParameterEntry.parameter(Member.class)));
Parameter memberParameter = Parameter
.builder()
.name(MEMBER_PARAMETER_KEY)
.type(Member.class)
.type(CombinedParameter.class)
.additionalInfo(parameterAlternatives)
.templated(true)
.optional(true)
.build();

View File

@@ -2,11 +2,13 @@ package dev.sheldan.abstracto.imagegeneration.command;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CombinedParameterEntry;
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.command.handler.parameter.CombinedParameter;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
@@ -21,16 +23,18 @@ import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationFeatureDefini
import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationSlashCommandNames;
import dev.sheldan.abstracto.imagegeneration.service.ImageGenerationService;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import static dev.sheldan.abstracto.core.command.config.Parameter.ADDITIONAL_TYPES_KEY;
@Component
public class Pat extends AbstractConditionableCommand {
public static final String MEMBER_PARAMETER_KEY = "member";
@@ -65,10 +69,14 @@ public class Pat extends AbstractConditionableCommand {
if(parameters.isEmpty()) {
member = commandContext.getAuthor();
} else {
member = (Member) parameters.get(0);
if(parameters.get(0) instanceof Message) {
member = ((Message) parameters.get(0)).getMember();
} else {
member = (Member) parameters.get(0);
}
}
File patGifFile = imageGenerationService.getPatGif(member.getEffectiveAvatar().getUrl(imageSize));
MessageToSend messageToSend = templateService.renderEmbedTemplate(PAT_EMBED_TEMPLATE_KEY, new Object());
MessageToSend messageToSend = templateService.renderEmbedTemplate(PAT_EMBED_TEMPLATE_KEY, new Object(), commandContext.getGuild().getIdLong());
// template support does not support binary files
AttachedFile file = AttachedFile
.builder()
@@ -91,7 +99,7 @@ public class Pat extends AbstractConditionableCommand {
targetMember = event.getMember();
}
File patGifFile = imageGenerationService.getPatGif(targetMember.getEffectiveAvatar().getUrl(imageSize));
MessageToSend messageToSend = templateService.renderEmbedTemplate(PAT_EMBED_TEMPLATE_KEY, new Object());
MessageToSend messageToSend = templateService.renderEmbedTemplate(PAT_EMBED_TEMPLATE_KEY, new Object(), event.getGuild().getIdLong());
// template support does not support binary files
AttachedFile file = AttachedFile
.builder()
@@ -107,10 +115,13 @@ public class Pat extends AbstractConditionableCommand {
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
Map<String, Object> parameterAlternatives = new HashMap<>();
parameterAlternatives.put(ADDITIONAL_TYPES_KEY, Arrays.asList(CombinedParameterEntry.messageParameter(Message.class), CombinedParameterEntry.parameter(Member.class)));
Parameter memberParameter = Parameter
.builder()
.name(MEMBER_PARAMETER_KEY)
.type(Member.class)
.type(CombinedParameter.class)
.additionalInfo(parameterAlternatives)
.templated(true)
.optional(true)
.build();

View File

@@ -68,7 +68,7 @@ public class Triggered extends AbstractConditionableCommand {
member = (Member) parameters.get(0);
}
File triggeredGifFile = imageGenerationService.getTriggeredGif(member.getEffectiveAvatar().getUrl(imageSize));
MessageToSend messageToSend = templateService.renderEmbedTemplate(TRIGGERED_EMBED_TEMPLATE_KEY, new Object());
MessageToSend messageToSend = templateService.renderEmbedTemplate(TRIGGERED_EMBED_TEMPLATE_KEY, new Object(), commandContext.getGuild().getIdLong());
// template support does not support binary files
AttachedFile file = AttachedFile
.builder()
@@ -91,7 +91,7 @@ public class Triggered extends AbstractConditionableCommand {
targetMember = event.getMember();
}
File triggeredGifFile = imageGenerationService.getTriggeredGif(targetMember.getEffectiveAvatar().getUrl(imageSize));
MessageToSend messageToSend = templateService.renderEmbedTemplate(TRIGGERED_EMBED_TEMPLATE_KEY, new Object());
MessageToSend messageToSend = templateService.renderEmbedTemplate(TRIGGERED_EMBED_TEMPLATE_KEY, new Object(), event.getGuild().getIdLong());
// template support does not support binary files
AttachedFile file = AttachedFile
.builder()

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>image-generation</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<artifactId>image-generation-int</artifactId>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<artifactId>image-generation</artifactId>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>invite-filter</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>invite-filter</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -18,7 +18,7 @@
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>moderation-int</artifactId>
<version>1.5.28</version>
<version>1.5.51</version>
<scope>compile</scope>
</dependency>
</dependencies>

View File

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

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>link-embed</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>link-embed</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

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

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>logging</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,13 +5,14 @@ import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncLeaveListener;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.listener.MemberLeaveModel;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
import dev.sheldan.abstracto.core.service.PostTargetService;
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.logging.config.LoggingFeatureDefinition;
import dev.sheldan.abstracto.logging.config.LoggingPostTarget;
import dev.sheldan.abstracto.logging.model.template.MemberLeaveLogModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -40,10 +41,10 @@ public class LeaveLogger implements AsyncLeaveListener {
.userId(listenerModel.getUser().getIdLong())
.serverId(listenerModel.getServerId())
.build();
MemberLeaveModel model = MemberLeaveModel
MemberLeaveLogModel model = MemberLeaveLogModel
.builder()
.leavingUser(leavingUser)
.user(listenerModel.getUser())
.user(UserDisplay.fromUser(listenerModel.getUser()))
.build();
log.debug("Logging leave event for user {} in server {}.", listenerModel.getUser().getIdLong(), listenerModel.getServerId());
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_LEAVE_TEMPLATE, model, listenerModel.getServerId());

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>logging</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -1,13 +1,15 @@
package dev.sheldan.abstracto.logging.model.template;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Member;
@Getter
@Setter
@Builder
public class MemberLeaveLogModel {
private Member member;
private ServerUser leavingUser;
private UserDisplay user;
}

View File

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

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>moderation</artifactId>
<version>1.5.28</version>
<version>1.5.51</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -116,7 +116,7 @@ public class Infractions extends AbstractConditionableCommand {
} else if(slashCommandParameterService.hasCommandOptionWithFullType(USER_PARAMETER, event, OptionType.STRING)){
String userIdStr = slashCommandParameterService.getCommandOption(USER_PARAMETER, event, User.class, String.class);
Long userId = Long.parseLong(userIdStr);
AUserInAServer userInServer = userInServerManagementService.createUserInServer(event.getGuild().getIdLong(), userId);
AUserInAServer userInServer = userInServerManagementService.loadOrCreateUser(event.getGuild().getIdLong(), userId);
infractions = infractionManagementService.getInfractionsForUser(userInServer);
} else {

View File

@@ -29,6 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -73,11 +74,15 @@ public class Mute extends AbstractConditionableCommand {
Duration duration = (Duration) parameters.get(1);
String defaultReason = templateService.renderSimpleTemplate(MUTE_DEFAULT_REASON_TEMPLATE, guild.getIdLong());
String reason = parameters.size() == 3 ? (String) parameters.get(2) : defaultReason;
Instant oldTimeoutDate = null;
if(member.getTimeOutEnd() != null && member.isTimedOut()) {
oldTimeoutDate = member.getTimeOutEnd().toInstant();
}
ServerUser userToMute = ServerUser.fromMember(member);
ServerUser mutingUser = ServerUser.fromMember(commandContext.getAuthor());
Long serverId = commandContext.getGuild().getIdLong();
ServerChannelMessage serverChannelMessage = ServerChannelMessage.fromMessage(commandContext.getMessage());
return muteService.muteMemberWithLog(userToMute, mutingUser, reason, duration, commandContext.getGuild(), serverChannelMessage)
return muteService.muteMemberWithLog(userToMute, mutingUser, reason, duration, commandContext.getGuild(), serverChannelMessage, oldTimeoutDate)
.thenCompose(muteResult -> {
if(muteResult == NOTIFICATION_FAILED) {
MessageToSend errorNotification = templateService.renderEmbedTemplate(MUTE_NOTIFICATION_NOT_POSSIBLE_TEMPLATE_KEY, new Object(), serverId);

View File

@@ -54,7 +54,7 @@ public class MyWarnings extends AbstractConditionableCommand {
.totalWarnCount(totalWarnCount)
.currentWarnCount(currentWarnCount)
.build();
channelService.sendEmbedTemplateInTextChannelList(MY_WARNINGS_RESPONSE_EMBED_TEMPLATE, model, commandContext.getChannel());
channelService.sendEmbedTemplateInMessageChannel(MY_WARNINGS_RESPONSE_EMBED_TEMPLATE, model, commandContext.getChannel());
return CommandResult.fromIgnored();
}

View File

@@ -11,7 +11,6 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.service.UserService;
import dev.sheldan.abstracto.moderation.config.ModerationModuleDefinition;
import dev.sheldan.abstracto.moderation.config.ModerationSlashCommandNames;
@@ -51,7 +50,7 @@ public class UnBan extends AbstractConditionableCommand {
String userIdStr = (String) parameters.get(0);
Long userId = Long.parseLong(userIdStr);
return userService.retrieveUserForId(userId)
.thenCompose(user -> banService.unBanUserWithNotification(userId, ServerUser.fromMember(commandContext.getAuthor()), commandContext.getGuild()))
.thenCompose(user -> banService.unbanUser(commandContext.getGuild(), user, commandContext.getAuthor()))
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@@ -60,7 +59,7 @@ public class UnBan extends AbstractConditionableCommand {
String userIdStr = slashCommandParameterService.getCommandOption(USER_PARAMETER, event, String.class);
Long userId = Long.parseLong(userIdStr);
return userService.retrieveUserForId(userId)
.thenCompose(user -> banService.unBanUserWithNotification(userId, ServerUser.fromMember(event.getMember()), event.getGuild()))
.thenCompose(user -> banService.unbanUser(event.getGuild(), user, event.getMember()))
.thenCompose(unused -> interactionService.replyEmbed(UN_BAN_RESPONSE, event))
.thenApply(interactionHook -> CommandResult.fromSuccess());
}

View File

@@ -12,7 +12,6 @@ import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.moderation.config.ModerationModuleDefinition;
import dev.sheldan.abstracto.moderation.config.ModerationSlashCommandNames;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
@@ -42,9 +41,6 @@ public class UnMute extends AbstractConditionableCommand {
@Autowired
private InteractionService interactionService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();

View File

@@ -90,7 +90,7 @@ public class UserNotes extends AbstractConditionableCommand {
CompletableFuture<List<NoteEntryModel>> listCompletableFuture = userNotesConverter.fromNotes(userNotes);
return listCompletableFuture.thenCompose(noteEntryModels -> {
model.setUserNotes(noteEntryModels);
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(USER_NOTES_RESPONSE_TEMPLATE, model, commandContext.getChannel()))
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannel(USER_NOTES_RESPONSE_TEMPLATE, model, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromIgnored());
});
}

View File

@@ -76,12 +76,12 @@ public class BanModerationActionModalListener implements ModalInteractionListene
.getEvent()
.getValues()
.stream()
.filter(modalMapping -> modalMapping.getId().equals(payload.getDurationInputId()))
.filter(modalMapping -> modalMapping.getId().equals(payload.getReasonInputId()))
.map(ModalMapping::getAsString)
.findFirst()
.orElse(null);
if(StringUtils.isBlank(tempReason)) {
reason = templateService.renderSimpleTemplate(DEFAULT_BAN_REASON_TEMPLATE_KEY);
reason = templateService.renderSimpleTemplate(DEFAULT_BAN_REASON_TEMPLATE_KEY, model.getServerId());
} else {
reason = tempReason;
}

View File

@@ -78,7 +78,7 @@ public class HoneyPotRoleAddedListener implements RoleAddedListener {
.memberDisplay(MemberDisplay.fromMember(model.getTargetMember()))
.roleDisplay(RoleDisplay.fromRole(model.getRole()))
.build();
String banReason = templateService.renderTemplate(HONEYPOT_BAN_REASON_TEMPLATE, reasonModel);
String banReason = templateService.renderTemplate(HONEYPOT_BAN_REASON_TEMPLATE, reasonModel, model.getServerId());
banService.banUserWithNotification(model.getTargetUser(), banReason, ServerUser.fromMember(model.getTargetMember().getGuild().getSelfMember()),
model.getTargetMember().getGuild(), Duration.ofDays(7)).thenAccept(banResult -> {
log.info("Banned user {} in guild {} due to role {}.", model.getTargetUser().getUserId(), model.getTargetUser().getServerId(), model.getRoleId());

View File

@@ -0,0 +1,43 @@
package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMemberKickedListener;
import dev.sheldan.abstracto.core.models.listener.MemberKickedModel;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.model.template.command.KickLogModel;
import dev.sheldan.abstracto.moderation.service.KickService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class MemberKickedListener implements AsyncMemberKickedListener {
@Autowired
private KickService kickService;
@Override
public FeatureDefinition getFeature() {
return ModerationFeatureDefinition.MODERATION;
}
@Override
public DefaultListenerResult execute(MemberKickedModel eventModel) {
log.info("Notifying about kicked of user {} in guild {}.", eventModel.getKickedServerUser().getUserId(), eventModel.getServerId());
if(eventModel.getKickingServerUser().getUserId() == eventModel.getGuild().getJDA().getSelfUser().getIdLong()) {
log.info("Skipping logging kicked event about user {} in guild {}, because it was done by us.", eventModel.getKickedServerUser().getUserId(), eventModel.getGuild().getIdLong());
return DefaultListenerResult.IGNORED;
}
KickLogModel model = KickLogModel
.builder()
.kickedUser(eventModel.getKickedUser() != null ? UserDisplay.fromUser(eventModel.getKickedUser()) : UserDisplay.fromId(eventModel.getKickedServerUser().getUserId()))
.kickingUser(eventModel.getKickingUser() != null ? UserDisplay.fromUser(eventModel.getKickingUser()) : UserDisplay.fromServerUser(eventModel.getKickingServerUser()))
.reason(eventModel.getReason())
.build();
kickService.sendKicklog(model, eventModel.getServerId());
return DefaultListenerResult.PROCESSED;
}
}

View File

@@ -1,139 +0,0 @@
package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMemberTimeoutUpdatedListener;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.listener.MemberTimeoutUpdatedModel;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.PostTargetService;
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.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.config.posttarget.MutingPostTarget;
import dev.sheldan.abstracto.moderation.model.template.command.MuteListenerModel;
import dev.sheldan.abstracto.moderation.service.MuteServiceBean;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.audit.ActionType;
import net.dv8tion.jda.api.audit.AuditLogEntry;
import net.dv8tion.jda.api.audit.AuditLogKey;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class MemberTimeoutListener implements AsyncMemberTimeoutUpdatedListener {
@Autowired
private TemplateService templateService;
@Autowired
private PostTargetService postTargetService;
@Autowired
private MemberTimeoutListener self;
@Autowired
private MemberService memberService;
@Override
public DefaultListenerResult execute(MemberTimeoutUpdatedModel model) {
Guild guild = model.getGuild();
guild.retrieveAuditLogs()
.type(ActionType.MEMBER_UPDATE)
.limit(10)
.queue(auditLogEntries -> {
CompletableFuture<Void> notificationFuture = null;
if(auditLogEntries.isEmpty()) {
log.info("Did not find recent timeouts in guild {}.", model.getServerId());
notificationFuture = self.sendMutingUpdateNotification(model, null);
} else {
Optional<AuditLogEntry> timeoutEntryOptional = auditLogEntries
.stream()
.filter(auditLogEntry -> auditLogEntry.getChangeByKey(AuditLogKey.MEMBER_TIME_OUT) != null
&& auditLogEntry.getTargetIdLong() == model.getTimeoutUser().getUserId())
.findFirst();
if(timeoutEntryOptional.isPresent()) {
AuditLogEntry auditLogEntry = timeoutEntryOptional.get();
User responsibleUser = auditLogEntry.getUser();
if(guild.getSelfMember().getIdLong() != responsibleUser.getIdLong()) {
notificationFuture = self.sendMutingUpdateNotification(model, auditLogEntry);
}
} else {
notificationFuture = self.sendMutingUpdateNotification(model, null);
}
}
if(notificationFuture != null) {
notificationFuture.thenAccept(unused -> {
log.info("Sent notification about timeout change {} -> {} of user {} in server {}.",
model.getOldTimeout(), model.getNewTimeout(), model.getTimeoutUser().getUserId(), model.getServerId());
}).exceptionally(throwable -> {
log.info("Sent notification about timeout change {} -> {} of user {} in server {}.",
model.getOldTimeout(), model.getNewTimeout(), model.getTimeoutUser().getUserId(), model.getServerId());
return null;
});
}
});
return DefaultListenerResult.PROCESSED;
}
@Transactional
public CompletableFuture<Void> sendMutingUpdateNotification(MemberTimeoutUpdatedModel model, AuditLogEntry logEntry) {
User responsibleUser;
Guild guild = model.getGuild();
CompletableFuture<Member> future;
String reason;
if(logEntry != null) {
responsibleUser = logEntry.getUser();
if(responsibleUser != null) {
ServerUser responsibleServerUser = ServerUser
.builder()
.serverId(guild.getIdLong())
.isBot(responsibleUser.isBot())
.userId(responsibleUser.getIdLong())
.build();
future = memberService.retrieveMemberInServer(responsibleServerUser);
} else {
future = CompletableFuture.completedFuture(null);
}
reason = logEntry.getReason();
} else {
future = CompletableFuture.completedFuture(null);
reason = null;
}
CompletableFuture<Void> returningFuture = new CompletableFuture<>();
future.whenComplete((aVoid, throwable) -> {
try {
MuteListenerModel muteLogModel = MuteListenerModel
.builder()
.muteTargetDate(model.getNewTimeout() != null ? model.getNewTimeout().toInstant() : null)
.oldMuteTargetDate(model.getOldTimeout() != null ? model.getOldTimeout().toInstant() : null)
.mutingUser(future.isCompletedExceptionally() ? null : MemberDisplay.fromMember(future.join()))
.mutedUser(MemberDisplay.fromMember(model.getMember()))
.reason(reason)
.build();
MessageToSend message = templateService.renderEmbedTemplate(MuteServiceBean.MUTE_LOG_TEMPLATE, muteLogModel, guild.getIdLong());
FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, model.getServerId()));
returningFuture.complete(null);
} catch (Exception exception) {
log.error("Failed to log timeout update event for user {} in guild {}.", model.getTimeoutUser().getUserId(), model.getServerId(), exception);
}
});
return returningFuture;
}
@Override
public FeatureDefinition getFeature() {
return ModerationFeatureDefinition.MUTING;
}
}

View File

@@ -0,0 +1,55 @@
package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMemberTimeoutUpdatedListener;
import dev.sheldan.abstracto.core.models.listener.MemberTimeoutUpdatedModel;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.model.template.command.MuteLogModel;
import dev.sheldan.abstracto.moderation.service.MuteService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.Instant;
@Component
@Slf4j
public class MemberTimeoutLoggerListener implements AsyncMemberTimeoutUpdatedListener {
@Autowired
private MuteService muteService;
@Override
public DefaultListenerResult execute(MemberTimeoutUpdatedModel model) {
log.info("Notifying about timeout of user {} in guild {}.", model.getMutedUser().getUserId(), model.getServerId());
if(model.getMutingUser().getUserId() == model.getGuild().getSelfMember().getIdLong()) {
log.info("Skipping logging timeout event about user {} in guild {}, because it was done by us.", model.getMutedUser().getUserId(), model.getGuild().getIdLong());
return DefaultListenerResult.IGNORED;
}
MemberDisplay mutedMemberDisplay = model.getMutedMember() != null ? MemberDisplay.fromMember(model.getMutedMember()) : MemberDisplay.fromServerUser(model.getMutedUser());
MemberDisplay mutingMemberDisplay = model.getMutingMember() != null ? MemberDisplay.fromMember(model.getMutingMember()) : MemberDisplay.fromServerUser(model.getMutingUser());
Duration duration = null;
if(model.getNewTimeout() != null) {
duration = Duration.between(Instant.now(), model.getNewTimeout());
}
MuteLogModel muteLogModel = MuteLogModel
.builder()
.muteTargetDate(model.getNewTimeout() != null ? model.getNewTimeout().toInstant() : null)
.oldMuteTargetDate(model.getOldTimeout() != null ? model.getOldTimeout().toInstant() : null)
.mutingMember(mutingMemberDisplay)
.mutedMember(mutedMemberDisplay)
.duration(duration)
.reason(model.getReason())
.build();
muteService.sendMuteLogMessage(muteLogModel, model.getServerId());
return DefaultListenerResult.PROCESSED;
}
@Override
public FeatureDefinition getFeature() {
return ModerationFeatureDefinition.MUTING;
}
}

View File

@@ -9,7 +9,6 @@ import dev.sheldan.abstracto.core.interaction.modal.listener.ModalInteractionLis
import dev.sheldan.abstracto.core.interaction.modal.listener.ModalInteractionListenerResult;
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.core.utils.ParseUtils;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
@@ -38,9 +37,6 @@ public class MuteModerationActionModalListener implements ModalInteractionListen
@Autowired
private InteractionService interactionService;
@Autowired
private TemplateService templateService;
@Autowired
private InteractionExceptionService interactionExceptionService;

View File

@@ -3,82 +3,37 @@ package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncUserBannedListener;
import dev.sheldan.abstracto.core.models.listener.UserBannedModel;
import dev.sheldan.abstracto.core.service.PostTargetService;
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.core.models.listener.UserBannedListenerModel;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.config.posttarget.ModerationPostTarget;
import dev.sheldan.abstracto.moderation.model.template.listener.UserBannedListenerModel;
import dev.sheldan.abstracto.moderation.model.template.listener.UserBannedLogModel;
import dev.sheldan.abstracto.moderation.service.BanService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.audit.ActionType;
import net.dv8tion.jda.api.audit.AuditLogEntry;
import net.dv8tion.jda.api.entities.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class UserBannedListener implements AsyncUserBannedListener {
@Autowired
private TemplateService templateService;
@Autowired
private PostTargetService postTargetService;
@Autowired
private UserBannedListener self;
private static final String USER_BANNED_NOTIFICATION_TEMPLATE = "userBanned_listener_notification";
private BanService banService;
@Override
public DefaultListenerResult execute(UserBannedModel model) {
model.getGuild()
.retrieveAuditLogs()
.type(ActionType.BAN)
.limit(5)
.queue(auditLogEntries -> {
if(auditLogEntries.isEmpty()) {
log.info("Did not find recent bans in guild {}.", model.getServerId());
self.sendBannedNotification(model.getUser(), null, null, model.getServerId());
return;
}
Optional<AuditLogEntry> banEntryOptional = auditLogEntries
.stream()
.filter(auditLogEntry -> auditLogEntry.getTargetIdLong() == model.getBannedUser().getUserId())
.findFirst();
if(banEntryOptional.isPresent()) {
AuditLogEntry auditLogEntry = banEntryOptional.get();
if(!model.getGuild().getJDA().getSelfUser().equals(auditLogEntry.getUser())) {
self.sendBannedNotification(model.getUser(), auditLogEntry.getUser(), auditLogEntry.getReason(), model.getServerId());
}
} else {
log.info("Did not find the banned user in the most recent bans for guild {}. Not adding audit log information.", model.getServerId());
self.sendBannedNotification(model.getUser(), null, null, model.getServerId());
}
}, throwable -> {
log.error("Retrieving bans for guild {} failed - logging ban regardless.", model.getServerId());
self.sendBannedNotification(model.getUser(), null, null, model.getServerId());
});
return DefaultListenerResult.PROCESSED;
}
@Transactional
public CompletableFuture<Void> sendBannedNotification(User bannedUser, User banningUser, String reason, Long serverId) {
UserBannedListenerModel model = UserBannedListenerModel
public DefaultListenerResult execute(UserBannedListenerModel eventModel) {
log.info("Notifying about ban of user {} in guild {}.", eventModel.getBannedServerUser().getUserId(), eventModel.getServerId());
if(eventModel.getBanningServerUser().getUserId() == eventModel.getGuild().getJDA().getSelfUser().getIdLong()) {
log.info("Skipping logging banned event about user {} in guild {}, because it was done by us.", eventModel.getBannedServerUser().getUserId(), eventModel.getGuild().getIdLong());
return DefaultListenerResult.IGNORED;
}
UserBannedLogModel model = UserBannedLogModel
.builder()
.bannedUser(bannedUser)
.banningUser(banningUser)
.reason(reason)
.bannedUser(eventModel.getBannedUser() != null ? UserDisplay.fromUser(eventModel.getBannedUser()) : UserDisplay.fromId(eventModel.getBannedServerUser().getUserId()))
.banningUser(eventModel.getBanningUser() != null ? UserDisplay.fromUser(eventModel.getBanningUser()) : UserDisplay.fromServerUser(eventModel.getBanningServerUser()))
.reason(eventModel.getReason())
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_BANNED_NOTIFICATION_TEMPLATE, model, serverId);
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, ModerationPostTarget.BAN_LOG, serverId));
banService.sendBanLogMessage(model, eventModel.getServerId());
return DefaultListenerResult.PROCESSED;
}
@Override

View File

@@ -3,88 +3,37 @@ package dev.sheldan.abstracto.moderation.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncUserUnBannedListener;
import dev.sheldan.abstracto.core.models.listener.UserUnBannedModel;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.service.PostTargetService;
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.core.models.listener.UserUnBannedListenerModel;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.config.posttarget.ModerationPostTarget;
import dev.sheldan.abstracto.moderation.model.template.listener.UserUnBannedListenerModel;
import dev.sheldan.abstracto.moderation.model.template.listener.UserUnBannedLogModel;
import dev.sheldan.abstracto.moderation.service.BanService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.audit.ActionType;
import net.dv8tion.jda.api.audit.AuditLogEntry;
import net.dv8tion.jda.api.entities.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class UserUnBannedListener implements AsyncUserUnBannedListener {
@Autowired
private FeatureModeService featureModeService;
@Autowired
private TemplateService templateService;
@Autowired
private PostTargetService postTargetService;
@Autowired
private UserUnBannedListener self;
private static final String USER_UN_BANNED_NOTIFICATION_TEMPLATE = "userUnBanned_listener_notification";
private BanService banService;
@Override
public DefaultListenerResult execute(UserUnBannedModel model) {
log.info("Notifying about unban of user {} in guild {}.", model.getUnbannedUser().getUserId(), model.getServerId());
model.getGuild()
.retrieveAuditLogs()
.type(ActionType.UNBAN)
.limit(5)
.queue(auditLogEntries -> {
try {
if(auditLogEntries.isEmpty()) {
log.info("Did not find recent bans in guild {}.", model.getServerId());
return;
}
Optional<AuditLogEntry> banEntryOptional = auditLogEntries
.stream()
.filter(auditLogEntry -> auditLogEntry.getTargetIdLong() == model.getUnbannedUser().getUserId())
.findFirst();
if(banEntryOptional.isPresent()) {
AuditLogEntry auditLogEntry = banEntryOptional.get();
if(!model.getGuild().getJDA().getSelfUser().equals(auditLogEntry.getUser())) {
self.sendUnBannedNotification(model.getUser(), auditLogEntry.getUser(), model.getServerId());
}
} else {
log.info("Did not find the un-banned user in the most recent un-bans for guild {}. Not adding audit log information.", model.getServerId());
self.sendUnBannedNotification(model.getUser(), null, model.getServerId());
}
} catch (Exception exception) {
log.error("Failed to properly send un ban log with error.", exception);
}
}, throwable -> {
log.error("Failed to retrieve audit log entries for unban log.", throwable);
self.sendUnBannedNotification(model.getUser(), null, model.getServerId());
});
return DefaultListenerResult.PROCESSED;
}
@Transactional
public CompletableFuture<Void> sendUnBannedNotification(User unbannedUser, User unbanningUser, Long serverId) {
UserUnBannedListenerModel model = UserUnBannedListenerModel
public DefaultListenerResult execute(UserUnBannedListenerModel eventModel) {
log.info("Notifying about unban of user {} in guild {}.", eventModel.getUnBannedServerUser().getUserId(), eventModel.getServerId());
if(eventModel.getUnBanningUser().getIdLong() == eventModel.getGuild().getSelfMember().getIdLong()) {
log.info("Skipping logging banned event about user {} in guild {}, because it was done by us.", eventModel.getUnBannedServerUser().getUserId(), eventModel.getGuild().getIdLong());
return DefaultListenerResult.IGNORED;
}
UserUnBannedLogModel model = UserUnBannedLogModel
.builder()
.unBannedUser(unbannedUser)
.unBanningUser(unbanningUser)
.unBannedUser(eventModel.getUnBannedUser() != null ? UserDisplay.fromUser(eventModel.getUnBannedUser()) : UserDisplay.fromId(eventModel.getUnBannedServerUser().getUserId()))
.unBanningUser(eventModel.getUnBanningUser() != null ? UserDisplay.fromUser(eventModel.getUnBanningUser()) : UserDisplay.fromServerUser(eventModel.getUnBanningServerUser()))
.reason(eventModel.getReason())
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_UN_BANNED_NOTIFICATION_TEMPLATE, model, serverId);
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, ModerationPostTarget.BAN_LOG, serverId));
banService.sendUnBanLogMessage(model, eventModel.getServerId());
return DefaultListenerResult.PROCESSED;
}
@Override

View File

@@ -3,34 +3,31 @@ package dev.sheldan.abstracto.moderation.listener.infraction;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.service.UserService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.listener.InfractionUpdatedDescriptionListener;
import dev.sheldan.abstracto.moderation.model.database.Infraction;
import dev.sheldan.abstracto.moderation.model.database.InfractionParameter;
import dev.sheldan.abstracto.moderation.model.listener.InfractionDescriptionEventModel;
import dev.sheldan.abstracto.moderation.model.template.command.BanLog;
import dev.sheldan.abstracto.moderation.model.template.listener.UserBannedLogModel;
import dev.sheldan.abstracto.moderation.service.BanService;
import dev.sheldan.abstracto.moderation.service.BanServiceBean;
import dev.sheldan.abstracto.moderation.service.management.InfractionManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class BanReasonUpdatedListener implements InfractionUpdatedDescriptionListener {
@@ -45,7 +42,7 @@ public class BanReasonUpdatedListener implements InfractionUpdatedDescriptionLis
private UserService userService;
@Autowired
private BanServiceBean banServiceBean;
private TemplateService templateService;
@Autowired
private BanReasonUpdatedListener self;
@@ -60,7 +57,7 @@ public class BanReasonUpdatedListener implements InfractionUpdatedDescriptionLis
public CompletableFuture<DefaultListenerResult> execute(InfractionDescriptionEventModel model) {
Infraction infraction = infractionManagementService.loadInfraction(model.getInfractionId());
CompletableFuture<User> infractionUser = userService.retrieveUserForId(infraction.getUser().getUserReference().getId());
CompletableFuture<Member> creatorUser = memberService.retrieveMemberInServer(ServerUser.fromAUserInAServer(infraction.getInfractionCreator()));
CompletableFuture<User> creatorUser = userService.retrieveUserForId(infraction.getInfractionCreator().getUserReference().getId());
CompletableFuture<DefaultListenerResult> returningFuture = new CompletableFuture<>();
Long infractionId = infraction.getId();
CompletableFuture.allOf(infractionUser, creatorUser)
@@ -74,26 +71,17 @@ public class BanReasonUpdatedListener implements InfractionUpdatedDescriptionLis
}
@Transactional
public void handleBanUpdate(InfractionDescriptionEventModel model, CompletableFuture<User> infractionUser, CompletableFuture<Member> infractionCreator, CompletableFuture<DefaultListenerResult> returningFuture) {
public void handleBanUpdate(InfractionDescriptionEventModel model, CompletableFuture<User> infractionUser, CompletableFuture<User> infractionCreator, CompletableFuture<DefaultListenerResult> returningFuture) {
Infraction infraction = infractionManagementService.loadInfraction(model.getInfractionId());
GuildMessageChannel messageChannel = channelService.getMessageChannelFromServer(model.getServerId(), infraction.getLogChannel().getId());
Duration deletionDuration = infraction
.getParameters()
.stream()
.filter(infractionParameter -> infractionParameter.getInfractionParameterId().getName().equals(BanService.INFRACTION_PARAMETER_DELETION_DURATION_KEY))
.findAny()
.map(InfractionParameter::getValue)
.map(Duration::parse)
.orElse(Duration.ZERO);
BanLog banLog = BanLog
UserBannedLogModel banLog = UserBannedLogModel
.builder()
.bannedUser(infractionUser.isCompletedExceptionally() ? null : UserDisplay.fromUser(infractionUser.join()))
.banningMember(infractionCreator.isCompletedExceptionally() ? null : MemberDisplay.fromMember(infractionCreator.join()))
.deletionDuration(deletionDuration)
.banningUser(infractionCreator.isCompletedExceptionally() ? null : UserDisplay.fromUser(infractionCreator.join()))
.reason(model.getNewDescription())
.build();
MessageToSend message = banServiceBean.renderBanMessage(banLog, model.getServerId());
MessageToSend message = templateService.renderEmbedTemplate(BanServiceBean.USER_BANNED_NOTIFICATION_TEMPLATE, banLog, model.getServerId());
messageService.editMessageInChannel(messageChannel, message, infraction.getLogMessageId())
.thenAccept(unused1 -> returningFuture.complete(DefaultListenerResult.PROCESSED))
.exceptionally(throwable1 -> {

View File

@@ -2,48 +2,47 @@ package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.CompletableFutureMap;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureConfig;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
import dev.sheldan.abstracto.moderation.config.posttarget.ModerationPostTarget;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.moderation.model.BanResult;
import dev.sheldan.abstracto.moderation.model.database.Infraction;
import dev.sheldan.abstracto.moderation.model.template.command.BanLog;
import dev.sheldan.abstracto.moderation.model.template.command.BanNotificationModel;
import dev.sheldan.abstracto.moderation.model.template.command.UnBanLog;
import dev.sheldan.abstracto.moderation.model.template.listener.UserBannedLogModel;
import dev.sheldan.abstracto.moderation.model.template.listener.UserUnBannedLogModel;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.UserSnowflake;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
@Component
@Slf4j
public class BanServiceBean implements BanService {
public static final String BAN_LOG_TEMPLATE = "ban_log";
public static final String UN_BAN_LOG_TEMPLATE = "unBan_log";
public static final String BAN_NOTIFICATION = "ban_notification";
@Autowired
private TemplateService templateService;
@Autowired
private PostTargetService postTargetService;
@Autowired
private BanServiceBean self;
@@ -62,15 +61,17 @@ public class BanServiceBean implements BanService {
@Autowired
private InfractionService infractionService;
@Autowired
private PostTargetService postTargetService;
@Autowired
private UserService userService;
public static final String USER_BANNED_NOTIFICATION_TEMPLATE = "userBanned_listener_notification";
private static final String USER_UN_BANNED_NOTIFICATION_TEMPLATE = "userUnBanned_listener_notification";
@Override
public CompletableFuture<BanResult> banUserWithNotification(ServerUser userToBeBanned, String reason, ServerUser banningUser, Guild guild, Duration deletionDuration) {
BanLog banLog = BanLog
.builder()
.bannedUser(UserDisplay.fromServerUser(userToBeBanned))
.banningMember(MemberDisplay.fromServerUser(banningUser))
.deletionDuration(deletionDuration)
.reason(reason)
.build();
BanResult[] result = {BanResult.SUCCESSFUL};
return sendBanNotification(userToBeBanned, reason, guild)
.exceptionally(throwable -> {
@@ -78,13 +79,13 @@ public class BanServiceBean implements BanService {
return null;
})
.thenCompose(unused -> banUser(guild, userToBeBanned, deletionDuration, reason))
.thenCompose(unused -> sendBanLogMessage(banLog, guild.getIdLong()))
.thenAccept(banLogMessage -> self.evaluateAndStoreInfraction(userToBeBanned, guild, reason, banningUser, banLogMessage, deletionDuration))
.thenCompose(unused -> self.composeAndSendBanLogMessage(userToBeBanned, banningUser, reason))
.thenAccept(banLogMessage -> self.evaluateAndStoreInfraction(userToBeBanned, guild, reason, banningUser, deletionDuration))
.thenApply(unused -> result[0]);
}
@Transactional
public CompletableFuture<Long> evaluateAndStoreInfraction(ServerUser user, Guild guild, String reason, ServerUser banningMember, Message banLogMessage, Duration deletionDuration) {
public CompletableFuture<Long> evaluateAndStoreInfraction(ServerUser user, Guild guild, String reason, ServerUser banningMember, Duration deletionDuration) {
if(featureFlagService.getFeatureFlagValue(ModerationFeatureDefinition.INFRACTIONS, guild.getIdLong())) {
Long infractionPoints = configService.getLongValueOrConfigDefault(ModerationFeatureConfig.BAN_INFRACTION_POINTS, guild.getIdLong());
AUserInAServer bannedUser = userInServerManagementService.loadOrCreateUser(guild.getIdLong(), user.getUserId());
@@ -94,7 +95,7 @@ public class BanServiceBean implements BanService {
deletionDuration = Duration.ZERO;
}
parameters.put(INFRACTION_PARAMETER_DELETION_DURATION_KEY, deletionDuration.toString());
return infractionService.createInfractionWithNotification(bannedUser, infractionPoints, BAN_INFRACTION_TYPE, reason, banningUser, parameters, banLogMessage)
return infractionService.createInfractionWithNotification(bannedUser, infractionPoints, BAN_INFRACTION_TYPE, reason, banningUser, parameters)
.thenApply(Infraction::getId);
} else {
return CompletableFuture.completedFuture(null);
@@ -111,17 +112,6 @@ public class BanServiceBean implements BanService {
return messageService.sendMessageToUser(serverUser, message).thenAccept(message1 -> {});
}
@Override
public CompletableFuture<Void> unBanUserWithNotification(Long userId, ServerUser unBanningMember, Guild guild) {
UnBanLog banLog = UnBanLog
.builder()
.bannedUser(UserDisplay.fromId(userId))
.unBanningMember(MemberDisplay.fromServerUser(unBanningMember))
.build();
return unbanUser(guild, userId)
.thenCompose(unused -> self.sendUnBanLogMessage(banLog, guild.getIdLong(), UN_BAN_LOG_TEMPLATE));
}
@Override
public CompletableFuture<Void> banUser(Guild guild, ServerUser userToBeBanned, Duration deletionDuration, String reason) {
log.info("Banning user {} in guild {}.", userToBeBanned.getUserId(), guild.getId());
@@ -132,31 +122,60 @@ public class BanServiceBean implements BanService {
}
@Override
public CompletableFuture<Void> unbanUser(Guild guild, Long userId) {
log.info("Unbanning user {} in guild {}.", userId, guild.getId());
return guild.unban(UserSnowflake.fromId(userId)).submit();
public CompletableFuture<Void> unbanUser(Guild guild, User user, Member memberPerforming) {
log.info("Unbanning user {} in guild {}.", user.getIdLong(), guild.getId());
return guild.unban(user).submit().thenCompose(unused -> self.composeAndSendUnBanLogMessage(guild, user, memberPerforming));
}
@Transactional
public CompletionStage<Void> composeAndSendUnBanLogMessage(Guild guild, User user, Member memberPerforming) {
UserUnBannedLogModel model = UserUnBannedLogModel
.builder()
.unBannedUser(UserDisplay.fromUser(user))
.unBanningUser(UserDisplay.fromUser(memberPerforming.getUser()))
.reason(null)
.build();
return sendUnBanLogMessage(model, guild.getIdLong());
}
@Transactional
public CompletableFuture<Void> composeAndSendBanLogMessage(ServerUser serverUserToBeBanned, ServerUser serverUserBanning, String reason) {
CompletableFutureMap<Long, User> userMap = userService.retrieveUsersMapped(Arrays.asList(serverUserToBeBanned.getUserId(), serverUserBanning.getUserId()));
return userMap.getMainFuture().thenCompose(unused -> {
User userToBeBanned = userMap.getElement(serverUserToBeBanned.getUserId());
User banningUser = userMap.getElement(serverUserBanning.getUserId());
UserBannedLogModel model = UserBannedLogModel
.builder()
.bannedUser(UserDisplay.fromUser(userToBeBanned))
.banningUser(UserDisplay.fromUser(banningUser))
.reason(reason)
.build();
return self.sendBanLogMessage(model, serverUserToBeBanned.getServerId());
}).exceptionally(throwable -> {
log.warn("Failed to load users ({}, {}) for ban log message.", serverUserToBeBanned.getUserId(), serverUserBanning.getUserId(), throwable);
UserBannedLogModel model = UserBannedLogModel
.builder()
.bannedUser(UserDisplay.fromId(serverUserToBeBanned.getUserId()))
.banningUser(UserDisplay.fromId(serverUserBanning.getUserId()))
.reason(reason)
.build();
self.sendBanLogMessage(model, serverUserToBeBanned.getServerId());
return null;
});
}
@Override
public CompletableFuture<Void> softBanUser(Guild guild, ServerUser user, Duration delDays) {
return banUser(guild, user, delDays, "")
.thenCompose(unused -> unbanUser(guild, user.getUserId()));
@Transactional
public CompletableFuture<Void> sendUnBanLogMessage(UserUnBannedLogModel model, Long serverId) {
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_UN_BANNED_NOTIFICATION_TEMPLATE, model, serverId);
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, ModerationPostTarget.BAN_LOG, serverId));
}
public CompletableFuture<Message> sendBanLogMessage(BanLog banLog, Long guildId) {
MessageToSend banLogMessage = renderBanMessage(banLog, guildId);
log.debug("Sending ban log message in guild {}.", guildId);
List<CompletableFuture<Message>> messageFutures = postTargetService.sendEmbedInPostTarget(banLogMessage, ModerationPostTarget.BAN_LOG, guildId);
return FutureUtils.toSingleFutureGeneric(messageFutures).thenApply(unused -> messageFutures.get(0).join());
@Override
@Transactional
public CompletableFuture<Void> sendBanLogMessage(UserBannedLogModel model, Long serverId) {
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_BANNED_NOTIFICATION_TEMPLATE, model, serverId);
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, ModerationPostTarget.BAN_LOG, serverId));
}
public MessageToSend renderBanMessage(BanLog banLog, Long guildId) {
return templateService.renderEmbedTemplate(BAN_LOG_TEMPLATE, banLog, guildId);
}
public CompletableFuture<Void> sendUnBanLogMessage(UnBanLog banLog, Long guildId, String template) {
MessageToSend banLogMessage = templateService.renderEmbedTemplate(template, banLog, guildId);
log.debug("Sending unban log message in guild {}.", guildId);
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(banLogMessage, ModerationPostTarget.UN_BAN_LOG, guildId));
}
}

View File

@@ -81,7 +81,7 @@ public class InfractionServiceBean implements InfractionService {
List<Infraction> infractions = infractionManagementService.getActiveInfractionsForUser(aUserInAServer);
log.info("Calculating points for user {} in server {} with {} infractions.",
aUserInAServer.getUserReference().getId(), aUserInAServer.getServerReference().getId(), infractions.size());
return infractions.stream().collect(Collectors.summarizingLong(Infraction::getPoints)).getCount();
return infractions.stream().collect(Collectors.summarizingLong(Infraction::getPoints)).getSum();
}
@Override
@@ -112,8 +112,8 @@ public class InfractionServiceBean implements InfractionService {
public CompletableFuture<Void> createInfractionNotification(AUserInAServer aUserInAServer, Long points, String type, String description) {
Long serverId = aUserInAServer.getServerReference().getId();
Long currentPoints = getActiveInfractionPointsForUser(aUserInAServer);
Long newPoints = currentPoints + points;
Pair<Integer, Integer> levelChange = infractionLevelChanged(serverId, newPoints, currentPoints);
Long oldPoints = currentPoints - points;
Pair<Integer, Integer> levelChange = infractionLevelChanged(serverId, currentPoints, oldPoints);
Integer oldLevel = levelChange.getFirst();
Integer newLevel = levelChange.getSecond();
if(!oldLevel.equals(newLevel)) {
@@ -124,10 +124,10 @@ public class InfractionServiceBean implements InfractionService {
.oldLevel(oldLevel)
.type(type)
.description(description)
.oldPoints(currentPoints)
.newPoints(newPoints)
.oldPoints(oldPoints)
.newPoints(currentPoints)
.build();
infractionLevelChangedListenerManager.sendInfractionLevelChangedEvent(newLevel, oldLevel, newPoints, currentPoints, ServerUser.fromAUserInAServer(aUserInAServer));
infractionLevelChangedListenerManager.sendInfractionLevelChangedEvent(newLevel, oldLevel, currentPoints, oldPoints, ServerUser.fromAUserInAServer(aUserInAServer));
MessageToSend messageToSend = templateService.renderEmbedTemplate(INFRACTION_NOTIFICATION_TEMPLATE_KEY, model, serverId);
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, InfractionPostTarget.INFRACTION_NOTIFICATION, serverId));
} else {

View File

@@ -2,11 +2,13 @@ package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.service.UserService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.CompletableFutureMap;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureConfig;
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
@@ -16,14 +18,12 @@ import dev.sheldan.abstracto.moderation.model.template.command.KickLogModel;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.UserSnowflake;
import net.dv8tion.jda.api.entities.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -51,6 +51,9 @@ public class KickServiceBean implements KickService {
@Autowired
private InfractionService infractionService;
@Autowired
private UserService userService;
@Autowired
private KickServiceBean self;
@@ -59,7 +62,7 @@ public class KickServiceBean implements KickService {
Guild guild = kickedMember.getGuild();
log.info("Kicking user {} from guild {}", kickedMember.getUser().getIdLong(), guild.getIdLong());
CompletableFuture<Void> kickFuture = guild.kick(kickedMember, reason).submit();
CompletableFuture<Message> logFuture = sendKickLog(kickedMember, kickingMember, reason, guild.getIdLong());
CompletableFuture<Message> logFuture = sendKickLog(kickedMember.getUser(), ServerUser.fromMember(kickedMember), kickingMember.getUser(), ServerUser.fromMember(kickingMember), reason, guild.getIdLong());
return CompletableFuture.allOf(kickFuture, logFuture)
.thenAccept(unused -> self.storeInfraction(kickedMember, kickingMember, reason, logFuture.join(), guild.getIdLong()));
}
@@ -96,27 +99,30 @@ public class KickServiceBean implements KickService {
}
}
private CompletableFuture<Message> sendKickLog(Member kickedMember, Member kickingMember, String reason, Long serverId) {
public CompletableFuture<Message> sendKickLog(User kickedUser, ServerUser kickedServerUser, User kickingUser, ServerUser kickingServerUser, String reason, Long serverId) {
KickLogModel kickLogModel = KickLogModel
.builder()
.kickedMember(MemberDisplay.fromMember(kickedMember))
.kickingMember(MemberDisplay.fromMember(kickingMember))
.kickedUser(kickedUser != null ? UserDisplay.fromUser(kickedUser) : UserDisplay.fromServerUser(kickedServerUser))
.kickingUser(kickingUser != null ? UserDisplay.fromUser(kickingUser) : UserDisplay.fromServerUser(kickingServerUser))
.reason(reason)
.build();
return sendKicklog(serverId, kickLogModel);
return sendKicklog(kickLogModel, serverId);
}
private CompletableFuture<Message> sendKickLog(ServerUser kickedMember, ServerUser kickingMember, String reason, Long serverId) {
KickLogModel kickLogModel = KickLogModel
.builder()
.kickedMember(MemberDisplay.fromServerUser(kickedMember))
.kickingMember(MemberDisplay.fromServerUser(kickingMember))
.reason(reason)
.build();
return sendKicklog(serverId, kickLogModel);
public CompletableFuture<Message> sendKickLog(ServerUser kickedMember, ServerUser kickingMember, String reason, Long serverId) {
CompletableFutureMap<Long, User> userMap = userService.retrieveUsersMapped(Arrays.asList(kickedMember.getUserId(), kickingMember.getUserId()));
return userMap.getMainFuture().thenCompose(unused -> {
User kickedUser = userMap.getElement(kickedMember.getUserId());
User kickingUser = userMap.getElement(kickingMember.getUserId());
return self.sendKickLog(kickedUser, kickedMember, kickingUser, kickingMember, reason, serverId);
}).exceptionally(throwable -> {
log.warn("Failed to fetch users ({}, {}) for kick event logging in server {}.", kickingMember.getUserId(), kickedMember.getUserId(), serverId, throwable);
self.sendKickLog(null, kickedMember, null, kickingMember, reason, serverId);
return null;
});
}
private CompletableFuture<Message> sendKicklog(Long serverId, KickLogModel kickLogModel) {
public CompletableFuture<Message> sendKicklog(KickLogModel kickLogModel, Long serverId) {
MessageToSend warnLogMessage = templateService.renderEmbedTemplate(KICK_LOG_TEMPLATE, kickLogModel, serverId);
log.debug("Sending kick log message in guild {}.", serverId);
List<CompletableFuture<Message>> messageFutures = postTargetService.sendEmbedInPostTarget(warnLogMessage, ModerationPostTarget.KICK_LOG, serverId);

View File

@@ -21,15 +21,14 @@ import dev.sheldan.abstracto.moderation.exception.NoMuteFoundException;
import dev.sheldan.abstracto.moderation.model.MuteResult;
import dev.sheldan.abstracto.moderation.model.database.Infraction;
import dev.sheldan.abstracto.moderation.model.database.Mute;
import dev.sheldan.abstracto.moderation.model.template.command.MuteListenerModel;
import dev.sheldan.abstracto.moderation.model.template.command.MuteLogModel;
import dev.sheldan.abstracto.moderation.model.template.command.MuteNotification;
import dev.sheldan.abstracto.moderation.model.template.command.UnMuteLog;
import dev.sheldan.abstracto.moderation.service.management.MuteManagementService;
import dev.sheldan.abstracto.scheduling.model.JobParameters;
import dev.sheldan.abstracto.scheduling.service.SchedulerService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@@ -168,21 +167,60 @@ public class MuteServiceBean implements MuteService {
@Override
public CompletableFuture<MuteResult> muteMemberWithLog(ServerUser userToMute, ServerUser mutingUser, String reason, Duration duration, Guild guild, ServerChannelMessage origin) {
return muteMemberWithLog(userToMute, mutingUser, reason, duration, guild, origin, null);
}
@Override
public CompletableFuture<MuteResult> muteMemberWithLog(ServerUser userToMute, ServerUser mutingUser, String reason, Duration duration, Guild guild, ServerChannelMessage origin, Instant oldTimeout) {
Long serverId = userToMute.getServerId();
Instant targetDate = Instant.now().plus(duration);
log.debug("Muting member {} in server {}.", userToMute.getUserId(), serverId);
log.info("Muting member {} in server {}.", userToMute.getUserId(), serverId);
AServer server = serverManagementService.loadOrCreate(serverId);
Long muteId = counterService.getNextCounterValue(server, MUTE_COUNTER_KEY);
CompletableFuture<MuteResult> result = muteUserInServer(guild, userToMute, reason, duration);
return result
.thenCompose(unused -> self.sendMuteLog(userToMute, mutingUser, duration, reason))
.thenCompose(logMessage -> self.evaluateAndStoreInfraction(userToMute, mutingUser, reason, targetDate, logMessage))
.thenCompose(muteResult -> self.composeAndLogMute(userToMute, mutingUser, reason, duration, guild, oldTimeout))
.thenCompose(logMessage -> self.evaluateAndStoreInfraction(userToMute, mutingUser, reason, targetDate))
.thenAccept(infractionId -> self.persistMute(userToMute, mutingUser, targetDate, muteId, reason, infractionId, origin))
.thenApply(unused -> result.join());
}
@Transactional
public CompletableFuture<Long> evaluateAndStoreInfraction(ServerUser userToMute, ServerUser mutingUser, String reason, Instant targetDate, Message logMessage) {
public CompletableFuture<Void> composeAndLogMute(ServerUser userToMute, ServerUser mutingUser, String reason, Duration duration, Guild guild, Instant oldTimeout) {
CompletableFuture<Member> mutedMemberFuture = memberService.retrieveMemberInServer(userToMute);
CompletableFuture<Member> mutingMemberFuture = memberService.retrieveMemberInServer(mutingUser);
Instant targetDate = Instant.now().plus(duration);
return CompletableFuture.allOf(mutedMemberFuture, mutingMemberFuture).thenCompose(unused -> {
Member mutedMember = mutedMemberFuture.join();
Member mutingMember = mutingMemberFuture.join();
MuteLogModel muteLogModel = MuteLogModel
.builder()
.muteTargetDate(targetDate)
.oldMuteTargetDate(oldTimeout)
.mutingMember(MemberDisplay.fromMember(mutingMember))
.mutedMember(MemberDisplay.fromMember(mutedMember))
.duration(duration)
.reason(reason)
.build();
return self.sendMuteLogMessage(muteLogModel, guild.getIdLong());
}).exceptionally(throwable -> {
log.warn("Failed to load users for mute log ({}, {}) in guild {}.", userToMute.getUserId(), mutingUser.getUserId(), guild.getIdLong(), throwable);
MuteLogModel muteLogModel = MuteLogModel
.builder()
.muteTargetDate(targetDate)
.oldMuteTargetDate(null)
.mutingMember(MemberDisplay.fromServerUser(mutingUser))
.mutedMember(MemberDisplay.fromServerUser(userToMute))
.duration(duration)
.reason(reason)
.build();
self.sendMuteLogMessage(muteLogModel, guild.getIdLong());
return null;
});
}
@Transactional
public CompletableFuture<Long> evaluateAndStoreInfraction(ServerUser userToMute, ServerUser mutingUser, String reason, Instant targetDate) {
Long serverId = userToMute.getServerId();
if(featureFlagService.getFeatureFlagValue(ModerationFeatureDefinition.INFRACTIONS, serverId)) {
Long infractionPoints = configService.getLongValueOrConfigDefault(MutingFeatureConfig.MUTE_INFRACTION_POINTS, serverId);
@@ -190,7 +228,7 @@ public class MuteServiceBean implements MuteService {
AUserInAServer mutingUserInAServer = userInServerManagementService.loadOrCreateUser(mutingUser);
Map<String, String> parameters = new HashMap<>();
parameters.put(INFRACTION_PARAMETER_DURATION_KEY, templateService.renderDuration(Duration.between(Instant.now(), targetDate), serverId));
return infractionService.createInfractionWithNotification(mutedUserInAServer, infractionPoints, MUTE_INFRACTION_TYPE, reason, mutingUserInAServer, parameters, logMessage)
return infractionService.createInfractionWithNotification(mutedUserInAServer, infractionPoints, MUTE_INFRACTION_TYPE, reason, mutingUserInAServer, parameters)
.thenApply(Infraction::getId);
} else {
return CompletableFuture.completedFuture(null);
@@ -204,48 +242,13 @@ public class MuteServiceBean implements MuteService {
createMuteObject(userToMute, mutingUser, reason, targetDate, muteId, triggerKey, infractionId, origin);
}
@Transactional
public CompletableFuture<Message> sendMuteLog(ServerUser userBeingMuted, ServerUser mutingUser, Duration duration, String reason) {
Instant targetDate = Instant.now().plus(duration);
MuteListenerModel model = MuteListenerModel
.builder()
.mutedUser(MemberDisplay.fromServerUser(userBeingMuted))
.mutingUser(MemberDisplay.fromServerUser(mutingUser))
.oldMuteTargetDate(null)
.duration(duration)
.muteTargetDate(targetDate)
.reason(reason)
.build();
log.debug("Sending mute log to the mute post target.");
Long serverId = userBeingMuted.getServerId();
MessageToSend message = templateService.renderEmbedTemplate(MUTE_LOG_TEMPLATE, model, serverId);
List<CompletableFuture<Message>> futures = postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, serverId);
return FutureUtils.toSingleFutureGeneric(futures).thenApply(unused -> futures.get(0).join());
}
private CompletableFuture<Void> sendUnMuteLogMessage(UnMuteLog muteLogModel, AServer server) {
MuteListenerModel model = MuteListenerModel
.builder()
.mutedUser(muteLogModel.getUnMutedUser())
.mutingUser(muteLogModel.getMutingUser())
.oldMuteTargetDate(muteLogModel.getMute() != null ? muteLogModel.getMute().getMuteTargetDate() : null)
.muteTargetDate(null)
.build();
if(muteLogModel.getMute() != null) {
log.debug("Sending unMute log for mute {} to the mute posttarget in server {}", muteLogModel.getMute().getMuteId().getId(), server.getId());
}
MessageToSend message = templateService.renderEmbedTemplate(MUTE_LOG_TEMPLATE, model, server.getId());
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, server.getId()));
}
@Override
@Transactional
public CompletableFuture<Void> unMuteUser(ServerUser userToUnmute, ServerUser unMutingUser, Guild guild) {
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(userToUnmute);
boolean muteActive = muteManagementService.hasActiveMute(aUserInAServer);
if(!muteActive) {
return memberService.removeTimeout(guild, userToUnmute, null)
.thenCompose(unused -> self.sendUnmuteLog(null, guild, userToUnmute, unMutingUser));
return memberService.removeTimeout(guild, userToUnmute, null);
} else {
Mute mute = muteManagementService.getAMuteOf(aUserInAServer);
return endMute(mute, guild);
@@ -261,31 +264,34 @@ public class MuteServiceBean implements MuteService {
Long muteId = mute.getMuteId().getId();
AServer mutingServer = mute.getServer();
ServerUser mutedUser = ServerUser.fromAUserInAServer(mute.getMutedUser());
ServerUser mutingUser = ServerUser.fromAUserInAServer(mute.getMutedUser());
ServerUser mutingUser = ServerUser.fromAUserInAServer(mute.getMutingUser());
log.info("UnMuting {} in server {}", mute.getMutedUser().getUserReference().getId(), mutingServer.getId());
return memberService.removeTimeout(guild, mutedUser, null)
.thenCompose(unused -> self.sendUnmuteLog(muteId, guild, mutedUser, mutingUser));
.thenCompose(unused -> self.composeAndLogUnmute(mutedUser, mutingUser, guild))
.thenAccept(unused -> {
if(muteId != null) {
self.endMuteInDatabase(muteId, guild.getIdLong());
}
});
}
@Transactional
public CompletableFuture<Void> sendUnmuteLog(Long muteId, Guild guild, ServerUser unMutedMember, ServerUser mutingMember) {
Mute mute = null;
if(muteId != null) {
mute = muteManagementService.findMute(muteId, guild.getIdLong());
}
AServer mutingServer = serverManagementService.loadServer(guild.getIdLong());
UnMuteLog unMuteLog = UnMuteLog
.builder()
.mute(mute)
.mutingUser(MemberDisplay.fromServerUser(mutingMember))
.unMutedUser(MemberDisplay.fromServerUser(unMutedMember))
.build();
CompletableFuture<Void> notificationFuture = sendUnMuteLogMessage(unMuteLog, mutingServer);
return CompletableFuture.allOf(notificationFuture).thenAccept(aVoid -> {
if(muteId != null) {
self.endMuteInDatabase(muteId, guild.getIdLong());
}
public CompletableFuture<Void> composeAndLogUnmute(ServerUser mutedUser, ServerUser mutingUser, Guild guild) {
CompletableFuture<Member> mutedMemberFuture = memberService.retrieveMemberInServer(mutedUser);
CompletableFuture<Member> mutingMemberFuture = memberService.retrieveMemberInServer(mutingUser);
return CompletableFuture.allOf(mutedMemberFuture, mutingMemberFuture).thenCompose(unused -> {
Member mutedMember = mutedMemberFuture.join();
Member mutingMember = mutingMemberFuture.join();
MuteLogModel muteLogModel = MuteLogModel
.builder()
.muteTargetDate(null)
.oldMuteTargetDate(null)
.mutingMember(MemberDisplay.fromMember(mutingMember))
.mutedMember(MemberDisplay.fromMember(mutedMember))
.duration(null)
.reason(null)
.build();
return self.sendMuteLogMessage(muteLogModel, guild.getIdLong());
});
}
@@ -310,6 +316,12 @@ public class MuteServiceBean implements MuteService {
}
}
@Override
public CompletableFuture<Void> sendMuteLogMessage(MuteLogModel model, Long serverId) {
MessageToSend message = templateService.renderEmbedTemplate(MuteServiceBean.MUTE_LOG_TEMPLATE, model, serverId);
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, serverId));
}
@Override
public void completelyUnMuteUser(AUserInAServer aUserInAServer) {
log.info("Completely unmuting user {} in server {}.", aUserInAServer.getUserReference().getId(), aUserInAServer.getServerReference().getId());

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