Compare commits

...

72 Commits

Author SHA1 Message Date
Sheldan
fed2d24a55 [AB-xxx] fixing ui code, and increasing window size in ui
updating dependency
2024-11-26 23:34:52 +01:00
release-bot
aa359f60f3 [maven-release-plugin] prepare for next development iteration 2024-11-26 22:21:02 +00:00
release-bot
353fda7770 [maven-release-plugin] prepare release v1.5.52 2024-11-26 22:21:01 +00:00
Sheldan
b88ae377b1 [AB-xxx] adding feature to focus on a particular user in both leaderboard command and leaderboard UI
adding button to view the specified user on loaderboard at the rank command
2024-11-26 23:17:24 +01:00
release-bot
3d915b96e7 Commit from GitHub Actions (Publishes a new version of abstracto) 2024-11-22 21:47:46 +00:00
release-bot
9a1e0f330a [maven-release-plugin] prepare for next development iteration 2024-11-22 21:35:44 +00:00
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
244 changed files with 1660 additions and 1169 deletions

2
.env
View File

@@ -1,2 +1,2 @@
REGISTRY_PREFIX=harbor.sheldan.dev/abstracto/ REGISTRY_PREFIX=harbor.sheldan.dev/abstracto/
VERSION=1.5.35 VERSION=1.5.51

View File

@@ -16,7 +16,7 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@@ -37,15 +37,4 @@ jobs:
- name: Build ui application - name: Build ui application
working-directory: ./ui/experience-tracking working-directory: ./ui/experience-tracking
run: npm run build 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 contents: write
jobs: jobs:
publish: publish:
runs-on: ubuntu-latest runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
@@ -64,7 +64,7 @@ jobs:
with: with:
path: .env path: .env
- name: Build and push Docker containers - name: Build and push Docker containers
run: docker-compose build && docker-compose push run: docker compose build && docker compose push
env: env:
REGISTRY_PREFIX: ${{ steps.dotenv.outputs.registry_prefix }} REGISTRY_PREFIX: ${{ steps.dotenv.outputs.registry_prefix }}
VERSION: ${{ steps.dotenv.outputs.version }} VERSION: ${{ steps.dotenv.outputs.version }}

2
.gitignore vendored
View File

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

View File

@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2023 Sheldan Copyright (c) 2024 Sheldan
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal 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. 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. 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 ## Technologies

View File

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

View File

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

View File

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

View File

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

View File

@@ -42,7 +42,7 @@ public class ShowAssignableRolePlaceConfig extends AbstractConditionableCommand
List<Object> parameters = commandContext.getParameters().getParameters(); List<Object> parameters = commandContext.getParameters().getParameters();
String name = (String) parameters.get(0); String name = (String) parameters.get(0);
AssignableRolePlaceConfig config = service.getAssignableRolePlaceConfig(commandContext.getGuild(), name); 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()); .thenApply(unused -> CommandResult.fromSuccess());
} }

View File

@@ -39,7 +39,7 @@ public class ShowAssignableRolePlaces extends AbstractConditionableCommand {
@Override @Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) { public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
AssignablePlaceOverview model = service.getAssignableRolePlaceOverview(commandContext.getGuild()); 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()); .thenApply(unused -> CommandResult.fromSuccess());
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -53,7 +53,7 @@ public class Choose extends AbstractConditionableCommand {
.builder() .builder()
.chosenValue(choice) .chosenValue(choice)
.build(); .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()); .thenApply(unused -> CommandResult.fromIgnored());
} }

View File

@@ -66,7 +66,7 @@ public class Mock extends AbstractConditionableCommand {
.originalText(messageText) .originalText(messageText)
.mockingText(mockingText) .mockingText(mockingText)
.build(); .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()); .thenApply(unused -> CommandResult.fromSuccess());
} }

View File

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

View File

@@ -69,7 +69,7 @@ public class Roll extends AbstractConditionableCommand {
.builder() .builder()
.rolled(rolled) .rolled(rolled)
.build(); .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()); .thenApply(unused -> CommandResult.fromIgnored());
} }

View File

@@ -45,7 +45,7 @@ public class Roulette extends AbstractConditionableCommand {
.builder() .builder()
.result(rouletteResult) .result(rouletteResult)
.build(); .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()); .thenApply(unused -> CommandResult.fromIgnored());
} }

View File

@@ -65,7 +65,7 @@ public class TransferCredits extends AbstractConditionableCommand {
.targetMember(MemberDisplay.fromMember(targetMember)) .targetMember(MemberDisplay.fromMember(targetMember))
.credits(amount) .credits(amount)
.build(); .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()); .thenApply(unused -> CommandResult.fromSuccess());
} }

View File

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

View File

@@ -54,7 +54,7 @@ public class MinesButtonClickedListener implements ButtonClickedListener {
} }
gameService.uncoverBoard(mineBoard); 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 -> { interactionService.editOriginal(messageToSend, model.getEvent().getHook()).thenAccept(message -> {
gameService.updateMineBoard(mineBoard); gameService.updateMineBoard(mineBoard);
log.info("Updated original mineboard for board {}.", mineBoard.getBoardId()); log.info("Updated original mineboard for board {}.", mineBoard.getBoardId());

View File

@@ -105,7 +105,9 @@ public class EntertainmentServiceBean implements EntertainmentService {
@Override @Override
public Integer getLoveCalcValue(String firstPart, String secondPart) { 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 @Override
@@ -191,8 +193,8 @@ public class EntertainmentServiceBean implements EntertainmentService {
.text(pressF.getText()) .text(pressF.getText())
.messageId(pressF.getMessageId()) .messageId(pressF.getMessageId())
.build(); .build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(PRESS_F_RESULT_TEMPLATE_KEY, model);
Long serverId = pressF.getServer().getId(); Long serverId = pressF.getServer().getId();
MessageToSend messageToSend = templateService.renderEmbedTemplate(PRESS_F_RESULT_TEMPLATE_KEY, model, serverId);
Long channelId = pressF.getPressFChannel().getId(); Long channelId = pressF.getPressFChannel().getId();
Long messageId = pressF.getMessageId(); Long messageId = pressF.getMessageId();
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageEmbedToSendToAChannel(messageToSend, pressF.getPressFChannel())) return FutureUtils.toSingleFutureGeneric(channelService.sendMessageEmbedToSendToAChannel(messageToSend, pressF.getPressFChannel()))

View File

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

View File

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

View File

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

View File

@@ -1,14 +1,26 @@
package dev.sheldan.abstracto.experience.api; package dev.sheldan.abstracto.experience.api;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.frontend.RoleDisplay; import dev.sheldan.abstracto.core.models.frontend.RoleDisplay;
import dev.sheldan.abstracto.core.models.frontend.UserDisplay; import dev.sheldan.abstracto.core.models.frontend.UserDisplay;
import dev.sheldan.abstracto.core.service.GuildService; import dev.sheldan.abstracto.core.service.GuildService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService; import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.experience.model.api.UserExperienceDisplay; import dev.sheldan.abstracto.experience.model.api.UserExperienceDisplay;
import dev.sheldan.abstracto.experience.model.database.AExperienceLevel;
import dev.sheldan.abstracto.experience.model.database.AExperienceRole; import dev.sheldan.abstracto.experience.model.database.AExperienceRole;
import dev.sheldan.abstracto.experience.model.database.AUserExperience; import dev.sheldan.abstracto.experience.model.database.AUserExperience;
import dev.sheldan.abstracto.experience.model.database.LeaderBoardEntryResult;
import dev.sheldan.abstracto.experience.service.ExperienceLevelService;
import dev.sheldan.abstracto.experience.service.management.ExperienceLevelManagementService;
import dev.sheldan.abstracto.experience.service.management.ExperienceRoleManagementService;
import dev.sheldan.abstracto.experience.service.management.UserExperienceManagementService; import dev.sheldan.abstracto.experience.service.management.UserExperienceManagementService;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.Role;
@@ -22,6 +34,7 @@ import org.springframework.data.web.SortDefault;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
@@ -37,9 +50,21 @@ public class LeaderboardController {
@Autowired @Autowired
private GuildService guildService; private GuildService guildService;
@Autowired
private ExperienceLevelService experienceLevelService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private ExperienceRoleManagementService experienceRoleManagementService;
@Autowired
private ExperienceLevelManagementService experienceLevelManagementService;
@GetMapping(value = "/leaderboards/{serverId}", produces = "application/json") @GetMapping(value = "/leaderboards/{serverId}", produces = "application/json")
public Page<UserExperienceDisplay> getLeaderboard(@PathVariable("serverId") Long serverId, public Page<UserExperienceDisplay> getLeaderboard(@PathVariable("serverId") Long serverId,
@PageableDefault(value = 25, page = 0) @PageableDefault(value = 50, page = 0)
@SortDefault(sort = "experience", direction = Sort.Direction.DESC) @SortDefault(sort = "experience", direction = Sort.Direction.DESC)
Pageable pageable) { Pageable pageable) {
AServer server = serverManagementService.loadServer(serverId); AServer server = serverManagementService.loadServer(serverId);
@@ -49,12 +74,31 @@ public class LeaderboardController {
.map(userExperience -> convertFromUser(guild, userExperience, pageable, allElements)); .map(userExperience -> convertFromUser(guild, userExperience, pageable, allElements));
} }
@GetMapping(value = "/leaderboards/{serverId}/{userId}", produces = "application/json")
public List<UserExperienceDisplay> getLeaderboardForUser(@PathVariable("serverId") Long serverId, @PathVariable("userId") Long userId,
@RequestParam("windowSize") Integer windowSize) {
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(ServerUser.fromId(serverId, userId));
Map<Long, AExperienceRole> experienceRolesForServer = experienceRoleManagementService.getExperienceRolesForServer(aUserInAServer.getServerReference())
.stream()
.collect(Collectors.toMap(AExperienceRole::getId, Function.identity()));
Map<Integer, AExperienceLevel> levels =
experienceLevelManagementService.getLevelConfig().stream().collect(Collectors.toMap(AExperienceLevel::getLevel, Function.identity()));
Guild guild = guildService.getGuildById(serverId);
List<LeaderBoardEntryResult> allElements = userExperienceManagementService.getWindowedLeaderboardEntriesForUser(aUserInAServer, windowSize);
return allElements.stream()
.map(leaderboardEntry -> convertFromLeaderboardEntry(guild, leaderboardEntry, experienceRolesForServer, levels))
.toList();
}
private UserExperienceDisplay convertFromUser(Guild guild, AUserExperience aUserExperience, Pageable pageable, Page<AUserExperience> page) { private UserExperienceDisplay convertFromUser(Guild guild, AUserExperience aUserExperience, Pageable pageable, Page<AUserExperience> page) {
Long userId = aUserExperience.getUser().getUserReference().getId(); Long userId = aUserExperience.getUser().getUserReference().getId();
Member member = guild.getMember(UserSnowflake.fromId(userId)); Member member = guild.getMember(UserSnowflake.fromId(userId));
AExperienceRole experienceRole = aUserExperience.getCurrentExperienceRole(); AExperienceRole experienceRole = aUserExperience.getCurrentExperienceRole();
UserDisplay userDisplay = null; UserDisplay userDisplay = null;
RoleDisplay roleDisplay = null; RoleDisplay roleDisplay = null;
Long experienceNeededToNextLevel = experienceLevelService.calculateExperienceToNextLevel(aUserExperience.getCurrentLevel().getLevel(), aUserExperience.getExperience());
Long nextLevelExperience = experienceLevelService.calculateNextLevel(aUserExperience.getCurrentLevel().getLevel()).getExperienceNeeded();
if(experienceRole != null) { if(experienceRole != null) {
Role role = guild.getRoleById(experienceRole.getRole().getId()); Role role = guild.getRoleById(experienceRole.getRole().getId());
if(role != null) { if(role != null) {
@@ -66,6 +110,9 @@ public class LeaderboardController {
if(member != null) { if(member != null) {
userDisplay = UserDisplay.fromMember(member); userDisplay = UserDisplay.fromMember(member);
} }
Long currentExpNeeded = aUserExperience.getCurrentLevel().getExperienceNeeded();
Long experienceWithinLevel = aUserExperience.getExperience() - currentExpNeeded;
Long experienceNeededForCurrentLevel = nextLevelExperience - currentExpNeeded;
return UserExperienceDisplay return UserExperienceDisplay
.builder() .builder()
.id(String.valueOf(userId)) .id(String.valueOf(userId))
@@ -73,9 +120,56 @@ public class LeaderboardController {
.level(aUserExperience.getLevelOrDefault()) .level(aUserExperience.getLevelOrDefault())
.rank((int) pageable.getOffset() + page.getContent().indexOf(aUserExperience) + 1) .rank((int) pageable.getOffset() + page.getContent().indexOf(aUserExperience) + 1)
.experience(aUserExperience.getExperience()) .experience(aUserExperience.getExperience())
.experienceToNextLevel(experienceNeededToNextLevel)
.currentLevelExperienceNeeded(experienceNeededForCurrentLevel)
.experienceOnCurrentLevel(experienceWithinLevel)
.percentage(((float) experienceWithinLevel / experienceNeededForCurrentLevel) * 100)
.nextLevelExperienceNeeded(nextLevelExperience)
.role(roleDisplay) .role(roleDisplay)
.member(userDisplay) .member(userDisplay)
.build(); .build();
} }
private UserExperienceDisplay convertFromLeaderboardEntry(Guild guild, LeaderBoardEntryResult leaderBoardEntryResult,
Map<Long, AExperienceRole> experienceRolesForServer, Map<Integer, AExperienceLevel> levels) {
Long userId = leaderBoardEntryResult.getUserId();
Member member = guild.getMember(UserSnowflake.fromId(userId));
UserDisplay userDisplay = null;
RoleDisplay roleDisplay = null;
Long experienceNeededToNextLevel = experienceLevelService.calculateExperienceToNextLevel(leaderBoardEntryResult.getLevel(),
leaderBoardEntryResult.getExperience());
AExperienceLevel currentExperienceLevel = levels.get(leaderBoardEntryResult.getLevel());
Long nextLevelExperience = experienceLevelService.calculateNextLevel(leaderBoardEntryResult.getLevel()).getExperienceNeeded();
if(experienceRolesForServer.containsKey(leaderBoardEntryResult.getRoleId())) {
AExperienceRole experienceRole = experienceRolesForServer.get(leaderBoardEntryResult.getRoleId());
Role role = guild.getRoleById(experienceRole.getRole().getId());
if(role != null) {
roleDisplay = RoleDisplay.fromRole(role);
} else {
roleDisplay = RoleDisplay.fromARole(experienceRole.getRole());
}
}
if(member != null) {
userDisplay = UserDisplay.fromMember(member);
}
Long currentExpNeeded = currentExperienceLevel.getExperienceNeeded();
Long experienceWithinLevel = leaderBoardEntryResult.getExperience() - currentExpNeeded;
Long experienceNeededForCurrentLevel = nextLevelExperience - currentExpNeeded;
return UserExperienceDisplay
.builder()
.id(String.valueOf(userId))
.messages(leaderBoardEntryResult.getMessageCount())
.level(leaderBoardEntryResult.getLevel())
.rank(leaderBoardEntryResult.getRank())
.experience(leaderBoardEntryResult.getExperience())
.experienceToNextLevel(experienceNeededToNextLevel)
.currentLevelExperienceNeeded(experienceNeededForCurrentLevel)
.experienceOnCurrentLevel(experienceWithinLevel)
.percentage(((float) experienceWithinLevel / experienceNeededForCurrentLevel) * 100)
.nextLevelExperienceNeeded(nextLevelExperience)
.role(roleDisplay)
.member(userDisplay)
.build();
}
} }

View File

@@ -1,19 +1,24 @@
package dev.sheldan.abstracto.experience.command; package dev.sheldan.abstracto.experience.command;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand; import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.*; import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.ParameterValidator;
import dev.sheldan.abstracto.core.command.config.validator.MinIntegerValueValidator; import dev.sheldan.abstracto.core.command.config.validator.MinIntegerValueValidator;
import dev.sheldan.abstracto.core.command.execution.CommandContext; import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult; import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService; import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.models.database.AServer; import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer; import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService; import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService; import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService; import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils; import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition; import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition;
import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames; import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames;
@@ -23,8 +28,10 @@ import dev.sheldan.abstracto.experience.model.LeaderBoardEntry;
import dev.sheldan.abstracto.experience.model.template.LeaderBoardEntryModel; import dev.sheldan.abstracto.experience.model.template.LeaderBoardEntryModel;
import dev.sheldan.abstracto.experience.model.template.LeaderBoardModel; import dev.sheldan.abstracto.experience.model.template.LeaderBoardModel;
import dev.sheldan.abstracto.experience.service.AUserExperienceService; import dev.sheldan.abstracto.experience.service.AUserExperienceService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend; import java.util.ArrayList;
import dev.sheldan.abstracto.core.templating.service.TemplateService; import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
@@ -33,11 +40,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/** /**
* Shows the experience gain information of the top 10 users in the server, or if a page number is provided as a parameter, only the members which are on this page. * Shows the experience gain information of the top 10 users in the server, or if a page number is provided as a parameter, only the members which are on this page.
*/ */
@@ -48,6 +50,7 @@ public class LeaderBoardCommand extends AbstractConditionableCommand {
public static final String LEADER_BOARD_POST_EMBED_TEMPLATE = "leaderboard_post"; public static final String LEADER_BOARD_POST_EMBED_TEMPLATE = "leaderboard_post";
private static final String LEDERBOARD_COMMAND_NAME = "leaderboard"; private static final String LEDERBOARD_COMMAND_NAME = "leaderboard";
private static final String PAGE_PARAMETER = "page"; private static final String PAGE_PARAMETER = "page";
private static final String FOCUS_PARAMETER = "focus";
@Autowired @Autowired
private AUserExperienceService userExperienceService; private AUserExperienceService userExperienceService;
@@ -80,21 +83,26 @@ public class LeaderBoardCommand extends AbstractConditionableCommand {
List<Object> parameters = commandContext.getParameters().getParameters(); List<Object> parameters = commandContext.getParameters().getParameters();
// parameter is optional, in case its not present, we default to the 0th page // parameter is optional, in case its not present, we default to the 0th page
Integer page = !parameters.isEmpty() ? (Integer) parameters.get(0) : 1; Integer page = !parameters.isEmpty() ? (Integer) parameters.get(0) : 1;
return getMessageToSend(commandContext.getAuthor(), page) return getMessageToSend(commandContext.getAuthor(), page, false)
.thenCompose(messageToSend -> FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))) .thenCompose(messageToSend -> FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel())))
.thenApply(aVoid -> CommandResult.fromIgnored()); .thenApply(aVoid -> CommandResult.fromIgnored());
} }
private CompletableFuture<MessageToSend> getMessageToSend(Member actorUser, Integer page) { private CompletableFuture<MessageToSend> getMessageToSend(Member actorUser, Integer page, boolean focusMe) {
AServer server = serverManagementService.loadServer(actorUser.getGuild()); AServer server = serverManagementService.loadServer(actorUser.getGuild());
LeaderBoard leaderBoard = userExperienceService.findLeaderBoardData(server, page); LeaderBoard leaderBoard;
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(actorUser);
if (focusMe) {
leaderBoard = userExperienceService.findLeaderBoardDataForUserFocus(aUserInAServer);
} else {
leaderBoard = userExperienceService.findLeaderBoardData(server, page);
}
List<CompletableFuture> futures = new ArrayList<>(); List<CompletableFuture> futures = new ArrayList<>();
CompletableFuture<List<LeaderBoardEntryModel>> completableFutures = converter.fromLeaderBoard(leaderBoard); CompletableFuture<List<LeaderBoardEntryModel>> completableFutures = converter.fromLeaderBoard(leaderBoard, actorUser.getGuild().getIdLong());
futures.add(completableFutures); futures.add(completableFutures);
log.info("Rendering leaderboard for page {} in server {} for user {}.", page, actorUser.getId(), actorUser.getGuild().getId()); log.info("Rendering leaderboard for page {} in server {} for user {}.", page, actorUser.getId(), actorUser.getGuild().getId());
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(actorUser);
LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(aUserInAServer); LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(aUserInAServer);
CompletableFuture<List<LeaderBoardEntryModel>> userRankFuture = converter.fromLeaderBoardEntry(Arrays.asList(userRank)); CompletableFuture<List<LeaderBoardEntryModel>> userRankFuture = converter.fromLeaderBoardEntry(Arrays.asList(userRank), actorUser.getGuild().getIdLong());
futures.add(userRankFuture); futures.add(userRankFuture);
String leaderboardUrl; String leaderboardUrl;
if(!StringUtils.isBlank(leaderboardExternalURL)) { if(!StringUtils.isBlank(leaderboardExternalURL)) {
@@ -108,6 +116,7 @@ public class LeaderBoardCommand extends AbstractConditionableCommand {
.builder() .builder()
.userExperiences(finalModels) .userExperiences(finalModels)
.leaderboardUrl(leaderboardUrl) .leaderboardUrl(leaderboardUrl)
.showPlacement(!focusMe)
.userExecuting(userRankFuture.join().get(0)) .userExecuting(userRankFuture.join().get(0))
.build(); .build();
return CompletableFuture.completedFuture(templateService.renderEmbedTemplate(LEADER_BOARD_POST_EMBED_TEMPLATE, leaderBoardModel, actorUser.getGuild().getIdLong())); return CompletableFuture.completedFuture(templateService.renderEmbedTemplate(LEADER_BOARD_POST_EMBED_TEMPLATE, leaderBoardModel, actorUser.getGuild().getIdLong()));
@@ -118,12 +127,18 @@ public class LeaderBoardCommand extends AbstractConditionableCommand {
@Override @Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) { public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Integer page; Integer page;
boolean focusMe;
if (slashCommandParameterService.hasCommandOption(FOCUS_PARAMETER, event)) {
focusMe = slashCommandParameterService.getCommandOption(FOCUS_PARAMETER, event, Boolean.class);
} else {
focusMe = false;
}
if(slashCommandParameterService.hasCommandOption(PAGE_PARAMETER, event)) { if(slashCommandParameterService.hasCommandOption(PAGE_PARAMETER, event)) {
page = slashCommandParameterService.getCommandOption(PAGE_PARAMETER, event, Integer.class); page = slashCommandParameterService.getCommandOption(PAGE_PARAMETER, event, Integer.class);
} else { } else {
page = 1; page = 1;
} }
return getMessageToSend(event.getMember(), page) return getMessageToSend(event.getMember(), page, focusMe)
.thenCompose(messageToSend -> interactionService.replyMessageToSend(messageToSend, event)) .thenCompose(messageToSend -> interactionService.replyMessageToSend(messageToSend, event))
.thenApply(aVoid -> CommandResult.fromIgnored()); .thenApply(aVoid -> CommandResult.fromIgnored());
} }
@@ -139,7 +154,17 @@ public class LeaderBoardCommand extends AbstractConditionableCommand {
.templated(true) .templated(true)
.type(Integer.class) .type(Integer.class)
.build(); .build();
List<Parameter> parameters = Arrays.asList(pageParameter);
Parameter focusMe = Parameter
.builder()
.name(FOCUS_PARAMETER)
.validators(leaderBoardPageValidators)
.optional(true)
.slashCommandOnly(true)
.templated(true)
.type(Boolean.class)
.build();
List<Parameter> parameters = Arrays.asList(pageParameter, focusMe);
HelpInfo helpInfo = HelpInfo HelpInfo helpInfo = HelpInfo
.builder() .builder()
.templated(true) .templated(true)

View File

@@ -30,7 +30,9 @@ import dev.sheldan.abstracto.core.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; 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.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -78,6 +80,9 @@ public class Rank extends AbstractConditionableCommand {
@Autowired @Autowired
private InteractionService interactionService; private InteractionService interactionService;
@Value("${abstracto.experience.leaderboard.externalUrl}")
private String leaderboardExternalURL;
@Override @Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) { public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters(); List<Object> parameters = commandContext.getParameters().getParameters();
@@ -87,7 +92,7 @@ public class Rank extends AbstractConditionableCommand {
} }
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(targetMember); AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(targetMember);
LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(aUserInAServer); LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(aUserInAServer);
CompletableFuture<List<LeaderBoardEntryModel>> future = converter.fromLeaderBoardEntry(Arrays.asList(userRank)); CompletableFuture<List<LeaderBoardEntryModel>> future = converter.fromLeaderBoardEntry(Arrays.asList(userRank), commandContext.getGuild().getIdLong());
RankModel rankModel = RankModel RankModel rankModel = RankModel
.builder() .builder()
.member(targetMember) .member(targetMember)
@@ -107,14 +112,21 @@ public class Rank extends AbstractConditionableCommand {
Long currentExpNeeded = experienceObj.getCurrentLevel().getExperienceNeeded(); Long currentExpNeeded = experienceObj.getCurrentLevel().getExperienceNeeded();
Long experienceNeededToNextLevel = experienceLevelService.calculateExperienceToNextLevel(experienceObj.getCurrentLevel().getLevel(), experienceObj.getExperience()); Long experienceNeededToNextLevel = experienceLevelService.calculateExperienceToNextLevel(experienceObj.getCurrentLevel().getLevel(), experienceObj.getExperience());
Long nextLevelExperience = experienceLevelService.calculateNextLevel(experienceObj.getCurrentLevel().getLevel()).getExperienceNeeded(); Long nextLevelExperience = experienceLevelService.calculateNextLevel(experienceObj.getCurrentLevel().getLevel()).getExperienceNeeded();
Long levelExperience = nextLevelExperience - currentExpNeeded; Long experienceNeededForCurrentLevel = nextLevelExperience - currentExpNeeded;
Long inLevelExperience = experienceObj.getExperience() - currentExpNeeded; Long experienceWithinLevel = experienceObj.getExperience() - currentExpNeeded;
rankModel.setExperienceForCurrentLevel(currentExpNeeded); rankModel.setExperienceForCurrentLevel(currentExpNeeded);
rankModel.setCurrentLevelPercentage(((float) inLevelExperience / levelExperience) * 100); rankModel.setCurrentLevelPercentage(((float) experienceWithinLevel / experienceNeededForCurrentLevel) * 100);
rankModel.setLevelExperience(levelExperience); rankModel.setLevelExperience(experienceNeededForCurrentLevel);
rankModel.setExperienceToNextLevel(experienceNeededToNextLevel); rankModel.setExperienceToNextLevel(experienceNeededToNextLevel);
rankModel.setInLevelExperience(inLevelExperience); rankModel.setInLevelExperience(experienceWithinLevel);
rankModel.setNextLevelExperience(nextLevelExperience); rankModel.setNextLevelExperience(nextLevelExperience);
String leaderboardUrl;
if(!StringUtils.isBlank(leaderboardExternalURL)) {
leaderboardUrl = String.format("%s/experience/leaderboards/%s/%s", leaderboardExternalURL, toRender.getGuild().getIdLong(), toRender.getId());
} else {
leaderboardUrl = null;
}
rankModel.setLeaderboardUrl(leaderboardUrl);
return templateService.renderEmbedTemplate(RANK_POST_EMBED_TEMPLATE, rankModel, toRender.getGuild().getIdLong()); return templateService.renderEmbedTemplate(RANK_POST_EMBED_TEMPLATE, rankModel, toRender.getGuild().getIdLong());
} }
@@ -128,7 +140,7 @@ public class Rank extends AbstractConditionableCommand {
} }
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(targetMember); AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(targetMember);
LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(aUserInAServer); LeaderBoardEntry userRank = userExperienceService.getRankOfUserInServer(aUserInAServer);
CompletableFuture<List<LeaderBoardEntryModel>> future = converter.fromLeaderBoardEntry(Arrays.asList(userRank)); CompletableFuture<List<LeaderBoardEntryModel>> future = converter.fromLeaderBoardEntry(Arrays.asList(userRank), event.getGuild().getIdLong());
RankModel rankModel = RankModel RankModel rankModel = RankModel
.builder() .builder()
.member(targetMember) .member(targetMember)

View File

@@ -3,7 +3,6 @@ package dev.sheldan.abstracto.experience.converter;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.experience.model.LeaderBoard; import dev.sheldan.abstracto.experience.model.LeaderBoard;
import dev.sheldan.abstracto.experience.model.LeaderBoardEntry; import dev.sheldan.abstracto.experience.model.LeaderBoardEntry;
import dev.sheldan.abstracto.experience.model.database.AUserExperience;
import dev.sheldan.abstracto.experience.model.template.LeaderBoardEntryModel; import dev.sheldan.abstracto.experience.model.template.LeaderBoardEntryModel;
import dev.sheldan.abstracto.experience.service.management.UserExperienceManagementService; import dev.sheldan.abstracto.experience.service.management.UserExperienceManagementService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -42,26 +41,23 @@ public class LeaderBoardModelConverter {
* @return The list of {@link LeaderBoardEntryModel leaderboarEntryModels} which contain the fully fledged information provided to the * @return The list of {@link LeaderBoardEntryModel leaderboarEntryModels} which contain the fully fledged information provided to the
* leader board template * leader board template
*/ */
public CompletableFuture<List<LeaderBoardEntryModel>> fromLeaderBoard(LeaderBoard leaderBoard) { public CompletableFuture<List<LeaderBoardEntryModel>> fromLeaderBoard(LeaderBoard leaderBoard, Long serverId) {
log.debug("Converting {} entries to a list of leaderboard entries.", leaderBoard.getEntries().size()); log.debug("Converting {} entries to a list of leaderboard entries.", leaderBoard.getEntries().size());
return fromLeaderBoardEntry(leaderBoard.getEntries()); return fromLeaderBoardEntry(leaderBoard.getEntries(), serverId);
} }
public CompletableFuture<List<LeaderBoardEntryModel>> fromLeaderBoardEntry(List<LeaderBoardEntry> leaderBoardEntries) { public CompletableFuture<List<LeaderBoardEntryModel>> fromLeaderBoardEntry(List<LeaderBoardEntry> leaderBoardEntries, Long serverId) {
List<Long> userIds = new ArrayList<>(); List<Long> userIds = new ArrayList<>();
Long serverId = leaderBoardEntries.get(0).getExperience().getServer().getId();
Map<Long, LeaderBoardEntryModel> models = leaderBoardEntries Map<Long, LeaderBoardEntryModel> models = leaderBoardEntries
.stream() .stream()
.map(leaderBoardEntry -> { .map(leaderBoardEntry -> {
AUserExperience experience = leaderBoardEntry.getExperience(); userIds.add(leaderBoardEntry.getUserId());
Long userId = experience.getUser().getUserReference().getId();
userIds.add(userId);
return LeaderBoardEntryModel return LeaderBoardEntryModel
.builder() .builder()
.userId(userId) .userId(leaderBoardEntry.getUserId())
.experience(experience.getExperience()) .experience(leaderBoardEntry.getExperience())
.messageCount(experience.getMessageCount()) .messageCount(leaderBoardEntry.getMessageCount())
.level(experience.getLevelOrDefault()) .level(leaderBoardEntry.getLevel())
.rank(leaderBoardEntry.getRank()) .rank(leaderBoardEntry.getRank())
.build(); .build();
}) })

View File

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

View File

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

View File

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

View File

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

@@ -16,4 +16,9 @@ public class UserExperienceDisplay {
private Long experience; private Long experience;
private Long messages; private Long messages;
private RoleDisplay role; private RoleDisplay role;
private Long experienceToNextLevel;
private Long experienceOnCurrentLevel;
private Long currentLevelExperienceNeeded;
private Float percentage;
private Long nextLevelExperienceNeeded;
} }

View File

@@ -55,6 +55,33 @@ public interface UserExperienceRepository extends JpaRepository<AUserExperience
"WHERE rank.id = :userInServerId", nativeQuery = true) "WHERE rank.id = :userInServerId", nativeQuery = true)
LeaderBoardEntryResult getRankOfUserInServer(@Param("userInServerId") Long id, @Param("serverId") Long serverId); LeaderBoardEntryResult getRankOfUserInServer(@Param("userInServerId") Long id, @Param("serverId") Long serverId);
@Query(value = "WITH user_experience_ranked AS" +
" ( " +
" SELECT us.id, uis.user_id, us.experience, us.role_id, us.level_id, us.message_count, ROW_NUMBER() OVER ( ORDER BY experience DESC ) as rank" +
" FROM user_experience us INNER JOIN user_in_server uis ON us.id = uis.user_in_server_id " +
" INNER JOIN server s ON s.id = uis.server_id WHERE s.id = :serverId" +
" )," +
" user_experience_target as (" +
" SELECT ranking.id as id, ranking.user_id, ranking.experience as experience, ranking.message_count, ranking.level_id as level, ranking.rank " +
" FROM user_experience_ranked ranking" +
" WHERE ranking.id = :userInServerId" +
" )" +
" (SELECT r.id, r.user_id as userId, r.experience, r.level_id as level, r.role_id as roleId, r.message_count as messageCount, r.rank as rank" +
" FROM user_experience_ranked r " +
" CROSS JOIN user_experience_target t" +
" WHERE r.rank <= t.rank " +
" ORDER BY r.rank DESC " +
" LIMIT :before) " +
"UNION ALL " +
" (SELECT r.id, r.user_id as userId, r.experience, r.level_id as level, r.role_id as roleId, r.message_count as messageCount, r.rank as rank" +
" FROM user_experience_ranked r" +
" CROSS JOIN user_experience_target t" +
" WHERE r.rank > t.rank " +
" ORDER BY r.rank " +
" LIMIT :after) " +
"ORDER BY rank", nativeQuery = true)
List<LeaderBoardEntryResult> getRankOfUserWithWindow(@Param("userInServerId") Long id, @Param("serverId") Long serverId, @Param("before") Long beforeInclusive, @Param("after") Long after);
@Modifying(clearAutomatically = true) @Modifying(clearAutomatically = true)
@Query("update AUserExperience u set u.currentExperienceRole = null where u.currentExperienceRole.id = :roleId") @Query("update AUserExperience u set u.currentExperienceRole = null where u.currentExperienceRole.id = :roleId")
void removeExperienceRoleFromUsers(@Param("roleId") Long experienceRoleId); void removeExperienceRoleFromUsers(@Param("roleId") Long experienceRoleId);

View File

@@ -337,8 +337,8 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
.builder() .builder()
.build(); .build();
boolean userChangesLevel = !Objects.equals(newLevel.getLevel(), aUserExperience.getCurrentLevel().getLevel()); boolean userChangesLevel = !Objects.equals(newLevel.getLevel(), aUserExperience.getCurrentLevel().getLevel());
Integer oldLevel = aUserExperience.getCurrentLevel() != null ? aUserExperience.getCurrentLevel().getLevel() : 0;
if(userChangesLevel) { if(userChangesLevel) {
Integer oldLevel = aUserExperience.getCurrentLevel() != null ? aUserExperience.getCurrentLevel().getLevel() : 0;
log.info("User {} in server {} changed level. New {}, Old {}.", member.getIdLong(), log.info("User {} in server {} changed level. New {}, Old {}.", member.getIdLong(),
member.getGuild().getIdLong(), newLevel.getLevel(), member.getGuild().getIdLong(), newLevel.getLevel(),
oldLevel); oldLevel);
@@ -361,7 +361,7 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
.newRole(oldRoleId != null ? RoleDisplay.fromRole(oldRoleId) : null) .newRole(oldRoleId != null ? RoleDisplay.fromRole(oldRoleId) : null)
.newRole(newRoleId != null ? RoleDisplay.fromRole(newRoleId) : null) .newRole(newRoleId != null ? RoleDisplay.fromRole(newRoleId) : null)
.build(); .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 -> { 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()); log.info("Sent level up notification to user {} in server {} in channel {}.", member.getIdLong(), serverId, message.getChannel().getIdLong());
}).exceptionally(throwable -> { }).exceptionally(throwable -> {
@@ -373,7 +373,7 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
} }
aUserExperience.setMessageCount(aUserExperience.getMessageCount() + 1L); aUserExperience.setMessageCount(aUserExperience.getMessageCount() + 1L);
if(userChangesLevel && featureModeService.featureModeActive(ExperienceFeatureDefinition.EXPERIENCE, server, ExperienceFeatureMode.LEVEL_ACTION)) { if(userChangesLevel && featureModeService.featureModeActive(ExperienceFeatureDefinition.EXPERIENCE, server, ExperienceFeatureMode.LEVEL_ACTION)) {
levelActionService.applyLevelActionsToUser(aUserExperience) levelActionService.applyLevelActionsToUser(aUserExperience, oldLevel)
.thenAccept(unused -> { .thenAccept(unused -> {
log.info("Executed level actions for user {}.", userInServerId); log.info("Executed level actions for user {}.", userInServerId);
}) })
@@ -491,11 +491,32 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
int pageOffset = page * pageSize; int pageOffset = page * pageSize;
for (int i = 0; i < experiences.size(); i++) { for (int i = 0; i < experiences.size(); i++) {
AUserExperience userExperience = experiences.get(i); AUserExperience userExperience = experiences.get(i);
entries.add(LeaderBoardEntry.builder().experience(userExperience).rank(pageOffset + i + 1).build()); LeaderBoardEntry entry = LeaderBoardEntry.fromAUserExperience(userExperience);
entry.setRank(pageOffset + i + 1);
entries.add(entry);
} }
return LeaderBoard.builder().entries(entries).build(); return LeaderBoard.builder().entries(entries).build();
} }
@Override
public LeaderBoard findLeaderBoardDataForUserFocus(AUserInAServer aUserInAServer) {
List<LeaderBoardEntry> allEntries =
userExperienceManagementService.getWindowedLeaderboardEntriesForUser(aUserInAServer, 10)
.stream().map(leaderBoardEntryResult -> LeaderBoardEntry
.builder()
.experience(leaderBoardEntryResult.getExperience())
.level(leaderBoardEntryResult.getLevel())
.userId(leaderBoardEntryResult.getUserId())
.messageCount(leaderBoardEntryResult.getMessageCount())
.rank(leaderBoardEntryResult.getRank())
.build())
.collect(Collectors.toList());
return LeaderBoard
.builder()
.entries(allEntries)
.build();
}
@Override @Override
public LeaderBoardEntry getRankOfUserInServer(AUserInAServer userInAServer) { public LeaderBoardEntry getRankOfUserInServer(AUserInAServer userInAServer) {
log.debug("Retrieving rank for {}", userInAServer.getUserReference().getId()); log.debug("Retrieving rank for {}", userInAServer.getUserReference().getId());
@@ -509,7 +530,9 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
if(rankOfUserInServer != null) { if(rankOfUserInServer != null) {
rank = rankOfUserInServer.getRank(); rank = rankOfUserInServer.getRank();
} }
return LeaderBoardEntry.builder().experience(aUserExperience).rank(rank).build(); LeaderBoardEntry entry = LeaderBoardEntry.fromAUserExperience(aUserExperience);
entry.setRank(rank);
return entry;
} }
} }

View File

@@ -49,6 +49,11 @@ public class LevelActionServiceBean implements LevelActionService {
@Override @Override
public CompletableFuture<Void> applyLevelActionsToUser(AUserExperience user) { 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()) { if(levelActions == null || levelActions.isEmpty()) {
return CompletableFuture.completedFuture(null); return CompletableFuture.completedFuture(null);
} }
@@ -60,8 +65,16 @@ public class LevelActionServiceBean implements LevelActionService {
Map<Integer, List<LevelAction>> actionConfigMap = new HashMap<>(); 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 -> { 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; return;
} }
if(actionConfigMap.containsKey(levelAction.getLevel().getLevel())) { 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 List<Integer> levels = actionConfigMap
.keySet() .keySet()

View File

@@ -82,6 +82,12 @@ public class UserExperienceManagementServiceBean implements UserExperienceManage
return repository.findTop10ByUser_ServerReferenceOrderByExperienceDesc(aServer, PageRequest.of(page, size)); return repository.findTop10ByUser_ServerReferenceOrderByExperienceDesc(aServer, PageRequest.of(page, size));
} }
@Override
public List<LeaderBoardEntryResult> getWindowedLeaderboardEntriesForUser(AUserInAServer aUserInAServer, Integer windowSize) {
return repository.getRankOfUserWithWindow(aUserInAServer.getUserInServerId(), aUserInAServer.getServerReference().getId(), windowSize.longValue() / 2 + 1,
windowSize.longValue() / 2);
}
@Override @Override
public LeaderBoardEntryResult getRankOfUserInServer(AUserExperience userExperience) { public LeaderBoardEntryResult getRankOfUserInServer(AUserExperience userExperience) {
return repository.getRankOfUserInServer(userExperience.getId(), userExperience.getServer().getId()); return repository.getRankOfUserInServer(userExperience.getId(), userExperience.getServer().getId());

View File

@@ -1,87 +0,0 @@
package dev.sheldan.abstracto.experience.converter;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.experience.model.LeaderBoard;
import dev.sheldan.abstracto.experience.model.LeaderBoardEntry;
import dev.sheldan.abstracto.experience.model.database.AUserExperience;
import dev.sheldan.abstracto.experience.model.template.LeaderBoardEntryModel;
import net.dv8tion.jda.api.entities.Member;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class LeaderBoardModelConverterTest {
@InjectMocks
public LeaderBoardModelConverter testUnit;
@Mock
private MemberService memberService;
@Mock
private LeaderBoardModelConverter self;
private static final Long SERVER_ID = 4L;
private static final Long USER_ID = 5L;
private static final Long USER_ID_2 = 6L;
private static final Long USER_IN_SERVER_ID = 7L;
private static final Long USER_IN_SERVER_ID_2 = 8L;
private static final Long EXPERIENCE = 9L;
private static final Long MESSAGES = 10L;
private static final Integer LEVEL = 54;
@Test
public void testFromLeaderBoard() {
Integer firstRank = 1;
LeaderBoardEntry entry = getEntry(firstRank, USER_ID, USER_IN_SERVER_ID);
Integer secondRank = 2;
LeaderBoardEntry entry2 = getEntry(secondRank, USER_ID_2, USER_IN_SERVER_ID_2);
List<LeaderBoardEntry> entries = Arrays.asList(entry, entry2);
LeaderBoard leaderBoard = Mockito.mock(LeaderBoard.class);
when(leaderBoard.getEntries()).thenReturn(entries);
Member member = Mockito.mock(Member.class);
when(member.getIdLong()).thenReturn(USER_ID);
when(memberService.getMembersInServerAsync(SERVER_ID, Arrays.asList(USER_ID, USER_ID_2))).thenReturn(CompletableFuture.completedFuture(Arrays.asList(member)));
CompletableFuture<List<LeaderBoardEntryModel>> leaderBoardEntryModels = testUnit.fromLeaderBoard(leaderBoard);
LeaderBoardEntryModel firstEntry = leaderBoardEntryModels.join().get(0);
Assert.assertEquals(USER_ID, firstEntry.getUserId());
LeaderBoardEntryModel secondEntry = leaderBoardEntryModels.join().get(1);
Assert.assertEquals(USER_ID_2, secondEntry.getUserId());
Assert.assertEquals(entries.size(), leaderBoardEntryModels.join().size());
}
private LeaderBoardEntry getEntry(Integer rank, Long userId, Long userInServerId) {
AUserExperience experience = Mockito.mock(AUserExperience.class);
when(experience.getMessageCount()).thenReturn(MESSAGES);
when(experience.getExperience()).thenReturn(EXPERIENCE);
AUserInAServer userInAServer = Mockito.mock(AUserInAServer.class);
when(experience.getUser()).thenReturn(userInAServer);
AUser user = Mockito.mock(AUser.class);
when(userInAServer.getUserReference()).thenReturn(user);
when(user.getId()).thenReturn(userId);
AServer server = Mockito.mock(AServer.class);
when(experience.getServer()).thenReturn(server);
when(server.getId()).thenReturn(SERVER_ID);
LeaderBoardEntry entry = Mockito.mock(LeaderBoardEntry.class);
when(entry.getRank()).thenReturn(rank);
when(entry.getExperience()).thenReturn(experience);
return entry;
}
}

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> <parent>
<groupId>dev.sheldan.abstracto.modules</groupId> <groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>experience-tracking</artifactId> <artifactId>experience-tracking</artifactId>
<version>1.5.36</version> <version>1.5.53-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

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.AUserExperience;
import dev.sheldan.abstracto.experience.model.database.LevelAction; import dev.sheldan.abstracto.experience.model.database.LevelAction;
import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Guild;
import org.springframework.stereotype.Component;
@Component
public interface LevelActionListener { public interface LevelActionListener {
String getName(); String getName();
void apply(AUserExperience userExperience, LevelAction levelAction, MemberActionModification container); 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); void prepareAction(LevelAction levelAction);

View File

@@ -12,12 +12,19 @@ import lombok.Setter;
@Setter @Setter
@Builder @Builder
public class LeaderBoardEntry { public class LeaderBoardEntry {
/** private Long userId;
* Object representing the current experience status of a user in a guild. private Integer level;
*/ private Long experience;
private AUserExperience experience; private Long messageCount;
/**
* The rank this user has in the respective guild.
*/
private Integer rank; private Integer rank;
public static LeaderBoardEntry fromAUserExperience(AUserExperience aUserExperience) {
return LeaderBoardEntry
.builder()
.experience(aUserExperience.getExperience())
.userId(aUserExperience.getUser().getUserReference().getId())
.messageCount(aUserExperience.getMessageCount())
.level(aUserExperience.getLevelOrDefault())
.build();
}
} }

View File

@@ -7,11 +7,8 @@ public interface LeaderBoardEntryResult {
Long getId(); Long getId();
/** Long getUserId();
* The {@link dev.sheldan.abstracto.core.models.database.AUserInAServer} id of the user Long getRoleId();
* @return The ID of the user in a server
*/
Long getUserInServerId();
/** /**
* The experience of the {@link dev.sheldan.abstracto.core.models.database.AUserInAServer} * The experience of the {@link dev.sheldan.abstracto.core.models.database.AUserInAServer}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.experience.model.template; package dev.sheldan.abstracto.experience.model.template;
import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext; import dev.sheldan.abstracto.core.models.context.SlimUserInitiatedServerContext;
import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
@@ -25,4 +26,9 @@ public class LeaderBoardModel extends SlimUserInitiatedServerContext {
*/ */
private LeaderBoardEntryModel userExecuting; private LeaderBoardEntryModel userExecuting;
private String leaderboardUrl; private String leaderboardUrl;
/**
* Whether to show the users own placement
*/
@Builder.Default
private boolean showPlacement = true;
} }

View File

@@ -47,4 +47,5 @@ public class RankModel {
* The member to show the rank for * The member to show the rank for
*/ */
private Member member; private Member member;
private String leaderboardUrl;
} }

View File

@@ -54,6 +54,7 @@ public interface AUserExperienceService {
* from the desired page * from the desired page
*/ */
LeaderBoard findLeaderBoardData(AServer server, Integer page); LeaderBoard findLeaderBoardData(AServer server, Integer page);
LeaderBoard findLeaderBoardDataForUserFocus(AUserInAServer aUserInAServer);
/** /**
* Retrieves the {@link LeaderBoardEntry} from a specific {@link AUserInAServer} containing information about the * Retrieves the {@link LeaderBoardEntry} from a specific {@link AUserInAServer} containing information about the

View File

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

View File

@@ -62,6 +62,7 @@ public interface UserExperienceManagementService {
* @return A list desc ordered by {@link AUserExperience} experience only containing the elements between {@code start} and @{code end} * @return A list desc ordered by {@link AUserExperience} experience only containing the elements between {@code start} and @{code end}
*/ */
List<AUserExperience> findLeaderBoardUsersPaginated(AServer server, Integer page, Integer size); List<AUserExperience> findLeaderBoardUsersPaginated(AServer server, Integer page, Integer size);
List<LeaderBoardEntryResult> getWindowedLeaderboardEntriesForUser(AUserInAServer aUserInAServer, Integer windowSize);
/** /**
* Returns the {@link LeaderBoardEntryResult} of the given {@link AUserExperience}. * Returns the {@link LeaderBoardEntryResult} of the given {@link AUserExperience}.

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>dev.sheldan.abstracto.modules</groupId> <groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>abstracto-modules</artifactId> <artifactId>abstracto-modules</artifactId>
<version>1.5.36</version> <version>1.5.53-SNAPSHOT</version>
</parent> </parent>
<artifactId>giveaway</artifactId> <artifactId>giveaway</artifactId>

View File

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

View File

@@ -58,7 +58,7 @@ public class AmongusText extends AbstractConditionableCommand {
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) { public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
String text = (String) commandContext.getParameters().getParameters().get(0); String text = (String) commandContext.getParameters().getParameters().get(0);
File amongusTextImage = imageGenerationService.getAmongusTextImage(text); 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 // template support does not support binary files
AttachedFile file = AttachedFile AttachedFile file = AttachedFile
.builder() .builder()
@@ -76,7 +76,7 @@ public class AmongusText extends AbstractConditionableCommand {
event.deferReply().queue(); event.deferReply().queue();
String text = slashCommandParameterService.getCommandOption(TEXT_PARAMETER_KEY, event, String.class); String text = slashCommandParameterService.getCommandOption(TEXT_PARAMETER_KEY, event, String.class);
File amongusTextImage = imageGenerationService.getAmongusTextImage(text); 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 // template support does not support binary files
AttachedFile file = AttachedFile AttachedFile file = AttachedFile
.builder() .builder()

View File

@@ -76,7 +76,7 @@ public class Bonk extends AbstractConditionableCommand {
} }
} }
File bonkGifFile = imageGenerationService.getBonkGif(member.getEffectiveAvatar().getUrl(imageSize)); 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 // template support does not support binary files
AttachedFile file = AttachedFile AttachedFile file = AttachedFile
.builder() .builder()
@@ -99,7 +99,7 @@ public class Bonk extends AbstractConditionableCommand {
targetMember = event.getMember(); targetMember = event.getMember();
} }
File bonkGifFile = imageGenerationService.getBonkGif(targetMember.getEffectiveAvatar().getUrl(imageSize)); 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 // template support does not support binary files
AttachedFile file = AttachedFile AttachedFile file = AttachedFile
.builder() .builder()

View File

@@ -76,7 +76,7 @@ public class Pat extends AbstractConditionableCommand {
} }
} }
File patGifFile = imageGenerationService.getPatGif(member.getEffectiveAvatar().getUrl(imageSize)); 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 // template support does not support binary files
AttachedFile file = AttachedFile AttachedFile file = AttachedFile
.builder() .builder()
@@ -99,7 +99,7 @@ public class Pat extends AbstractConditionableCommand {
targetMember = event.getMember(); targetMember = event.getMember();
} }
File patGifFile = imageGenerationService.getPatGif(targetMember.getEffectiveAvatar().getUrl(imageSize)); 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 // template support does not support binary files
AttachedFile file = AttachedFile AttachedFile file = AttachedFile
.builder() .builder()

View File

@@ -68,7 +68,7 @@ public class Triggered extends AbstractConditionableCommand {
member = (Member) parameters.get(0); member = (Member) parameters.get(0);
} }
File triggeredGifFile = imageGenerationService.getTriggeredGif(member.getEffectiveAvatar().getUrl(imageSize)); 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 // template support does not support binary files
AttachedFile file = AttachedFile AttachedFile file = AttachedFile
.builder() .builder()
@@ -91,7 +91,7 @@ public class Triggered extends AbstractConditionableCommand {
targetMember = event.getMember(); targetMember = event.getMember();
} }
File triggeredGifFile = imageGenerationService.getTriggeredGif(targetMember.getEffectiveAvatar().getUrl(imageSize)); 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 // template support does not support binary files
AttachedFile file = AttachedFile AttachedFile file = AttachedFile
.builder() .builder()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -90,7 +90,7 @@ public class UserNotes extends AbstractConditionableCommand {
CompletableFuture<List<NoteEntryModel>> listCompletableFuture = userNotesConverter.fromNotes(userNotes); CompletableFuture<List<NoteEntryModel>> listCompletableFuture = userNotesConverter.fromNotes(userNotes);
return listCompletableFuture.thenCompose(noteEntryModels -> { return listCompletableFuture.thenCompose(noteEntryModels -> {
model.setUserNotes(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()); .thenApply(aVoid -> CommandResult.fromIgnored());
}); });
} }

View File

@@ -81,7 +81,7 @@ public class BanModerationActionModalListener implements ModalInteractionListene
.findFirst() .findFirst()
.orElse(null); .orElse(null);
if(StringUtils.isBlank(tempReason)) { if(StringUtils.isBlank(tempReason)) {
reason = templateService.renderSimpleTemplate(DEFAULT_BAN_REASON_TEMPLATE_KEY); reason = templateService.renderSimpleTemplate(DEFAULT_BAN_REASON_TEMPLATE_KEY, model.getServerId());
} else { } else {
reason = tempReason; reason = tempReason;
} }

View File

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

View File

@@ -53,7 +53,7 @@ public class MyWarningsTest {
Long totalWarnCount = 10L; Long totalWarnCount = 10L;
when(warnManagementService.getTotalWarnsForUser(aUserInAServer)).thenReturn(totalWarnCount); when(warnManagementService.getTotalWarnsForUser(aUserInAServer)).thenReturn(totalWarnCount);
CommandResult result = testUnit.execute(noParameter); CommandResult result = testUnit.execute(noParameter);
verify(channelService, times(1)).sendEmbedTemplateInTextChannelList(eq(MyWarnings.MY_WARNINGS_RESPONSE_EMBED_TEMPLATE), argumentCaptor.capture(), eq(noParameter.getChannel())); verify(channelService, times(1)).sendEmbedTemplateInMessageChannel(eq(MyWarnings.MY_WARNINGS_RESPONSE_EMBED_TEMPLATE), argumentCaptor.capture(), eq(noParameter.getChannel()));
CommandTestUtilities.checkSuccessfulCompletion(result); CommandTestUtilities.checkSuccessfulCompletion(result);
MyWarningsModel usedModel = argumentCaptor.getValue(); MyWarningsModel usedModel = argumentCaptor.getValue();
Assert.assertEquals(activeWarnCount, usedModel.getCurrentWarnCount()); Assert.assertEquals(activeWarnCount, usedModel.getCurrentWarnCount());

View File

@@ -67,7 +67,7 @@ public class UserNotesTest {
CompletableFuture<List<NoteEntryModel>> convertedNotes = CompletableFuture.completedFuture(Arrays.asList(firstConvertedNote, secondConvertedNote)); CompletableFuture<List<NoteEntryModel>> convertedNotes = CompletableFuture.completedFuture(Arrays.asList(firstConvertedNote, secondConvertedNote));
when(userNotesConverter.fromNotes(userNotes)).thenReturn(convertedNotes); when(userNotesConverter.fromNotes(userNotes)).thenReturn(convertedNotes);
CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters); CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);
verify(channelService, times(1)).sendEmbedTemplateInTextChannelList(eq(UserNotes.USER_NOTES_RESPONSE_TEMPLATE), captor.capture(), eq(parameters.getChannel())); verify(channelService, times(1)).sendEmbedTemplateInMessageChannel(eq(UserNotes.USER_NOTES_RESPONSE_TEMPLATE), captor.capture(), eq(parameters.getChannel()));
ListNotesModel usedModel = captor.getValue(); ListNotesModel usedModel = captor.getValue();
List<NoteEntryModel> notes = convertedNotes.join(); List<NoteEntryModel> notes = convertedNotes.join();
Assert.assertEquals(notes.size(), usedModel.getUserNotes().size()); Assert.assertEquals(notes.size(), usedModel.getUserNotes().size());
@@ -96,7 +96,7 @@ public class UserNotesTest {
when(userNotesConverter.fromNotes(userNotes)).thenReturn(convertedNotes); when(userNotesConverter.fromNotes(userNotes)).thenReturn(convertedNotes);
CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters); CompletableFuture<CommandResult> result = testUnit.executeAsync(parameters);
List<NoteEntryModel> notes = convertedNotes.join(); List<NoteEntryModel> notes = convertedNotes.join();
verify(channelService, times(1)).sendEmbedTemplateInTextChannelList(eq(UserNotes.USER_NOTES_RESPONSE_TEMPLATE), captor.capture(), eq(parameters.getChannel())); verify(channelService, times(1)).sendEmbedTemplateInMessageChannel(eq(UserNotes.USER_NOTES_RESPONSE_TEMPLATE), captor.capture(), eq(parameters.getChannel()));
ListNotesModel usedModel = captor.getValue(); ListNotesModel usedModel = captor.getValue();
Assert.assertEquals(notes.size(), usedModel.getUserNotes().size()); Assert.assertEquals(notes.size(), usedModel.getUserNotes().size());
for (int i = 0; i < usedModel.getUserNotes().size(); i++) { for (int i = 0; i < usedModel.getUserNotes().size(); i++) {

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
<parent> <parent>
<groupId>dev.sheldan.abstracto.modules</groupId> <groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>modmail</artifactId> <artifactId>modmail</artifactId>
<version>1.5.36</version> <version>1.5.53-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -38,6 +38,12 @@
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>moderation-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>dev.sheldan.abstracto.core</groupId> <groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>metrics-int</artifactId> <artifactId>metrics-int</artifactId>

View File

@@ -8,7 +8,7 @@ import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandContext; import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult; import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.UserService;
import dev.sheldan.abstracto.modmail.condition.ModMailContextCondition; import dev.sheldan.abstracto.modmail.condition.ModMailContextCondition;
import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition; import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition;
import dev.sheldan.abstracto.modmail.exception.ModMailThreadClosedException; import dev.sheldan.abstracto.modmail.exception.ModMailThreadClosedException;
@@ -40,7 +40,7 @@ public class AnonReply extends AbstractConditionableCommand {
private ModMailThreadManagementService modMailThreadManagementService; private ModMailThreadManagementService modMailThreadManagementService;
@Autowired @Autowired
private MemberService memberService; private UserService userService;
@Override @Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) { public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
@@ -52,8 +52,8 @@ public class AnonReply extends AbstractConditionableCommand {
throw new ModMailThreadClosedException(); throw new ModMailThreadClosedException();
} }
Long threadId = modMailThread.getId(); Long threadId = modMailThread.getId();
return memberService.getMemberInServerAsync(modMailThread.getUser()).thenCompose(member -> return userService.retrieveUserForId(modMailThread.getUser().getUserReference().getId()).thenCompose(user ->
modMailThreadService.loadExecutingMemberAndRelay(threadId, text, commandContext.getMessage(), true, member) modMailThreadService.loadExecutingMemberAndRelay(threadId, text, commandContext.getMessage(), true, user, commandContext.getGuild())
).thenApply(aVoid -> CommandResult.fromSuccess()); ).thenApply(aVoid -> CommandResult.fromSuccess());
} }

View File

@@ -72,7 +72,8 @@ public class Close extends AbstractConditionableCommand {
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) { public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters(); List<Object> parameters = commandContext.getParameters().getParameters();
// the default value of the note is configurable via template // the default value of the note is configurable via template
String note = parameters.size() == 1 ? (String) parameters.get(0) : templateService.renderTemplate(MODMAIL_CLOSE_DEFAULT_NOTE_TEMPLATE_KEY, new Object()); String note = parameters.size() == 1 ? (String) parameters.get(0) : templateService.renderTemplate(MODMAIL_CLOSE_DEFAULT_NOTE_TEMPLATE_KEY, new Object(), commandContext.getGuild()
.getIdLong());
ModMailThread modMailThread = modMailThreadManagementService.getByChannelId(commandContext.getChannel().getIdLong()); ModMailThread modMailThread = modMailThreadManagementService.getByChannelId(commandContext.getChannel().getIdLong());
if(ModMailThreadState.CLOSED.equals(modMailThread.getState()) || ModMailThreadState.CLOSING.equals(modMailThread.getState())) { if(ModMailThreadState.CLOSED.equals(modMailThread.getState()) || ModMailThreadState.CLOSING.equals(modMailThread.getState())) {
throw new ModMailThreadClosedException(); throw new ModMailThreadClosedException();
@@ -81,7 +82,6 @@ public class Close extends AbstractConditionableCommand {
.builder() .builder()
.closingMember(commandContext.getAuthor()) .closingMember(commandContext.getAuthor())
.notifyUser(true) .notifyUser(true)
.channel(commandContext.getChannel())
.log(true) .log(true)
.note(note) .note(note)
.build(); .build();
@@ -112,19 +112,18 @@ public class Close extends AbstractConditionableCommand {
ClosingContext context = ClosingContext ClosingContext context = ClosingContext
.builder() .builder()
.closingMember(event.getMember()) .closingMember(event.getMember())
.channel(event.getChannel())
.notifyUser(!silently) .notifyUser(!silently)
.log(log) .log(log)
.note(note) .note(note)
.build(); .build();
return interactionService.replyEmbed(CLOSE_RESPONSE, event) return interactionService.replyEmbed(CLOSE_RESPONSE, event)
.thenCompose(interactionHook -> self.closeThread(context)) .thenCompose(interactionHook -> self.closeThread(context, event.getChannelIdLong()))
.thenApply(aVoid -> CommandResult.fromIgnored()); .thenApply(aVoid -> CommandResult.fromIgnored());
} }
@Transactional @Transactional
public CompletableFuture<Void> closeThread(ClosingContext closingContext) { public CompletableFuture<Void> closeThread(ClosingContext closingContext, Long channelId) {
ModMailThread modMailThread = modMailThreadManagementService.getByChannelId(closingContext.getChannel().getIdLong()); ModMailThread modMailThread = modMailThreadManagementService.getByChannelId(channelId);
if(ModMailThreadState.CLOSED.equals(modMailThread.getState()) || ModMailThreadState.CLOSING.equals(modMailThread.getState())) { if(ModMailThreadState.CLOSED.equals(modMailThread.getState()) || ModMailThreadState.CLOSING.equals(modMailThread.getState())) {
throw new ModMailThreadClosedException(); throw new ModMailThreadClosedException();
} }

View File

@@ -47,7 +47,8 @@ public class CloseSilently extends AbstractConditionableCommand {
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) { public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters(); List<Object> parameters = commandContext.getParameters().getParameters();
// default note text is configurable via template, because the note is optional // default note text is configurable via template, because the note is optional
String note = parameters.size() == 1 ? (String) parameters.get(0) : templateService.renderTemplate("modmail_close_default_note", new Object()); String note = parameters.size() == 1 ? (String) parameters.get(0) : templateService.renderTemplate("modmail_close_default_note", new Object(), commandContext.getGuild()
.getIdLong());
ModMailThread modMailThread = modMailThreadManagementService.getByChannelId(commandContext.getChannel().getIdLong()); ModMailThread modMailThread = modMailThreadManagementService.getByChannelId(commandContext.getChannel().getIdLong());
if(ModMailThreadState.CLOSED.equals(modMailThread.getState()) || ModMailThreadState.CLOSING.equals(modMailThread.getState())) { if(ModMailThreadState.CLOSED.equals(modMailThread.getState()) || ModMailThreadState.CLOSING.equals(modMailThread.getState())) {
throw new ModMailThreadClosedException(); throw new ModMailThreadClosedException();

View File

@@ -25,6 +25,7 @@ import dev.sheldan.abstracto.modmail.service.management.ModMailThreadManagementS
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.InteractionHook;
@@ -44,7 +45,7 @@ import java.util.concurrent.CompletableFuture;
@Slf4j @Slf4j
public class Contact extends AbstractConditionableCommand { public class Contact extends AbstractConditionableCommand {
private static final String CONTACT_PARAMETER = "contact"; private static final String COMMAND_NAME = "contact";
private static final String USER_PARMETER = "user"; private static final String USER_PARMETER = "user";
private static final String MODMAIL_THREAD_ALREADY_EXISTS_TEMPLATE = "modmail_thread_already_exists"; private static final String MODMAIL_THREAD_ALREADY_EXISTS_TEMPLATE = "modmail_thread_already_exists";
private static final String CONTACT_RESPONSE = "contact_response"; private static final String CONTACT_RESPONSE = "contact_response";
@@ -84,27 +85,24 @@ public class Contact extends AbstractConditionableCommand {
.existingModMailThread(existingThread) .existingModMailThread(existingThread)
.executingMemberDisplay(MemberNameDisplay.fromMember(targetUser)) .executingMemberDisplay(MemberNameDisplay.fromMember(targetUser))
.build(); .build();
List<CompletableFuture<Message>> futures = channelService.sendEmbedTemplateInTextChannelList(MODMAIL_THREAD_ALREADY_EXISTS_TEMPLATE, model, commandContext.getChannel()); List<CompletableFuture<Message>> futures = channelService.sendEmbedTemplateInMessageChannel(MODMAIL_THREAD_ALREADY_EXISTS_TEMPLATE, model, commandContext.getChannel());
return FutureUtils.toSingleFutureGeneric(futures).thenApply(aVoid -> CommandResult.fromIgnored()); return FutureUtils.toSingleFutureGeneric(futures).thenApply(aVoid -> CommandResult.fromIgnored());
} else { } else {
return modMailThreadService.createModMailThreadForUser(targetUser, null, false, commandContext.getUndoActions()) return modMailThreadService.createModMailThreadForUser(targetUser.getUser(), targetUser.getGuild(), null, false, commandContext.getUndoActions(), false)
.thenCompose(unused -> modMailThreadService.sendContactNotification(targetUser, unused, commandContext.getChannel())) .thenCompose(unused -> modMailThreadService.sendContactNotification(targetUser.getUser(), unused, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromSuccess()); .thenApply(aVoid -> CommandResult.fromSuccess());
} }
} }
@Override @Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) { public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Member member = slashCommandParameterService.getCommandOption(USER_PARMETER, event, Member.class); User user = slashCommandParameterService.getCommandOption(USER_PARMETER, event, User.class);
if(!member.getGuild().equals(event.getGuild())) { AUserInAServer userInAServer = userManagementService.loadOrCreateUser(event.getGuild().getIdLong(), user.getIdLong());
throw new EntityGuildMismatchException();
}
AUserInAServer user = userManagementService.loadOrCreateUser(member);
// if this AUserInAServer already has an open thread, we should instead post a message // if this AUserInAServer already has an open thread, we should instead post a message
// containing a link to the channel, instead of opening a new one // containing a link to the channel, instead of opening a new one
if(modMailThreadManagementService.hasOpenModMailThreadForUser(user)) { if(modMailThreadManagementService.hasOpenModMailThreadForUser(userInAServer)) {
log.info("Modmail thread for user {} in server {} already exists. Notifying user {}.", event.getMember().getId(), event.getGuild().getId(), user.getUserReference().getId()); log.info("Modmail thread for userInAServer {} in server {} already exists. Notifying userInAServer {}.", event.getMember().getId(), event.getGuild().getId(), userInAServer.getUserReference().getId());
ModMailThread existingThread = modMailThreadManagementService.getOpenModMailThreadForUser(user); ModMailThread existingThread = modMailThreadManagementService.getOpenModMailThreadForUser(userInAServer);
ModMailThreadExistsModel model = ModMailThreadExistsModel ModMailThreadExistsModel model = ModMailThreadExistsModel
.builder() .builder()
.existingModMailThread(existingThread) .existingModMailThread(existingThread)
@@ -114,9 +112,9 @@ public class Contact extends AbstractConditionableCommand {
.thenApply(interactionHook -> CommandResult.fromSuccess()); .thenApply(interactionHook -> CommandResult.fromSuccess());
} else { } else {
CompletableFuture<InteractionHook> response = interactionService.replyEmbed(CONTACT_RESPONSE, event); CompletableFuture<InteractionHook> response = interactionService.replyEmbed(CONTACT_RESPONSE, event);
CompletableFuture<MessageChannel> threadFuture = modMailThreadService.createModMailThreadForUser(member, null, false, new ArrayList<>()); CompletableFuture<MessageChannel> threadFuture = modMailThreadService.createModMailThreadForUser(user, event.getGuild(), null, false, new ArrayList<>(), false);
return CompletableFuture.allOf(response, threadFuture) return CompletableFuture.allOf(response, threadFuture)
.thenCompose(unused -> modMailThreadService.sendContactNotification(member, threadFuture.join(), response.join())) .thenCompose(unused -> modMailThreadService.sendContactNotification(user, threadFuture.join(), response.join()))
.thenApply(o -> CommandResult.fromSuccess()); .thenApply(o -> CommandResult.fromSuccess());
} }
} }
@@ -126,7 +124,7 @@ public class Contact extends AbstractConditionableCommand {
Parameter responseText = Parameter Parameter responseText = Parameter
.builder() .builder()
.name(USER_PARMETER) .name(USER_PARMETER)
.type(Member.class) .type(User.class)
.templated(true) .templated(true)
.build(); .build();
List<Parameter> parameters = Arrays.asList(responseText); List<Parameter> parameters = Arrays.asList(responseText);
@@ -139,11 +137,11 @@ public class Contact extends AbstractConditionableCommand {
.builder() .builder()
.enabled(true) .enabled(true)
.rootCommandName(ModMailSlashCommandNames.MODMAIL) .rootCommandName(ModMailSlashCommandNames.MODMAIL)
.commandName(CONTACT_PARAMETER) .commandName(COMMAND_NAME)
.build(); .build();
return CommandConfiguration.builder() return CommandConfiguration.builder()
.name(CONTACT_PARAMETER) .name(COMMAND_NAME)
.module(ModMailModuleDefinition.MODMAIL) .module(ModMailModuleDefinition.MODMAIL)
.parameters(parameters) .parameters(parameters)
.slashCommandConfig(slashCommandConfig) .slashCommandConfig(slashCommandConfig)

View File

@@ -0,0 +1,121 @@
package dev.sheldan.abstracto.modmail.command;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.condition.CommandCondition;
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.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.modmail.condition.ModMailContextCondition;
import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition;
import dev.sheldan.abstracto.modmail.config.ModMailMode;
import dev.sheldan.abstracto.modmail.config.ModMailSlashCommandNames;
import dev.sheldan.abstracto.modmail.exception.ModMailThreadClosedException;
import dev.sheldan.abstracto.modmail.model.database.ModMailThread;
import dev.sheldan.abstracto.modmail.model.database.ModMailThreadState;
import dev.sheldan.abstracto.modmail.service.ModMailThreadService;
import dev.sheldan.abstracto.modmail.service.management.ModMailThreadManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.InteractionHook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class DenyModmailAppeal extends AbstractConditionableCommand {
private static final String COMMAND_NAME = "denyappeal";
private static final String FULL_COMMAND_NAME = "denyModmailAppeal";
private static final String REASON_PARAMETER = "reason";
private static final String RESPONSE_TEMPLATE = "denyModmailAppeal_response";
@Autowired
private ModMailContextCondition requiresModMailCondition;
@Autowired
private ModMailThreadManagementService modMailThreadManagementService;
@Autowired
private ModMailThreadService modMailThreadService;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
ModMailThread modMailThread = modMailThreadManagementService.getByChannelId(event.getChannel().getIdLong());
if(ModMailThreadState.CLOSED.equals(modMailThread.getState()) || ModMailThreadState.CLOSING.equals(modMailThread.getState())) {
throw new ModMailThreadClosedException();
}
String reason = slashCommandParameterService.getCommandOption(REASON_PARAMETER, event, String.class);
CompletableFuture<InteractionHook> response = interactionService.replyEmbed(RESPONSE_TEMPLATE, event);
CompletableFuture<Void> threadFuture = modMailThreadService.rejectAppeal(modMailThread, reason, event.getMember());
return CompletableFuture.allOf(response, threadFuture)
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public FeatureDefinition getFeature() {
return ModMailFeatureDefinition.MOD_MAIL;
}
@Override
public CommandConfiguration getConfiguration() {
Parameter responseText = Parameter
.builder()
.name(REASON_PARAMETER)
.type(String.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(responseText);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ModMailSlashCommandNames.MODMAIL)
.commandName(COMMAND_NAME)
.build();
return CommandConfiguration.builder()
.name(FULL_COMMAND_NAME)
.module(ModMailModuleDefinition.MODMAIL)
.parameters(parameters)
.slashCommandConfig(slashCommandConfig)
.help(helpInfo)
.slashCommandOnly(true)
.supportsEmbedException(true)
.templated(true)
.build();
}
@Override
public List<CommandCondition> getConditions() {
List<CommandCondition> conditions = super.getConditions();
conditions.add(requiresModMailCondition);
return conditions;
}
@Override
public List<FeatureMode> getFeatureModeLimitations() {
return List.of(ModMailMode.MOD_MAIL_APPEALS);
}
}

View File

@@ -9,6 +9,7 @@ import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult; import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition; import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.UserService;
import dev.sheldan.abstracto.modmail.condition.ModMailContextCondition; import dev.sheldan.abstracto.modmail.condition.ModMailContextCondition;
import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition; import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition;
import dev.sheldan.abstracto.modmail.exception.ModMailThreadClosedException; import dev.sheldan.abstracto.modmail.exception.ModMailThreadClosedException;
@@ -39,7 +40,7 @@ public class Reply extends AbstractConditionableCommand {
private ModMailThreadManagementService modMailThreadManagementService; private ModMailThreadManagementService modMailThreadManagementService;
@Autowired @Autowired
private MemberService memberService; private UserService userService;
@Override @Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) { public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
@@ -50,8 +51,8 @@ public class Reply extends AbstractConditionableCommand {
throw new ModMailThreadClosedException(); throw new ModMailThreadClosedException();
} }
Long threadId = modMailThread.getId(); Long threadId = modMailThread.getId();
return memberService.getMemberInServerAsync(modMailThread.getUser()).thenCompose(member -> return userService.retrieveUserForId(modMailThread.getUser().getUserReference().getId()).thenCompose(user ->
modMailThreadService.loadExecutingMemberAndRelay(threadId, text, commandContext.getMessage(), false, member) modMailThreadService.loadExecutingMemberAndRelay(threadId, text, commandContext.getMessage(), false, user, commandContext.getGuild())
).thenApply(aVoid -> CommandResult.fromSuccess()); ).thenApply(aVoid -> CommandResult.fromSuccess());
} }
@@ -65,7 +66,7 @@ public class Reply extends AbstractConditionableCommand {
.optional(true) .optional(true)
.templated(true) .templated(true)
.build(); .build();
List<Parameter> parameters = Arrays.asList(responseText); List<Parameter> parameters = List.of(responseText);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build(); HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder() return CommandConfiguration.builder()
.name("reply") .name("reply")

View File

@@ -14,6 +14,7 @@ import dev.sheldan.abstracto.modmail.model.dto.ServiceChoicesPayload;
import dev.sheldan.abstracto.modmail.service.ModMailThreadService; import dev.sheldan.abstracto.modmail.service.ModMailThreadService;
import dev.sheldan.abstracto.modmail.service.ModMailThreadServiceBean; import dev.sheldan.abstracto.modmail.service.ModMailThreadServiceBean;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -25,7 +26,7 @@ import java.util.ArrayList;
public class ModMailInitialButtonListener implements ButtonClickedListener { public class ModMailInitialButtonListener implements ButtonClickedListener {
@Autowired @Autowired
private MemberService memberService; private GuildService guildService;
@Autowired @Autowired
private ModMailThreadService modMailThreadService; private ModMailThreadService modMailThreadService;
@@ -51,20 +52,20 @@ public class ModMailInitialButtonListener implements ButtonClickedListener {
Long userId = choices.getUserId(); Long userId = choices.getUserId();
log.debug("Executing action for creationg a modmail thread in server {} for user {}.", chosenServer.getServerId(), userId); log.debug("Executing action for creationg a modmail thread in server {} for user {}.", chosenServer.getServerId(), userId);
ArrayList<UndoActionInstance> undoActions = new ArrayList<>(); ArrayList<UndoActionInstance> undoActions = new ArrayList<>();
memberService.getMemberInServerAsync(chosenServer.getServerId(), userId) Guild guild = guildService.getGuildById(chosenServer.getServerId());
.thenCompose(member -> channelService.retrieveMessageInChannel(model.getEvent().getChannel(), choices.getMessageId()) boolean appeal = chosenServer.getAppealModmail() != null && chosenServer.getAppealModmail();
channelService.retrieveMessageInChannel(model.getEvent().getChannel(), choices.getMessageId())
.thenCompose(originalMessage -> { .thenCompose(originalMessage -> {
try { try {
return modMailThreadService.createModMailThreadForUser(member, originalMessage, true, undoActions); return modMailThreadService.createModMailThreadForUser(model.getEvent().getUser(), guild, originalMessage, true, undoActions, appeal);
} catch (Exception ex) { } catch (Exception ex) {
log.error("Failed to setup thread correctly", ex); log.error("Failed to setup thread correctly", ex);
undoActionService.performActions(undoActions); undoActionService.performActions(undoActions);
return null; return null;
} }
}) })
.thenAccept(unused -> self.cleanup(model))) .thenAccept(unused -> self.cleanup(model)).exceptionally(throwable -> {
.exceptionally(throwable -> { log.warn("Failed to setup modmail thread.");
log.error("Failed to setup thread correctly", throwable);
undoActionService.performActions(undoActions); undoActionService.performActions(undoActions);
return null; return null;
}); });

View File

@@ -7,6 +7,7 @@ import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageDeletedModel; import dev.sheldan.abstracto.core.models.listener.MessageDeletedModel;
import dev.sheldan.abstracto.core.service.MemberService; import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.MessageService; import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.service.UserService;
import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition; import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition;
import dev.sheldan.abstracto.modmail.model.database.ModMailMessage; import dev.sheldan.abstracto.modmail.model.database.ModMailMessage;
import dev.sheldan.abstracto.modmail.model.database.ModMailThread; import dev.sheldan.abstracto.modmail.model.database.ModMailThread;
@@ -33,7 +34,7 @@ public class ModMailMessageDeletedListener implements AsyncMessageDeletedListene
private ModMailMessageDeletedListener self; private ModMailMessageDeletedListener self;
@Autowired @Autowired
private MemberService memberService; private UserService userService;
@Override @Override
public DefaultListenerResult execute(MessageDeletedModel model) { public DefaultListenerResult execute(MessageDeletedModel model) {
@@ -47,8 +48,8 @@ public class ModMailMessageDeletedListener implements AsyncMessageDeletedListene
Long channelId = thread.getChannel().getId(); Long channelId = thread.getChannel().getId();
Long serverId = thread.getServer().getId(); Long serverId = thread.getServer().getId();
log.info("Deleting message for mod mail thread {} in channel {} in server {}.", thread.getId(), channelId, serverId); log.info("Deleting message for mod mail thread {} in channel {} in server {}.", thread.getId(), channelId, serverId);
memberService.getMemberInServerAsync(model.getServerId(), modMailMessage.getThreadReference().getUser().getUserReference().getId()).thenAccept(member -> { userService.retrieveUserForId(modMailMessage.getThreadReference().getUser().getUserReference().getId()).thenAccept(user -> {
CompletableFuture<Void> dmDeletePromise = messageService.deleteMessageInChannelWithUser(member.getUser(), dmMessageId); CompletableFuture<Void> dmDeletePromise = messageService.deleteMessageInChannelWithUser(user, dmMessageId);
CompletableFuture<Void> channelDeletePromise; CompletableFuture<Void> channelDeletePromise;
if(hasMessageInChannel) { if(hasMessageInChannel) {
channelDeletePromise = messageService.deleteMessageInChannelInServer(serverId, channelId, channelMessage); channelDeletePromise = messageService.deleteMessageInChannelInServer(serverId, channelId, channelMessage);

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