mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-01-01 23:35:29 +00:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b973c4660c | ||
|
|
34692d22eb | ||
|
|
33268cded6 | ||
|
|
40d66df9b0 | ||
|
|
92508d7a1d | ||
|
|
ccbf6147e9 | ||
|
|
a3545c4af0 | ||
|
|
fa187f8817 | ||
|
|
c39b7ebeec | ||
|
|
5cbad801ff | ||
|
|
826bee1f81 | ||
|
|
99bf9a9be0 | ||
|
|
5b5e4973a7 | ||
|
|
65e956827c | ||
|
|
b258a8bc54 | ||
|
|
9864b7d875 | ||
|
|
27466b7333 | ||
|
|
bfb8969d1f | ||
|
|
0097ff801a | ||
|
|
b6a188c04d | ||
|
|
2fa1adde02 | ||
|
|
cb8b64cc01 | ||
|
|
3b3dd0dbb7 | ||
|
|
388fead2a6 | ||
|
|
336c3d0bd8 | ||
|
|
4991ad8f1c | ||
|
|
c5136a1808 | ||
|
|
446d882eec | ||
|
|
b3e207a967 | ||
|
|
5c25345cf8 | ||
|
|
a01a5055a0 | ||
|
|
234aae3783 | ||
|
|
ca45137cc6 | ||
|
|
bc3d16b40e | ||
|
|
66e212d30c | ||
|
|
b69811479f | ||
|
|
43c5d041ef | ||
|
|
dfe9792330 | ||
|
|
250df57bd0 | ||
|
|
02b8ed2b5d | ||
|
|
71c1445439 | ||
|
|
d86299cdf6 | ||
|
|
1b86fba3e0 |
2
.env
2
.env
@@ -1,2 +1,2 @@
|
||||
REGISTRY_PREFIX=harbor.sheldan.dev/abstracto/
|
||||
VERSION=1.5.34
|
||||
VERSION=1.5.42
|
||||
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -16,7 +16,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
4
.github/workflows/release_manual.yml
vendored
4
.github/workflows/release_manual.yml
vendored
@@ -5,7 +5,7 @@ permissions:
|
||||
contents: write
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
with:
|
||||
path: .env
|
||||
- name: Build and push Docker containers
|
||||
run: docker-compose build && docker-compose push
|
||||
run: docker compose build && docker compose push
|
||||
env:
|
||||
REGISTRY_PREFIX: ${{ steps.dotenv.outputs.registry_prefix }}
|
||||
VERSION: ${{ steps.dotenv.outputs.version }}
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Sheldan
|
||||
Copyright (c) 2024 Sheldan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -8,7 +8,6 @@ Abstracto represents a framework to be used as a basis for a Discord bot. It use
|
||||
and provides an extensive tool set to create new commands and a wide range of commands out of the box.
|
||||
|
||||
This repository does not provide the full functionality in order to start a discord bot, because it requires a Main class.
|
||||
An example implementation of this bot can be seen [here](https://github.com/Sheldan/Crimson). This repository contains the required configuration in order to run a bot and example customizations.
|
||||
|
||||
|
||||
## Technologies
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>anti-raid</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>anti-raid</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>assignable-roles</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>assignable-roles</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>assignable-roles-int</artifactId>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>custom-command</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>custom-command</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>dynamic-activity</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>dynamic-activity</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>entertainment</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package dev.sheldan.abstracto.entertainment.command;
|
||||
|
||||
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
|
||||
import dev.sheldan.abstracto.core.command.config.CombinedParameterEntry;
|
||||
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
|
||||
import dev.sheldan.abstracto.core.command.config.HelpInfo;
|
||||
import dev.sheldan.abstracto.core.command.config.Parameter;
|
||||
@@ -86,7 +87,7 @@ public class Mock extends AbstractConditionableCommand {
|
||||
public CommandConfiguration getConfiguration() {
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
Map<String, Object> parameterAlternatives = new HashMap<>();
|
||||
parameterAlternatives.put(ADDITIONAL_TYPES_KEY, Arrays.asList(Message.class, String.class));
|
||||
parameterAlternatives.put(ADDITIONAL_TYPES_KEY, Arrays.asList(CombinedParameterEntry.messageParameter(Message.class), CombinedParameterEntry.parameter(String.class)));
|
||||
|
||||
Parameter messageParameter = Parameter
|
||||
.builder()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>entertainment</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>experience-tracking</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import dev.sheldan.abstracto.core.service.management.UserInServerManagementServi
|
||||
import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition;
|
||||
import dev.sheldan.abstracto.experience.config.ExperienceFeatureMode;
|
||||
import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames;
|
||||
import dev.sheldan.abstracto.experience.exception.LevelActionAlreadyExistsException;
|
||||
import dev.sheldan.abstracto.experience.exception.LevelActionNotFoundException;
|
||||
import dev.sheldan.abstracto.experience.listener.LevelActionListener;
|
||||
import dev.sheldan.abstracto.experience.model.LevelActionPayload;
|
||||
@@ -99,6 +100,9 @@ public class AddLevelAction extends AbstractConditionableCommand {
|
||||
return userExperienceManagementService.saveUser(user);
|
||||
});
|
||||
}
|
||||
if(levelActionManagementService.getLevelAction(actionName, level, server, userExperience).isPresent()) {
|
||||
throw new LevelActionAlreadyExistsException();
|
||||
}
|
||||
levelActionManagementService.createLevelAction(level, server, actionName, userExperience, payload);
|
||||
return interactionService.replyEmbed(RESPONSE_TEMPLATE, event)
|
||||
.thenApply(interactionHook -> CommandResult.fromSuccess());
|
||||
|
||||
@@ -7,12 +7,14 @@ import dev.sheldan.abstracto.core.command.config.Parameter;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandContext;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.config.FeatureMode;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.experience.config.ExperienceFeatureDefinition;
|
||||
import dev.sheldan.abstracto.experience.config.ExperienceFeatureMode;
|
||||
import dev.sheldan.abstracto.experience.config.ExperienceSlashCommandNames;
|
||||
import dev.sheldan.abstracto.experience.service.AUserExperienceService;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
@@ -100,4 +102,10 @@ public class ExpLevelUpNotification extends AbstractConditionableCommand {
|
||||
.help(helpInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<FeatureMode> getFeatureModeLimitations() {
|
||||
return Arrays.asList(ExperienceFeatureMode.LEVEL_UP_NOTIFICATION);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public class AddMemberToChannelLevelAction implements LevelActionListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExecute(AUserExperience aUserExperience, LevelAction levelAction) {
|
||||
public boolean shouldExecute(AUserExperience aUserExperience, Integer oldLevel, LevelAction levelAction) {
|
||||
return aUserExperience.getLevelOrDefault() >= levelAction.getLevel().getLevel();
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ public class AddRoleLevelAction implements LevelActionListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExecute(AUserExperience aUserExperience, LevelAction levelAction) {
|
||||
public boolean shouldExecute(AUserExperience aUserExperience, Integer oldLevel, LevelAction levelAction) {
|
||||
return aUserExperience.getLevelOrDefault() >= levelAction.getLevel().getLevel();
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ public class RemoveMemberFromChannelLevelAction implements LevelActionListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExecute(AUserExperience aUserExperience, LevelAction levelAction) {
|
||||
public boolean shouldExecute(AUserExperience aUserExperience, Integer oldLevel, LevelAction levelAction) {
|
||||
return aUserExperience.getLevelOrDefault() >= levelAction.getLevel().getLevel();
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ public class RemoveRoleLevelAction implements LevelActionListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExecute(AUserExperience aUserExperience, LevelAction levelAction) {
|
||||
public boolean shouldExecute(AUserExperience aUserExperience, Integer oldLevel, LevelAction levelAction) {
|
||||
return aUserExperience.getLevelOrDefault() >= levelAction.getLevel().getLevel();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -337,8 +337,8 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
|
||||
.builder()
|
||||
.build();
|
||||
boolean userChangesLevel = !Objects.equals(newLevel.getLevel(), aUserExperience.getCurrentLevel().getLevel());
|
||||
Integer oldLevel = aUserExperience.getCurrentLevel() != null ? aUserExperience.getCurrentLevel().getLevel() : 0;
|
||||
if(userChangesLevel) {
|
||||
Integer oldLevel = aUserExperience.getCurrentLevel() != null ? aUserExperience.getCurrentLevel().getLevel() : 0;
|
||||
log.info("User {} in server {} changed level. New {}, Old {}.", member.getIdLong(),
|
||||
member.getGuild().getIdLong(), newLevel.getLevel(),
|
||||
oldLevel);
|
||||
@@ -373,7 +373,7 @@ public class AUserExperienceServiceBean implements AUserExperienceService {
|
||||
}
|
||||
aUserExperience.setMessageCount(aUserExperience.getMessageCount() + 1L);
|
||||
if(userChangesLevel && featureModeService.featureModeActive(ExperienceFeatureDefinition.EXPERIENCE, server, ExperienceFeatureMode.LEVEL_ACTION)) {
|
||||
levelActionService.applyLevelActionsToUser(aUserExperience)
|
||||
levelActionService.applyLevelActionsToUser(aUserExperience, oldLevel)
|
||||
.thenAccept(unused -> {
|
||||
log.info("Executed level actions for user {}.", userInServerId);
|
||||
})
|
||||
|
||||
@@ -49,6 +49,11 @@ public class LevelActionServiceBean implements LevelActionService {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> applyLevelActionsToUser(AUserExperience user) {
|
||||
return applyLevelActionsToUser(user, user.getLevelOrDefault());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> applyLevelActionsToUser(AUserExperience user, Integer oldLevel) {
|
||||
if(levelActions == null || levelActions.isEmpty()) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
@@ -60,8 +65,16 @@ public class LevelActionServiceBean implements LevelActionService {
|
||||
|
||||
Map<Integer, List<LevelAction>> actionConfigMap = new HashMap<>();
|
||||
|
||||
Map<String, LevelActionListener> actionStringListenerMap = levelActions
|
||||
.stream()
|
||||
.collect(Collectors.toMap(a -> a.getName().toLowerCase(), Function.identity()));
|
||||
|
||||
levelActionsOfUserInServer.forEach(levelAction -> {
|
||||
if(levelAction.getLevel().getLevel() > user.getLevelOrDefault()) {
|
||||
LevelActionListener listener = actionStringListenerMap.get(levelAction.getAction());
|
||||
if(listener == null) { // if for some reason the config is still in the database, but we don't have code for it anymore
|
||||
return;
|
||||
}
|
||||
if(!listener.shouldExecute(user, oldLevel, levelAction)) {
|
||||
return;
|
||||
}
|
||||
if(actionConfigMap.containsKey(levelAction.getLevel().getLevel())) {
|
||||
@@ -73,9 +86,6 @@ public class LevelActionServiceBean implements LevelActionService {
|
||||
}
|
||||
});
|
||||
|
||||
Map<String, LevelActionListener> actionStringListenerMap = levelActions
|
||||
.stream()
|
||||
.collect(Collectors.toMap(a -> a.getName().toLowerCase(), Function.identity()));
|
||||
|
||||
List<Integer> levels = actionConfigMap
|
||||
.keySet()
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>experience-tracking</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package dev.sheldan.abstracto.experience.exception;
|
||||
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||
import dev.sheldan.abstracto.core.templating.Templatable;
|
||||
|
||||
public class LevelActionAlreadyExistsException extends AbstractoRunTimeException implements Templatable {
|
||||
|
||||
public LevelActionAlreadyExistsException() {
|
||||
super("Level action already exists.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTemplateName() {
|
||||
return "level_action_already_exists_exception";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTemplateModel() {
|
||||
return new Object();
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,13 @@ import dev.sheldan.abstracto.experience.model.LevelActionPayload;
|
||||
import dev.sheldan.abstracto.experience.model.database.AUserExperience;
|
||||
import dev.sheldan.abstracto.experience.model.database.LevelAction;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public interface LevelActionListener {
|
||||
String getName();
|
||||
|
||||
void apply(AUserExperience userExperience, LevelAction levelAction, MemberActionModification container);
|
||||
|
||||
boolean shouldExecute(AUserExperience aUserExperience, LevelAction levelAction);
|
||||
boolean shouldExecute(AUserExperience aUserExperience, Integer oldLevel, LevelAction levelAction);
|
||||
|
||||
void prepareAction(LevelAction levelAction);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface LevelActionService {
|
||||
CompletableFuture<Void> applyLevelActionsToUser(AUserExperience user);
|
||||
CompletableFuture<Void> applyLevelActionsToUser(AUserExperience user, Integer oldLevel);
|
||||
List<String> getAvailableLevelActions();
|
||||
Optional<LevelActionListener> getLevelActionListenerForName(String name);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>giveaway</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>giveaway-impl</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>giveaway</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>giveaway-int</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>giveaway</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>image-generation</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>image-generation-impl</artifactId>
|
||||
|
||||
@@ -2,11 +2,13 @@ package dev.sheldan.abstracto.imagegeneration.command;
|
||||
|
||||
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
|
||||
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
|
||||
import dev.sheldan.abstracto.core.command.config.CombinedParameterEntry;
|
||||
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
|
||||
import dev.sheldan.abstracto.core.command.config.HelpInfo;
|
||||
import dev.sheldan.abstracto.core.command.config.Parameter;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandContext;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.command.handler.parameter.CombinedParameter;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
|
||||
@@ -21,16 +23,18 @@ import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationFeatureDefini
|
||||
import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationSlashCommandNames;
|
||||
import dev.sheldan.abstracto.imagegeneration.service.ImageGenerationService;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static dev.sheldan.abstracto.core.command.config.Parameter.ADDITIONAL_TYPES_KEY;
|
||||
|
||||
@Component
|
||||
public class Bonk extends AbstractConditionableCommand {
|
||||
public static final String MEMBER_PARAMETER_KEY = "member";
|
||||
@@ -65,7 +69,11 @@ public class Bonk extends AbstractConditionableCommand {
|
||||
if(parameters.isEmpty()) {
|
||||
member = commandContext.getAuthor();
|
||||
} else {
|
||||
member = (Member) parameters.get(0);
|
||||
if(parameters.get(0) instanceof Message) {
|
||||
member = ((Message) parameters.get(0)).getMember();
|
||||
} else {
|
||||
member = (Member) parameters.get(0);
|
||||
}
|
||||
}
|
||||
File bonkGifFile = imageGenerationService.getBonkGif(member.getEffectiveAvatar().getUrl(imageSize));
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(BONK_EMBED_TEMPLATE_KEY, new Object());
|
||||
@@ -107,10 +115,13 @@ public class Bonk extends AbstractConditionableCommand {
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
Map<String, Object> parameterAlternatives = new HashMap<>();
|
||||
parameterAlternatives.put(ADDITIONAL_TYPES_KEY, Arrays.asList(CombinedParameterEntry.messageParameter(Message.class), CombinedParameterEntry.parameter(Member.class)));
|
||||
Parameter memberParameter = Parameter
|
||||
.builder()
|
||||
.name(MEMBER_PARAMETER_KEY)
|
||||
.type(Member.class)
|
||||
.type(CombinedParameter.class)
|
||||
.additionalInfo(parameterAlternatives)
|
||||
.templated(true)
|
||||
.optional(true)
|
||||
.build();
|
||||
|
||||
@@ -2,11 +2,13 @@ package dev.sheldan.abstracto.imagegeneration.command;
|
||||
|
||||
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
|
||||
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
|
||||
import dev.sheldan.abstracto.core.command.config.CombinedParameterEntry;
|
||||
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
|
||||
import dev.sheldan.abstracto.core.command.config.HelpInfo;
|
||||
import dev.sheldan.abstracto.core.command.config.Parameter;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandContext;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.command.handler.parameter.CombinedParameter;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
|
||||
@@ -21,16 +23,18 @@ import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationFeatureDefini
|
||||
import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationSlashCommandNames;
|
||||
import dev.sheldan.abstracto.imagegeneration.service.ImageGenerationService;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static dev.sheldan.abstracto.core.command.config.Parameter.ADDITIONAL_TYPES_KEY;
|
||||
|
||||
@Component
|
||||
public class Pat extends AbstractConditionableCommand {
|
||||
public static final String MEMBER_PARAMETER_KEY = "member";
|
||||
@@ -65,7 +69,11 @@ public class Pat extends AbstractConditionableCommand {
|
||||
if(parameters.isEmpty()) {
|
||||
member = commandContext.getAuthor();
|
||||
} else {
|
||||
member = (Member) parameters.get(0);
|
||||
if(parameters.get(0) instanceof Message) {
|
||||
member = ((Message) parameters.get(0)).getMember();
|
||||
} else {
|
||||
member = (Member) parameters.get(0);
|
||||
}
|
||||
}
|
||||
File patGifFile = imageGenerationService.getPatGif(member.getEffectiveAvatar().getUrl(imageSize));
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(PAT_EMBED_TEMPLATE_KEY, new Object());
|
||||
@@ -107,10 +115,13 @@ public class Pat extends AbstractConditionableCommand {
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
Map<String, Object> parameterAlternatives = new HashMap<>();
|
||||
parameterAlternatives.put(ADDITIONAL_TYPES_KEY, Arrays.asList(CombinedParameterEntry.messageParameter(Message.class), CombinedParameterEntry.parameter(Member.class)));
|
||||
Parameter memberParameter = Parameter
|
||||
.builder()
|
||||
.name(MEMBER_PARAMETER_KEY)
|
||||
.type(Member.class)
|
||||
.type(CombinedParameter.class)
|
||||
.additionalInfo(parameterAlternatives)
|
||||
.templated(true)
|
||||
.optional(true)
|
||||
.build();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>image-generation</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>image-generation-int</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>image-generation</artifactId>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>invite-filter</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>invite-filter</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<dependency>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>moderation-int</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>link-embed</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>link-embed</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>logging</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -5,13 +5,14 @@ import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
|
||||
import dev.sheldan.abstracto.core.listener.async.jda.AsyncLeaveListener;
|
||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||
import dev.sheldan.abstracto.core.models.listener.MemberLeaveModel;
|
||||
import dev.sheldan.abstracto.core.service.MemberService;
|
||||
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
|
||||
import dev.sheldan.abstracto.core.service.PostTargetService;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.logging.config.LoggingFeatureDefinition;
|
||||
import dev.sheldan.abstracto.logging.config.LoggingPostTarget;
|
||||
import dev.sheldan.abstracto.logging.model.template.MemberLeaveLogModel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -40,10 +41,10 @@ public class LeaveLogger implements AsyncLeaveListener {
|
||||
.userId(listenerModel.getUser().getIdLong())
|
||||
.serverId(listenerModel.getServerId())
|
||||
.build();
|
||||
MemberLeaveModel model = MemberLeaveModel
|
||||
MemberLeaveLogModel model = MemberLeaveLogModel
|
||||
.builder()
|
||||
.leavingUser(leavingUser)
|
||||
.user(listenerModel.getUser())
|
||||
.user(UserDisplay.fromUser(listenerModel.getUser()))
|
||||
.build();
|
||||
log.debug("Logging leave event for user {} in server {}.", listenerModel.getUser().getIdLong(), listenerModel.getServerId());
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_LEAVE_TEMPLATE, model, listenerModel.getServerId());
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>logging</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
package dev.sheldan.abstracto.logging.model.template;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class MemberLeaveLogModel {
|
||||
private Member member;
|
||||
private ServerUser leavingUser;
|
||||
private UserDisplay user;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>moderation</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ public class Infractions extends AbstractConditionableCommand {
|
||||
} else if(slashCommandParameterService.hasCommandOptionWithFullType(USER_PARAMETER, event, OptionType.STRING)){
|
||||
String userIdStr = slashCommandParameterService.getCommandOption(USER_PARAMETER, event, User.class, String.class);
|
||||
Long userId = Long.parseLong(userIdStr);
|
||||
AUserInAServer userInServer = userInServerManagementService.createUserInServer(event.getGuild().getIdLong(), userId);
|
||||
AUserInAServer userInServer = userInServerManagementService.loadOrCreateUser(event.getGuild().getIdLong(), userId);
|
||||
infractions = infractionManagementService.getInfractionsForUser(userInServer);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -73,11 +74,15 @@ public class Mute extends AbstractConditionableCommand {
|
||||
Duration duration = (Duration) parameters.get(1);
|
||||
String defaultReason = templateService.renderSimpleTemplate(MUTE_DEFAULT_REASON_TEMPLATE, guild.getIdLong());
|
||||
String reason = parameters.size() == 3 ? (String) parameters.get(2) : defaultReason;
|
||||
Instant oldTimeoutDate = null;
|
||||
if(member.getTimeOutEnd() != null && member.isTimedOut()) {
|
||||
oldTimeoutDate = member.getTimeOutEnd().toInstant();
|
||||
}
|
||||
ServerUser userToMute = ServerUser.fromMember(member);
|
||||
ServerUser mutingUser = ServerUser.fromMember(commandContext.getAuthor());
|
||||
Long serverId = commandContext.getGuild().getIdLong();
|
||||
ServerChannelMessage serverChannelMessage = ServerChannelMessage.fromMessage(commandContext.getMessage());
|
||||
return muteService.muteMemberWithLog(userToMute, mutingUser, reason, duration, commandContext.getGuild(), serverChannelMessage)
|
||||
return muteService.muteMemberWithLog(userToMute, mutingUser, reason, duration, commandContext.getGuild(), serverChannelMessage, oldTimeoutDate)
|
||||
.thenCompose(muteResult -> {
|
||||
if(muteResult == NOTIFICATION_FAILED) {
|
||||
MessageToSend errorNotification = templateService.renderEmbedTemplate(MUTE_NOTIFICATION_NOT_POSSIBLE_TEMPLATE_KEY, new Object(), serverId);
|
||||
|
||||
@@ -11,7 +11,6 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||
import dev.sheldan.abstracto.core.service.UserService;
|
||||
import dev.sheldan.abstracto.moderation.config.ModerationModuleDefinition;
|
||||
import dev.sheldan.abstracto.moderation.config.ModerationSlashCommandNames;
|
||||
@@ -51,7 +50,7 @@ public class UnBan extends AbstractConditionableCommand {
|
||||
String userIdStr = (String) parameters.get(0);
|
||||
Long userId = Long.parseLong(userIdStr);
|
||||
return userService.retrieveUserForId(userId)
|
||||
.thenCompose(user -> banService.unBanUserWithNotification(userId, ServerUser.fromMember(commandContext.getAuthor()), commandContext.getGuild()))
|
||||
.thenCompose(user -> banService.unbanUser(commandContext.getGuild(), user, commandContext.getAuthor()))
|
||||
.thenApply(aVoid -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@@ -60,7 +59,7 @@ public class UnBan extends AbstractConditionableCommand {
|
||||
String userIdStr = slashCommandParameterService.getCommandOption(USER_PARAMETER, event, String.class);
|
||||
Long userId = Long.parseLong(userIdStr);
|
||||
return userService.retrieveUserForId(userId)
|
||||
.thenCompose(user -> banService.unBanUserWithNotification(userId, ServerUser.fromMember(event.getMember()), event.getGuild()))
|
||||
.thenCompose(user -> banService.unbanUser(event.getGuild(), user, event.getMember()))
|
||||
.thenCompose(unused -> interactionService.replyEmbed(UN_BAN_RESPONSE, event))
|
||||
.thenApply(interactionHook -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.exception.EntityGuildMismatchException;
|
||||
import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.moderation.config.ModerationModuleDefinition;
|
||||
import dev.sheldan.abstracto.moderation.config.ModerationSlashCommandNames;
|
||||
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
|
||||
@@ -42,9 +41,6 @@ public class UnMute extends AbstractConditionableCommand {
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
List<Object> parameters = commandContext.getParameters().getParameters();
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package dev.sheldan.abstracto.moderation.listener;
|
||||
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
|
||||
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMemberKickedListener;
|
||||
import dev.sheldan.abstracto.core.models.listener.MemberKickedModel;
|
||||
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
|
||||
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
|
||||
import dev.sheldan.abstracto.moderation.model.template.command.KickLogModel;
|
||||
import dev.sheldan.abstracto.moderation.service.KickService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class MemberKickedListener implements AsyncMemberKickedListener {
|
||||
|
||||
@Autowired
|
||||
private KickService kickService;
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return ModerationFeatureDefinition.MODERATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultListenerResult execute(MemberKickedModel eventModel) {
|
||||
log.info("Notifying about kicked of user {} in guild {}.", eventModel.getKickedServerUser().getUserId(), eventModel.getServerId());
|
||||
if(eventModel.getKickingServerUser().getUserId() == eventModel.getGuild().getJDA().getSelfUser().getIdLong()) {
|
||||
log.info("Skipping logging kicked event about user {} in guild {}, because it was done by us.", eventModel.getKickedServerUser().getUserId(), eventModel.getGuild().getIdLong());
|
||||
return DefaultListenerResult.IGNORED;
|
||||
}
|
||||
KickLogModel model = KickLogModel
|
||||
.builder()
|
||||
.kickedUser(eventModel.getKickedUser() != null ? UserDisplay.fromUser(eventModel.getKickedUser()) : UserDisplay.fromId(eventModel.getKickedServerUser().getUserId()))
|
||||
.kickingUser(eventModel.getKickingUser() != null ? UserDisplay.fromUser(eventModel.getKickingUser()) : UserDisplay.fromServerUser(eventModel.getKickingServerUser()))
|
||||
.reason(eventModel.getReason())
|
||||
.build();
|
||||
kickService.sendKicklog(model, eventModel.getServerId());
|
||||
return DefaultListenerResult.PROCESSED;
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
package dev.sheldan.abstracto.moderation.listener;
|
||||
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
|
||||
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMemberTimeoutUpdatedListener;
|
||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||
import dev.sheldan.abstracto.core.models.listener.MemberTimeoutUpdatedModel;
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
|
||||
import dev.sheldan.abstracto.core.service.MemberService;
|
||||
import dev.sheldan.abstracto.core.service.PostTargetService;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
|
||||
import dev.sheldan.abstracto.moderation.config.posttarget.MutingPostTarget;
|
||||
import dev.sheldan.abstracto.moderation.model.template.command.MuteListenerModel;
|
||||
import dev.sheldan.abstracto.moderation.service.MuteServiceBean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.audit.ActionType;
|
||||
import net.dv8tion.jda.api.audit.AuditLogEntry;
|
||||
import net.dv8tion.jda.api.audit.AuditLogKey;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class MemberTimeoutListener implements AsyncMemberTimeoutUpdatedListener {
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private PostTargetService postTargetService;
|
||||
|
||||
@Autowired
|
||||
private MemberTimeoutListener self;
|
||||
|
||||
@Autowired
|
||||
private MemberService memberService;
|
||||
|
||||
@Override
|
||||
public DefaultListenerResult execute(MemberTimeoutUpdatedModel model) {
|
||||
Guild guild = model.getGuild();
|
||||
guild.retrieveAuditLogs()
|
||||
.type(ActionType.MEMBER_UPDATE)
|
||||
.limit(10)
|
||||
.queue(auditLogEntries -> {
|
||||
CompletableFuture<Void> notificationFuture = null;
|
||||
if(auditLogEntries.isEmpty()) {
|
||||
log.info("Did not find recent timeouts in guild {}.", model.getServerId());
|
||||
notificationFuture = self.sendMutingUpdateNotification(model, null);
|
||||
} else {
|
||||
Optional<AuditLogEntry> timeoutEntryOptional = auditLogEntries
|
||||
.stream()
|
||||
.filter(auditLogEntry -> auditLogEntry.getChangeByKey(AuditLogKey.MEMBER_TIME_OUT) != null
|
||||
&& auditLogEntry.getTargetIdLong() == model.getTimeoutUser().getUserId())
|
||||
.findFirst();
|
||||
if(timeoutEntryOptional.isPresent()) {
|
||||
AuditLogEntry auditLogEntry = timeoutEntryOptional.get();
|
||||
User responsibleUser = auditLogEntry.getUser();
|
||||
if(guild.getSelfMember().getIdLong() != responsibleUser.getIdLong()) {
|
||||
notificationFuture = self.sendMutingUpdateNotification(model, auditLogEntry);
|
||||
}
|
||||
} else {
|
||||
notificationFuture = self.sendMutingUpdateNotification(model, null);
|
||||
}
|
||||
}
|
||||
if(notificationFuture != null) {
|
||||
notificationFuture.thenAccept(unused -> {
|
||||
log.info("Sent notification about timeout change {} -> {} of user {} in server {}.",
|
||||
model.getOldTimeout(), model.getNewTimeout(), model.getTimeoutUser().getUserId(), model.getServerId());
|
||||
}).exceptionally(throwable -> {
|
||||
log.info("Sent notification about timeout change {} -> {} of user {} in server {}.",
|
||||
model.getOldTimeout(), model.getNewTimeout(), model.getTimeoutUser().getUserId(), model.getServerId());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
});
|
||||
return DefaultListenerResult.PROCESSED;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CompletableFuture<Void> sendMutingUpdateNotification(MemberTimeoutUpdatedModel model, AuditLogEntry logEntry) {
|
||||
User responsibleUser;
|
||||
Guild guild = model.getGuild();
|
||||
CompletableFuture<Member> future;
|
||||
String reason;
|
||||
if(logEntry != null) {
|
||||
responsibleUser = logEntry.getUser();
|
||||
if(responsibleUser != null) {
|
||||
ServerUser responsibleServerUser = ServerUser
|
||||
.builder()
|
||||
.serverId(guild.getIdLong())
|
||||
.isBot(responsibleUser.isBot())
|
||||
.userId(responsibleUser.getIdLong())
|
||||
.build();
|
||||
future = memberService.retrieveMemberInServer(responsibleServerUser);
|
||||
} else {
|
||||
future = CompletableFuture.completedFuture(null);
|
||||
}
|
||||
reason = logEntry.getReason();
|
||||
} else {
|
||||
future = CompletableFuture.completedFuture(null);
|
||||
reason = null;
|
||||
}
|
||||
CompletableFuture<Void> returningFuture = new CompletableFuture<>();
|
||||
future.whenComplete((aVoid, throwable) -> {
|
||||
try {
|
||||
MuteListenerModel muteLogModel = MuteListenerModel
|
||||
.builder()
|
||||
.muteTargetDate(model.getNewTimeout() != null ? model.getNewTimeout().toInstant() : null)
|
||||
.oldMuteTargetDate(model.getOldTimeout() != null ? model.getOldTimeout().toInstant() : null)
|
||||
.mutingUser(future.isCompletedExceptionally() ? null : MemberDisplay.fromMember(future.join()))
|
||||
.mutedUser(MemberDisplay.fromMember(model.getMember()))
|
||||
.reason(reason)
|
||||
.build();
|
||||
MessageToSend message = templateService.renderEmbedTemplate(MuteServiceBean.MUTE_LOG_TEMPLATE, muteLogModel, guild.getIdLong());
|
||||
FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, model.getServerId()));
|
||||
returningFuture.complete(null);
|
||||
} catch (Exception exception) {
|
||||
log.error("Failed to log timeout update event for user {} in guild {}.", model.getTimeoutUser().getUserId(), model.getServerId(), exception);
|
||||
}
|
||||
});
|
||||
|
||||
return returningFuture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return ModerationFeatureDefinition.MUTING;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package dev.sheldan.abstracto.moderation.listener;
|
||||
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
|
||||
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMemberTimeoutUpdatedListener;
|
||||
import dev.sheldan.abstracto.core.models.listener.MemberTimeoutUpdatedModel;
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
|
||||
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
|
||||
import dev.sheldan.abstracto.moderation.model.template.command.MuteLogModel;
|
||||
import dev.sheldan.abstracto.moderation.service.MuteService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class MemberTimeoutLoggerListener implements AsyncMemberTimeoutUpdatedListener {
|
||||
|
||||
@Autowired
|
||||
private MuteService muteService;
|
||||
|
||||
@Override
|
||||
public DefaultListenerResult execute(MemberTimeoutUpdatedModel model) {
|
||||
log.info("Notifying about timeout of user {} in guild {}.", model.getMutedUser().getUserId(), model.getServerId());
|
||||
if(model.getMutingUser().getUserId() == model.getGuild().getSelfMember().getIdLong()) {
|
||||
log.info("Skipping logging timeout event about user {} in guild {}, because it was done by us.", model.getMutedUser().getUserId(), model.getGuild().getIdLong());
|
||||
return DefaultListenerResult.IGNORED;
|
||||
}
|
||||
MemberDisplay mutedMemberDisplay = model.getMutedMember() != null ? MemberDisplay.fromMember(model.getMutedMember()) : MemberDisplay.fromServerUser(model.getMutedUser());
|
||||
MemberDisplay mutingMemberDisplay = model.getMutingMember() != null ? MemberDisplay.fromMember(model.getMutingMember()) : MemberDisplay.fromServerUser(model.getMutingUser());
|
||||
Duration duration = null;
|
||||
if(model.getNewTimeout() != null) {
|
||||
duration = Duration.between(Instant.now(), model.getNewTimeout());
|
||||
}
|
||||
MuteLogModel muteLogModel = MuteLogModel
|
||||
.builder()
|
||||
.muteTargetDate(model.getNewTimeout() != null ? model.getNewTimeout().toInstant() : null)
|
||||
.oldMuteTargetDate(model.getOldTimeout() != null ? model.getOldTimeout().toInstant() : null)
|
||||
.mutingMember(mutingMemberDisplay)
|
||||
.mutedMember(mutedMemberDisplay)
|
||||
.duration(duration)
|
||||
.reason(model.getReason())
|
||||
.build();
|
||||
muteService.sendMuteLogMessage(muteLogModel, model.getServerId());
|
||||
return DefaultListenerResult.PROCESSED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureDefinition getFeature() {
|
||||
return ModerationFeatureDefinition.MUTING;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import dev.sheldan.abstracto.core.interaction.modal.listener.ModalInteractionLis
|
||||
import dev.sheldan.abstracto.core.interaction.modal.listener.ModalInteractionListenerResult;
|
||||
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
|
||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.core.utils.ParseUtils;
|
||||
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
|
||||
@@ -38,9 +37,6 @@ public class MuteModerationActionModalListener implements ModalInteractionListen
|
||||
@Autowired
|
||||
private InteractionService interactionService;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private InteractionExceptionService interactionExceptionService;
|
||||
|
||||
|
||||
@@ -3,82 +3,37 @@ package dev.sheldan.abstracto.moderation.listener;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
|
||||
import dev.sheldan.abstracto.core.listener.async.jda.AsyncUserBannedListener;
|
||||
import dev.sheldan.abstracto.core.models.listener.UserBannedModel;
|
||||
import dev.sheldan.abstracto.core.service.PostTargetService;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.core.models.listener.UserBannedListenerModel;
|
||||
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
|
||||
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
|
||||
import dev.sheldan.abstracto.moderation.config.posttarget.ModerationPostTarget;
|
||||
import dev.sheldan.abstracto.moderation.model.template.listener.UserBannedListenerModel;
|
||||
import dev.sheldan.abstracto.moderation.model.template.listener.UserBannedLogModel;
|
||||
import dev.sheldan.abstracto.moderation.service.BanService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.audit.ActionType;
|
||||
import net.dv8tion.jda.api.audit.AuditLogEntry;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class UserBannedListener implements AsyncUserBannedListener {
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private PostTargetService postTargetService;
|
||||
|
||||
@Autowired
|
||||
private UserBannedListener self;
|
||||
|
||||
private static final String USER_BANNED_NOTIFICATION_TEMPLATE = "userBanned_listener_notification";
|
||||
private BanService banService;
|
||||
|
||||
@Override
|
||||
public DefaultListenerResult execute(UserBannedModel model) {
|
||||
model.getGuild()
|
||||
.retrieveAuditLogs()
|
||||
.type(ActionType.BAN)
|
||||
.limit(5)
|
||||
.queue(auditLogEntries -> {
|
||||
if(auditLogEntries.isEmpty()) {
|
||||
log.info("Did not find recent bans in guild {}.", model.getServerId());
|
||||
self.sendBannedNotification(model.getUser(), null, null, model.getServerId());
|
||||
return;
|
||||
}
|
||||
Optional<AuditLogEntry> banEntryOptional = auditLogEntries
|
||||
.stream()
|
||||
.filter(auditLogEntry -> auditLogEntry.getTargetIdLong() == model.getBannedUser().getUserId())
|
||||
.findFirst();
|
||||
if(banEntryOptional.isPresent()) {
|
||||
AuditLogEntry auditLogEntry = banEntryOptional.get();
|
||||
if(!model.getGuild().getJDA().getSelfUser().equals(auditLogEntry.getUser())) {
|
||||
self.sendBannedNotification(model.getUser(), auditLogEntry.getUser(), auditLogEntry.getReason(), model.getServerId());
|
||||
}
|
||||
} else {
|
||||
log.info("Did not find the banned user in the most recent bans for guild {}. Not adding audit log information.", model.getServerId());
|
||||
self.sendBannedNotification(model.getUser(), null, null, model.getServerId());
|
||||
}
|
||||
}, throwable -> {
|
||||
log.error("Retrieving bans for guild {} failed - logging ban regardless.", model.getServerId());
|
||||
self.sendBannedNotification(model.getUser(), null, null, model.getServerId());
|
||||
});
|
||||
return DefaultListenerResult.PROCESSED;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CompletableFuture<Void> sendBannedNotification(User bannedUser, User banningUser, String reason, Long serverId) {
|
||||
UserBannedListenerModel model = UserBannedListenerModel
|
||||
public DefaultListenerResult execute(UserBannedListenerModel eventModel) {
|
||||
log.info("Notifying about ban of user {} in guild {}.", eventModel.getBannedServerUser().getUserId(), eventModel.getServerId());
|
||||
if(eventModel.getBanningServerUser().getUserId() == eventModel.getGuild().getJDA().getSelfUser().getIdLong()) {
|
||||
log.info("Skipping logging banned event about user {} in guild {}, because it was done by us.", eventModel.getBannedServerUser().getUserId(), eventModel.getGuild().getIdLong());
|
||||
return DefaultListenerResult.IGNORED;
|
||||
}
|
||||
UserBannedLogModel model = UserBannedLogModel
|
||||
.builder()
|
||||
.bannedUser(bannedUser)
|
||||
.banningUser(banningUser)
|
||||
.reason(reason)
|
||||
.bannedUser(eventModel.getBannedUser() != null ? UserDisplay.fromUser(eventModel.getBannedUser()) : UserDisplay.fromId(eventModel.getBannedServerUser().getUserId()))
|
||||
.banningUser(eventModel.getBanningUser() != null ? UserDisplay.fromUser(eventModel.getBanningUser()) : UserDisplay.fromServerUser(eventModel.getBanningServerUser()))
|
||||
.reason(eventModel.getReason())
|
||||
.build();
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_BANNED_NOTIFICATION_TEMPLATE, model, serverId);
|
||||
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, ModerationPostTarget.BAN_LOG, serverId));
|
||||
banService.sendBanLogMessage(model, eventModel.getServerId());
|
||||
return DefaultListenerResult.PROCESSED;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,88 +3,37 @@ package dev.sheldan.abstracto.moderation.listener;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
|
||||
import dev.sheldan.abstracto.core.listener.async.jda.AsyncUserUnBannedListener;
|
||||
import dev.sheldan.abstracto.core.models.listener.UserUnBannedModel;
|
||||
import dev.sheldan.abstracto.core.service.FeatureModeService;
|
||||
import dev.sheldan.abstracto.core.service.PostTargetService;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.core.models.listener.UserUnBannedListenerModel;
|
||||
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
|
||||
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
|
||||
import dev.sheldan.abstracto.moderation.config.posttarget.ModerationPostTarget;
|
||||
import dev.sheldan.abstracto.moderation.model.template.listener.UserUnBannedListenerModel;
|
||||
import dev.sheldan.abstracto.moderation.model.template.listener.UserUnBannedLogModel;
|
||||
import dev.sheldan.abstracto.moderation.service.BanService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.audit.ActionType;
|
||||
import net.dv8tion.jda.api.audit.AuditLogEntry;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class UserUnBannedListener implements AsyncUserUnBannedListener {
|
||||
@Autowired
|
||||
private FeatureModeService featureModeService;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private PostTargetService postTargetService;
|
||||
|
||||
@Autowired
|
||||
private UserUnBannedListener self;
|
||||
|
||||
private static final String USER_UN_BANNED_NOTIFICATION_TEMPLATE = "userUnBanned_listener_notification";
|
||||
private BanService banService;
|
||||
|
||||
@Override
|
||||
public DefaultListenerResult execute(UserUnBannedModel model) {
|
||||
log.info("Notifying about unban of user {} in guild {}.", model.getUnbannedUser().getUserId(), model.getServerId());
|
||||
model.getGuild()
|
||||
.retrieveAuditLogs()
|
||||
.type(ActionType.UNBAN)
|
||||
.limit(5)
|
||||
.queue(auditLogEntries -> {
|
||||
try {
|
||||
if(auditLogEntries.isEmpty()) {
|
||||
log.info("Did not find recent bans in guild {}.", model.getServerId());
|
||||
return;
|
||||
}
|
||||
Optional<AuditLogEntry> banEntryOptional = auditLogEntries
|
||||
.stream()
|
||||
.filter(auditLogEntry -> auditLogEntry.getTargetIdLong() == model.getUnbannedUser().getUserId())
|
||||
.findFirst();
|
||||
if(banEntryOptional.isPresent()) {
|
||||
AuditLogEntry auditLogEntry = banEntryOptional.get();
|
||||
if(!model.getGuild().getJDA().getSelfUser().equals(auditLogEntry.getUser())) {
|
||||
self.sendUnBannedNotification(model.getUser(), auditLogEntry.getUser(), model.getServerId());
|
||||
}
|
||||
} else {
|
||||
log.info("Did not find the un-banned user in the most recent un-bans for guild {}. Not adding audit log information.", model.getServerId());
|
||||
self.sendUnBannedNotification(model.getUser(), null, model.getServerId());
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
log.error("Failed to properly send un ban log with error.", exception);
|
||||
}
|
||||
}, throwable -> {
|
||||
log.error("Failed to retrieve audit log entries for unban log.", throwable);
|
||||
self.sendUnBannedNotification(model.getUser(), null, model.getServerId());
|
||||
});
|
||||
return DefaultListenerResult.PROCESSED;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CompletableFuture<Void> sendUnBannedNotification(User unbannedUser, User unbanningUser, Long serverId) {
|
||||
UserUnBannedListenerModel model = UserUnBannedListenerModel
|
||||
public DefaultListenerResult execute(UserUnBannedListenerModel eventModel) {
|
||||
log.info("Notifying about unban of user {} in guild {}.", eventModel.getUnBannedServerUser().getUserId(), eventModel.getServerId());
|
||||
if(eventModel.getUnBanningUser().getIdLong() == eventModel.getGuild().getSelfMember().getIdLong()) {
|
||||
log.info("Skipping logging banned event about user {} in guild {}, because it was done by us.", eventModel.getUnBannedServerUser().getUserId(), eventModel.getGuild().getIdLong());
|
||||
return DefaultListenerResult.IGNORED;
|
||||
}
|
||||
UserUnBannedLogModel model = UserUnBannedLogModel
|
||||
.builder()
|
||||
.unBannedUser(unbannedUser)
|
||||
.unBanningUser(unbanningUser)
|
||||
.unBannedUser(eventModel.getUnBannedUser() != null ? UserDisplay.fromUser(eventModel.getUnBannedUser()) : UserDisplay.fromId(eventModel.getUnBannedServerUser().getUserId()))
|
||||
.unBanningUser(eventModel.getUnBanningUser() != null ? UserDisplay.fromUser(eventModel.getUnBanningUser()) : UserDisplay.fromServerUser(eventModel.getUnBanningServerUser()))
|
||||
.reason(eventModel.getReason())
|
||||
.build();
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_UN_BANNED_NOTIFICATION_TEMPLATE, model, serverId);
|
||||
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, ModerationPostTarget.BAN_LOG, serverId));
|
||||
banService.sendUnBanLogMessage(model, eventModel.getServerId());
|
||||
return DefaultListenerResult.PROCESSED;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,34 +3,31 @@ package dev.sheldan.abstracto.moderation.listener.infraction;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.config.ListenerPriority;
|
||||
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
|
||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
|
||||
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.MemberService;
|
||||
import dev.sheldan.abstracto.core.service.MessageService;
|
||||
import dev.sheldan.abstracto.core.service.UserService;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
|
||||
import dev.sheldan.abstracto.moderation.listener.InfractionUpdatedDescriptionListener;
|
||||
import dev.sheldan.abstracto.moderation.model.database.Infraction;
|
||||
import dev.sheldan.abstracto.moderation.model.database.InfractionParameter;
|
||||
import dev.sheldan.abstracto.moderation.model.listener.InfractionDescriptionEventModel;
|
||||
import dev.sheldan.abstracto.moderation.model.template.command.BanLog;
|
||||
import dev.sheldan.abstracto.moderation.model.template.listener.UserBannedLogModel;
|
||||
import dev.sheldan.abstracto.moderation.service.BanService;
|
||||
import dev.sheldan.abstracto.moderation.service.BanServiceBean;
|
||||
import dev.sheldan.abstracto.moderation.service.management.InfractionManagementService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class BanReasonUpdatedListener implements InfractionUpdatedDescriptionListener {
|
||||
@@ -45,7 +42,7 @@ public class BanReasonUpdatedListener implements InfractionUpdatedDescriptionLis
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private BanServiceBean banServiceBean;
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private BanReasonUpdatedListener self;
|
||||
@@ -60,7 +57,7 @@ public class BanReasonUpdatedListener implements InfractionUpdatedDescriptionLis
|
||||
public CompletableFuture<DefaultListenerResult> execute(InfractionDescriptionEventModel model) {
|
||||
Infraction infraction = infractionManagementService.loadInfraction(model.getInfractionId());
|
||||
CompletableFuture<User> infractionUser = userService.retrieveUserForId(infraction.getUser().getUserReference().getId());
|
||||
CompletableFuture<Member> creatorUser = memberService.retrieveMemberInServer(ServerUser.fromAUserInAServer(infraction.getInfractionCreator()));
|
||||
CompletableFuture<User> creatorUser = userService.retrieveUserForId(infraction.getInfractionCreator().getUserReference().getId());
|
||||
CompletableFuture<DefaultListenerResult> returningFuture = new CompletableFuture<>();
|
||||
Long infractionId = infraction.getId();
|
||||
CompletableFuture.allOf(infractionUser, creatorUser)
|
||||
@@ -74,26 +71,17 @@ public class BanReasonUpdatedListener implements InfractionUpdatedDescriptionLis
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void handleBanUpdate(InfractionDescriptionEventModel model, CompletableFuture<User> infractionUser, CompletableFuture<Member> infractionCreator, CompletableFuture<DefaultListenerResult> returningFuture) {
|
||||
public void handleBanUpdate(InfractionDescriptionEventModel model, CompletableFuture<User> infractionUser, CompletableFuture<User> infractionCreator, CompletableFuture<DefaultListenerResult> returningFuture) {
|
||||
Infraction infraction = infractionManagementService.loadInfraction(model.getInfractionId());
|
||||
GuildMessageChannel messageChannel = channelService.getMessageChannelFromServer(model.getServerId(), infraction.getLogChannel().getId());
|
||||
Duration deletionDuration = infraction
|
||||
.getParameters()
|
||||
.stream()
|
||||
.filter(infractionParameter -> infractionParameter.getInfractionParameterId().getName().equals(BanService.INFRACTION_PARAMETER_DELETION_DURATION_KEY))
|
||||
.findAny()
|
||||
.map(InfractionParameter::getValue)
|
||||
.map(Duration::parse)
|
||||
.orElse(Duration.ZERO);
|
||||
BanLog banLog = BanLog
|
||||
UserBannedLogModel banLog = UserBannedLogModel
|
||||
.builder()
|
||||
.bannedUser(infractionUser.isCompletedExceptionally() ? null : UserDisplay.fromUser(infractionUser.join()))
|
||||
.banningMember(infractionCreator.isCompletedExceptionally() ? null : MemberDisplay.fromMember(infractionCreator.join()))
|
||||
.deletionDuration(deletionDuration)
|
||||
.banningUser(infractionCreator.isCompletedExceptionally() ? null : UserDisplay.fromUser(infractionCreator.join()))
|
||||
.reason(model.getNewDescription())
|
||||
.build();
|
||||
|
||||
MessageToSend message = banServiceBean.renderBanMessage(banLog, model.getServerId());
|
||||
MessageToSend message = templateService.renderEmbedTemplate(BanServiceBean.USER_BANNED_NOTIFICATION_TEMPLATE, banLog, model.getServerId());
|
||||
messageService.editMessageInChannel(messageChannel, message, infraction.getLogMessageId())
|
||||
.thenAccept(unused1 -> returningFuture.complete(DefaultListenerResult.PROCESSED))
|
||||
.exceptionally(throwable1 -> {
|
||||
|
||||
@@ -2,48 +2,47 @@ package dev.sheldan.abstracto.moderation.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
|
||||
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
|
||||
import dev.sheldan.abstracto.core.service.*;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.core.utils.CompletableFutureMap;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureConfig;
|
||||
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
|
||||
import dev.sheldan.abstracto.moderation.config.posttarget.ModerationPostTarget;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.moderation.model.BanResult;
|
||||
import dev.sheldan.abstracto.moderation.model.database.Infraction;
|
||||
import dev.sheldan.abstracto.moderation.model.template.command.BanLog;
|
||||
import dev.sheldan.abstracto.moderation.model.template.command.BanNotificationModel;
|
||||
import dev.sheldan.abstracto.moderation.model.template.command.UnBanLog;
|
||||
import dev.sheldan.abstracto.moderation.model.template.listener.UserBannedLogModel;
|
||||
import dev.sheldan.abstracto.moderation.model.template.listener.UserUnBannedLogModel;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.*;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.UserSnowflake;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class BanServiceBean implements BanService {
|
||||
|
||||
public static final String BAN_LOG_TEMPLATE = "ban_log";
|
||||
public static final String UN_BAN_LOG_TEMPLATE = "unBan_log";
|
||||
public static final String BAN_NOTIFICATION = "ban_notification";
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private PostTargetService postTargetService;
|
||||
|
||||
@Autowired
|
||||
private BanServiceBean self;
|
||||
|
||||
@@ -62,15 +61,17 @@ public class BanServiceBean implements BanService {
|
||||
@Autowired
|
||||
private InfractionService infractionService;
|
||||
|
||||
@Autowired
|
||||
private PostTargetService postTargetService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
public static final String USER_BANNED_NOTIFICATION_TEMPLATE = "userBanned_listener_notification";
|
||||
private static final String USER_UN_BANNED_NOTIFICATION_TEMPLATE = "userUnBanned_listener_notification";
|
||||
|
||||
@Override
|
||||
public CompletableFuture<BanResult> banUserWithNotification(ServerUser userToBeBanned, String reason, ServerUser banningUser, Guild guild, Duration deletionDuration) {
|
||||
BanLog banLog = BanLog
|
||||
.builder()
|
||||
.bannedUser(UserDisplay.fromServerUser(userToBeBanned))
|
||||
.banningMember(MemberDisplay.fromServerUser(banningUser))
|
||||
.deletionDuration(deletionDuration)
|
||||
.reason(reason)
|
||||
.build();
|
||||
BanResult[] result = {BanResult.SUCCESSFUL};
|
||||
return sendBanNotification(userToBeBanned, reason, guild)
|
||||
.exceptionally(throwable -> {
|
||||
@@ -78,13 +79,13 @@ public class BanServiceBean implements BanService {
|
||||
return null;
|
||||
})
|
||||
.thenCompose(unused -> banUser(guild, userToBeBanned, deletionDuration, reason))
|
||||
.thenCompose(unused -> sendBanLogMessage(banLog, guild.getIdLong()))
|
||||
.thenAccept(banLogMessage -> self.evaluateAndStoreInfraction(userToBeBanned, guild, reason, banningUser, banLogMessage, deletionDuration))
|
||||
.thenCompose(unused -> self.composeAndSendBanLogMessage(userToBeBanned, banningUser, reason))
|
||||
.thenAccept(banLogMessage -> self.evaluateAndStoreInfraction(userToBeBanned, guild, reason, banningUser, deletionDuration))
|
||||
.thenApply(unused -> result[0]);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CompletableFuture<Long> evaluateAndStoreInfraction(ServerUser user, Guild guild, String reason, ServerUser banningMember, Message banLogMessage, Duration deletionDuration) {
|
||||
public CompletableFuture<Long> evaluateAndStoreInfraction(ServerUser user, Guild guild, String reason, ServerUser banningMember, Duration deletionDuration) {
|
||||
if(featureFlagService.getFeatureFlagValue(ModerationFeatureDefinition.INFRACTIONS, guild.getIdLong())) {
|
||||
Long infractionPoints = configService.getLongValueOrConfigDefault(ModerationFeatureConfig.BAN_INFRACTION_POINTS, guild.getIdLong());
|
||||
AUserInAServer bannedUser = userInServerManagementService.loadOrCreateUser(guild.getIdLong(), user.getUserId());
|
||||
@@ -94,7 +95,7 @@ public class BanServiceBean implements BanService {
|
||||
deletionDuration = Duration.ZERO;
|
||||
}
|
||||
parameters.put(INFRACTION_PARAMETER_DELETION_DURATION_KEY, deletionDuration.toString());
|
||||
return infractionService.createInfractionWithNotification(bannedUser, infractionPoints, BAN_INFRACTION_TYPE, reason, banningUser, parameters, banLogMessage)
|
||||
return infractionService.createInfractionWithNotification(bannedUser, infractionPoints, BAN_INFRACTION_TYPE, reason, banningUser, parameters)
|
||||
.thenApply(Infraction::getId);
|
||||
} else {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
@@ -111,17 +112,6 @@ public class BanServiceBean implements BanService {
|
||||
return messageService.sendMessageToUser(serverUser, message).thenAccept(message1 -> {});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> unBanUserWithNotification(Long userId, ServerUser unBanningMember, Guild guild) {
|
||||
UnBanLog banLog = UnBanLog
|
||||
.builder()
|
||||
.bannedUser(UserDisplay.fromId(userId))
|
||||
.unBanningMember(MemberDisplay.fromServerUser(unBanningMember))
|
||||
.build();
|
||||
return unbanUser(guild, userId)
|
||||
.thenCompose(unused -> self.sendUnBanLogMessage(banLog, guild.getIdLong(), UN_BAN_LOG_TEMPLATE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> banUser(Guild guild, ServerUser userToBeBanned, Duration deletionDuration, String reason) {
|
||||
log.info("Banning user {} in guild {}.", userToBeBanned.getUserId(), guild.getId());
|
||||
@@ -132,31 +122,60 @@ public class BanServiceBean implements BanService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> unbanUser(Guild guild, Long userId) {
|
||||
log.info("Unbanning user {} in guild {}.", userId, guild.getId());
|
||||
return guild.unban(UserSnowflake.fromId(userId)).submit();
|
||||
public CompletableFuture<Void> unbanUser(Guild guild, User user, Member memberPerforming) {
|
||||
log.info("Unbanning user {} in guild {}.", user.getIdLong(), guild.getId());
|
||||
return guild.unban(user).submit().thenCompose(unused -> self.composeAndSendUnBanLogMessage(guild, user, memberPerforming));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CompletionStage<Void> composeAndSendUnBanLogMessage(Guild guild, User user, Member memberPerforming) {
|
||||
UserUnBannedLogModel model = UserUnBannedLogModel
|
||||
.builder()
|
||||
.unBannedUser(UserDisplay.fromUser(user))
|
||||
.unBanningUser(UserDisplay.fromUser(memberPerforming.getUser()))
|
||||
.reason(null)
|
||||
.build();
|
||||
return sendUnBanLogMessage(model, guild.getIdLong());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CompletableFuture<Void> composeAndSendBanLogMessage(ServerUser serverUserToBeBanned, ServerUser serverUserBanning, String reason) {
|
||||
CompletableFutureMap<Long, User> userMap = userService.retrieveUsersMapped(Arrays.asList(serverUserToBeBanned.getUserId(), serverUserBanning.getUserId()));
|
||||
return userMap.getMainFuture().thenCompose(unused -> {
|
||||
User userToBeBanned = userMap.getElement(serverUserToBeBanned.getUserId());
|
||||
User banningUser = userMap.getElement(serverUserBanning.getUserId());
|
||||
UserBannedLogModel model = UserBannedLogModel
|
||||
.builder()
|
||||
.bannedUser(UserDisplay.fromUser(userToBeBanned))
|
||||
.banningUser(UserDisplay.fromUser(banningUser))
|
||||
.reason(reason)
|
||||
.build();
|
||||
return self.sendBanLogMessage(model, serverUserToBeBanned.getServerId());
|
||||
}).exceptionally(throwable -> {
|
||||
log.warn("Failed to load users ({}, {}) for ban log message.", serverUserToBeBanned.getUserId(), serverUserBanning.getUserId(), throwable);
|
||||
UserBannedLogModel model = UserBannedLogModel
|
||||
.builder()
|
||||
.bannedUser(UserDisplay.fromId(serverUserToBeBanned.getUserId()))
|
||||
.banningUser(UserDisplay.fromId(serverUserBanning.getUserId()))
|
||||
.reason(reason)
|
||||
.build();
|
||||
self.sendBanLogMessage(model, serverUserToBeBanned.getServerId());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> softBanUser(Guild guild, ServerUser user, Duration delDays) {
|
||||
return banUser(guild, user, delDays, "")
|
||||
.thenCompose(unused -> unbanUser(guild, user.getUserId()));
|
||||
@Transactional
|
||||
public CompletableFuture<Void> sendUnBanLogMessage(UserUnBannedLogModel model, Long serverId) {
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_UN_BANNED_NOTIFICATION_TEMPLATE, model, serverId);
|
||||
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, ModerationPostTarget.BAN_LOG, serverId));
|
||||
}
|
||||
|
||||
public CompletableFuture<Message> sendBanLogMessage(BanLog banLog, Long guildId) {
|
||||
MessageToSend banLogMessage = renderBanMessage(banLog, guildId);
|
||||
log.debug("Sending ban log message in guild {}.", guildId);
|
||||
List<CompletableFuture<Message>> messageFutures = postTargetService.sendEmbedInPostTarget(banLogMessage, ModerationPostTarget.BAN_LOG, guildId);
|
||||
return FutureUtils.toSingleFutureGeneric(messageFutures).thenApply(unused -> messageFutures.get(0).join());
|
||||
@Override
|
||||
@Transactional
|
||||
public CompletableFuture<Void> sendBanLogMessage(UserBannedLogModel model, Long serverId) {
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(USER_BANNED_NOTIFICATION_TEMPLATE, model, serverId);
|
||||
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, ModerationPostTarget.BAN_LOG, serverId));
|
||||
}
|
||||
|
||||
public MessageToSend renderBanMessage(BanLog banLog, Long guildId) {
|
||||
return templateService.renderEmbedTemplate(BAN_LOG_TEMPLATE, banLog, guildId);
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> sendUnBanLogMessage(UnBanLog banLog, Long guildId, String template) {
|
||||
MessageToSend banLogMessage = templateService.renderEmbedTemplate(template, banLog, guildId);
|
||||
log.debug("Sending unban log message in guild {}.", guildId);
|
||||
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(banLogMessage, ModerationPostTarget.UN_BAN_LOG, guildId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ public class InfractionServiceBean implements InfractionService {
|
||||
List<Infraction> infractions = infractionManagementService.getActiveInfractionsForUser(aUserInAServer);
|
||||
log.info("Calculating points for user {} in server {} with {} infractions.",
|
||||
aUserInAServer.getUserReference().getId(), aUserInAServer.getServerReference().getId(), infractions.size());
|
||||
return infractions.stream().collect(Collectors.summarizingLong(Infraction::getPoints)).getCount();
|
||||
return infractions.stream().collect(Collectors.summarizingLong(Infraction::getPoints)).getSum();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -112,8 +112,8 @@ public class InfractionServiceBean implements InfractionService {
|
||||
public CompletableFuture<Void> createInfractionNotification(AUserInAServer aUserInAServer, Long points, String type, String description) {
|
||||
Long serverId = aUserInAServer.getServerReference().getId();
|
||||
Long currentPoints = getActiveInfractionPointsForUser(aUserInAServer);
|
||||
Long newPoints = currentPoints + points;
|
||||
Pair<Integer, Integer> levelChange = infractionLevelChanged(serverId, newPoints, currentPoints);
|
||||
Long oldPoints = currentPoints - points;
|
||||
Pair<Integer, Integer> levelChange = infractionLevelChanged(serverId, currentPoints, oldPoints);
|
||||
Integer oldLevel = levelChange.getFirst();
|
||||
Integer newLevel = levelChange.getSecond();
|
||||
if(!oldLevel.equals(newLevel)) {
|
||||
@@ -124,10 +124,10 @@ public class InfractionServiceBean implements InfractionService {
|
||||
.oldLevel(oldLevel)
|
||||
.type(type)
|
||||
.description(description)
|
||||
.oldPoints(currentPoints)
|
||||
.newPoints(newPoints)
|
||||
.oldPoints(oldPoints)
|
||||
.newPoints(currentPoints)
|
||||
.build();
|
||||
infractionLevelChangedListenerManager.sendInfractionLevelChangedEvent(newLevel, oldLevel, newPoints, currentPoints, ServerUser.fromAUserInAServer(aUserInAServer));
|
||||
infractionLevelChangedListenerManager.sendInfractionLevelChangedEvent(newLevel, oldLevel, currentPoints, oldPoints, ServerUser.fromAUserInAServer(aUserInAServer));
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(INFRACTION_NOTIFICATION_TEMPLATE_KEY, model, serverId);
|
||||
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, InfractionPostTarget.INFRACTION_NOTIFICATION, serverId));
|
||||
} else {
|
||||
|
||||
@@ -2,11 +2,13 @@ package dev.sheldan.abstracto.moderation.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
|
||||
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
|
||||
import dev.sheldan.abstracto.core.service.ConfigService;
|
||||
import dev.sheldan.abstracto.core.service.FeatureFlagService;
|
||||
import dev.sheldan.abstracto.core.service.PostTargetService;
|
||||
import dev.sheldan.abstracto.core.service.UserService;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.core.utils.CompletableFutureMap;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureConfig;
|
||||
import dev.sheldan.abstracto.moderation.config.feature.ModerationFeatureDefinition;
|
||||
@@ -16,14 +18,12 @@ import dev.sheldan.abstracto.moderation.model.template.command.KickLogModel;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.UserSnowflake;
|
||||
import net.dv8tion.jda.api.entities.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@@ -51,6 +51,9 @@ public class KickServiceBean implements KickService {
|
||||
@Autowired
|
||||
private InfractionService infractionService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private KickServiceBean self;
|
||||
|
||||
@@ -59,7 +62,7 @@ public class KickServiceBean implements KickService {
|
||||
Guild guild = kickedMember.getGuild();
|
||||
log.info("Kicking user {} from guild {}", kickedMember.getUser().getIdLong(), guild.getIdLong());
|
||||
CompletableFuture<Void> kickFuture = guild.kick(kickedMember, reason).submit();
|
||||
CompletableFuture<Message> logFuture = sendKickLog(kickedMember, kickingMember, reason, guild.getIdLong());
|
||||
CompletableFuture<Message> logFuture = sendKickLog(kickedMember.getUser(), ServerUser.fromMember(kickedMember), kickingMember.getUser(), ServerUser.fromMember(kickingMember), reason, guild.getIdLong());
|
||||
return CompletableFuture.allOf(kickFuture, logFuture)
|
||||
.thenAccept(unused -> self.storeInfraction(kickedMember, kickingMember, reason, logFuture.join(), guild.getIdLong()));
|
||||
}
|
||||
@@ -96,27 +99,30 @@ public class KickServiceBean implements KickService {
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<Message> sendKickLog(Member kickedMember, Member kickingMember, String reason, Long serverId) {
|
||||
public CompletableFuture<Message> sendKickLog(User kickedUser, ServerUser kickedServerUser, User kickingUser, ServerUser kickingServerUser, String reason, Long serverId) {
|
||||
KickLogModel kickLogModel = KickLogModel
|
||||
.builder()
|
||||
.kickedMember(MemberDisplay.fromMember(kickedMember))
|
||||
.kickingMember(MemberDisplay.fromMember(kickingMember))
|
||||
.kickedUser(kickedUser != null ? UserDisplay.fromUser(kickedUser) : UserDisplay.fromServerUser(kickedServerUser))
|
||||
.kickingUser(kickingUser != null ? UserDisplay.fromUser(kickingUser) : UserDisplay.fromServerUser(kickingServerUser))
|
||||
.reason(reason)
|
||||
.build();
|
||||
return sendKicklog(serverId, kickLogModel);
|
||||
return sendKicklog(kickLogModel, serverId);
|
||||
}
|
||||
|
||||
private CompletableFuture<Message> sendKickLog(ServerUser kickedMember, ServerUser kickingMember, String reason, Long serverId) {
|
||||
KickLogModel kickLogModel = KickLogModel
|
||||
.builder()
|
||||
.kickedMember(MemberDisplay.fromServerUser(kickedMember))
|
||||
.kickingMember(MemberDisplay.fromServerUser(kickingMember))
|
||||
.reason(reason)
|
||||
.build();
|
||||
return sendKicklog(serverId, kickLogModel);
|
||||
public CompletableFuture<Message> sendKickLog(ServerUser kickedMember, ServerUser kickingMember, String reason, Long serverId) {
|
||||
CompletableFutureMap<Long, User> userMap = userService.retrieveUsersMapped(Arrays.asList(kickedMember.getUserId(), kickingMember.getUserId()));
|
||||
return userMap.getMainFuture().thenCompose(unused -> {
|
||||
User kickedUser = userMap.getElement(kickedMember.getUserId());
|
||||
User kickingUser = userMap.getElement(kickingMember.getUserId());
|
||||
return self.sendKickLog(kickedUser, kickedMember, kickingUser, kickingMember, reason, serverId);
|
||||
}).exceptionally(throwable -> {
|
||||
log.warn("Failed to fetch users ({}, {}) for kick event logging in server {}.", kickingMember.getUserId(), kickedMember.getUserId(), serverId, throwable);
|
||||
self.sendKickLog(null, kickedMember, null, kickingMember, reason, serverId);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private CompletableFuture<Message> sendKicklog(Long serverId, KickLogModel kickLogModel) {
|
||||
public CompletableFuture<Message> sendKicklog(KickLogModel kickLogModel, Long serverId) {
|
||||
MessageToSend warnLogMessage = templateService.renderEmbedTemplate(KICK_LOG_TEMPLATE, kickLogModel, serverId);
|
||||
log.debug("Sending kick log message in guild {}.", serverId);
|
||||
List<CompletableFuture<Message>> messageFutures = postTargetService.sendEmbedInPostTarget(warnLogMessage, ModerationPostTarget.KICK_LOG, serverId);
|
||||
|
||||
@@ -21,15 +21,14 @@ import dev.sheldan.abstracto.moderation.exception.NoMuteFoundException;
|
||||
import dev.sheldan.abstracto.moderation.model.MuteResult;
|
||||
import dev.sheldan.abstracto.moderation.model.database.Infraction;
|
||||
import dev.sheldan.abstracto.moderation.model.database.Mute;
|
||||
import dev.sheldan.abstracto.moderation.model.template.command.MuteListenerModel;
|
||||
import dev.sheldan.abstracto.moderation.model.template.command.MuteLogModel;
|
||||
import dev.sheldan.abstracto.moderation.model.template.command.MuteNotification;
|
||||
import dev.sheldan.abstracto.moderation.model.template.command.UnMuteLog;
|
||||
import dev.sheldan.abstracto.moderation.service.management.MuteManagementService;
|
||||
import dev.sheldan.abstracto.scheduling.model.JobParameters;
|
||||
import dev.sheldan.abstracto.scheduling.service.SchedulerService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -168,21 +167,60 @@ public class MuteServiceBean implements MuteService {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<MuteResult> muteMemberWithLog(ServerUser userToMute, ServerUser mutingUser, String reason, Duration duration, Guild guild, ServerChannelMessage origin) {
|
||||
return muteMemberWithLog(userToMute, mutingUser, reason, duration, guild, origin, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<MuteResult> muteMemberWithLog(ServerUser userToMute, ServerUser mutingUser, String reason, Duration duration, Guild guild, ServerChannelMessage origin, Instant oldTimeout) {
|
||||
Long serverId = userToMute.getServerId();
|
||||
Instant targetDate = Instant.now().plus(duration);
|
||||
log.debug("Muting member {} in server {}.", userToMute.getUserId(), serverId);
|
||||
log.info("Muting member {} in server {}.", userToMute.getUserId(), serverId);
|
||||
AServer server = serverManagementService.loadOrCreate(serverId);
|
||||
Long muteId = counterService.getNextCounterValue(server, MUTE_COUNTER_KEY);
|
||||
CompletableFuture<MuteResult> result = muteUserInServer(guild, userToMute, reason, duration);
|
||||
return result
|
||||
.thenCompose(unused -> self.sendMuteLog(userToMute, mutingUser, duration, reason))
|
||||
.thenCompose(logMessage -> self.evaluateAndStoreInfraction(userToMute, mutingUser, reason, targetDate, logMessage))
|
||||
.thenCompose(muteResult -> self.composeAndLogMute(userToMute, mutingUser, reason, duration, guild, oldTimeout))
|
||||
.thenCompose(logMessage -> self.evaluateAndStoreInfraction(userToMute, mutingUser, reason, targetDate))
|
||||
.thenAccept(infractionId -> self.persistMute(userToMute, mutingUser, targetDate, muteId, reason, infractionId, origin))
|
||||
.thenApply(unused -> result.join());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CompletableFuture<Long> evaluateAndStoreInfraction(ServerUser userToMute, ServerUser mutingUser, String reason, Instant targetDate, Message logMessage) {
|
||||
public CompletableFuture<Void> composeAndLogMute(ServerUser userToMute, ServerUser mutingUser, String reason, Duration duration, Guild guild, Instant oldTimeout) {
|
||||
CompletableFuture<Member> mutedMemberFuture = memberService.retrieveMemberInServer(userToMute);
|
||||
CompletableFuture<Member> mutingMemberFuture = memberService.retrieveMemberInServer(mutingUser);
|
||||
Instant targetDate = Instant.now().plus(duration);
|
||||
return CompletableFuture.allOf(mutedMemberFuture, mutingMemberFuture).thenCompose(unused -> {
|
||||
Member mutedMember = mutedMemberFuture.join();
|
||||
Member mutingMember = mutingMemberFuture.join();
|
||||
MuteLogModel muteLogModel = MuteLogModel
|
||||
.builder()
|
||||
.muteTargetDate(targetDate)
|
||||
.oldMuteTargetDate(oldTimeout)
|
||||
.mutingMember(MemberDisplay.fromMember(mutingMember))
|
||||
.mutedMember(MemberDisplay.fromMember(mutedMember))
|
||||
.duration(duration)
|
||||
.reason(reason)
|
||||
.build();
|
||||
return self.sendMuteLogMessage(muteLogModel, guild.getIdLong());
|
||||
}).exceptionally(throwable -> {
|
||||
log.warn("Failed to load users for mute log ({}, {}) in guild {}.", userToMute.getUserId(), mutingUser.getUserId(), guild.getIdLong(), throwable);
|
||||
MuteLogModel muteLogModel = MuteLogModel
|
||||
.builder()
|
||||
.muteTargetDate(targetDate)
|
||||
.oldMuteTargetDate(null)
|
||||
.mutingMember(MemberDisplay.fromServerUser(mutingUser))
|
||||
.mutedMember(MemberDisplay.fromServerUser(userToMute))
|
||||
.duration(duration)
|
||||
.reason(reason)
|
||||
.build();
|
||||
self.sendMuteLogMessage(muteLogModel, guild.getIdLong());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CompletableFuture<Long> evaluateAndStoreInfraction(ServerUser userToMute, ServerUser mutingUser, String reason, Instant targetDate) {
|
||||
Long serverId = userToMute.getServerId();
|
||||
if(featureFlagService.getFeatureFlagValue(ModerationFeatureDefinition.INFRACTIONS, serverId)) {
|
||||
Long infractionPoints = configService.getLongValueOrConfigDefault(MutingFeatureConfig.MUTE_INFRACTION_POINTS, serverId);
|
||||
@@ -190,7 +228,7 @@ public class MuteServiceBean implements MuteService {
|
||||
AUserInAServer mutingUserInAServer = userInServerManagementService.loadOrCreateUser(mutingUser);
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
parameters.put(INFRACTION_PARAMETER_DURATION_KEY, templateService.renderDuration(Duration.between(Instant.now(), targetDate), serverId));
|
||||
return infractionService.createInfractionWithNotification(mutedUserInAServer, infractionPoints, MUTE_INFRACTION_TYPE, reason, mutingUserInAServer, parameters, logMessage)
|
||||
return infractionService.createInfractionWithNotification(mutedUserInAServer, infractionPoints, MUTE_INFRACTION_TYPE, reason, mutingUserInAServer, parameters)
|
||||
.thenApply(Infraction::getId);
|
||||
} else {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
@@ -204,48 +242,13 @@ public class MuteServiceBean implements MuteService {
|
||||
createMuteObject(userToMute, mutingUser, reason, targetDate, muteId, triggerKey, infractionId, origin);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CompletableFuture<Message> sendMuteLog(ServerUser userBeingMuted, ServerUser mutingUser, Duration duration, String reason) {
|
||||
Instant targetDate = Instant.now().plus(duration);
|
||||
MuteListenerModel model = MuteListenerModel
|
||||
.builder()
|
||||
.mutedUser(MemberDisplay.fromServerUser(userBeingMuted))
|
||||
.mutingUser(MemberDisplay.fromServerUser(mutingUser))
|
||||
.oldMuteTargetDate(null)
|
||||
.duration(duration)
|
||||
.muteTargetDate(targetDate)
|
||||
.reason(reason)
|
||||
.build();
|
||||
log.debug("Sending mute log to the mute post target.");
|
||||
Long serverId = userBeingMuted.getServerId();
|
||||
MessageToSend message = templateService.renderEmbedTemplate(MUTE_LOG_TEMPLATE, model, serverId);
|
||||
List<CompletableFuture<Message>> futures = postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, serverId);
|
||||
return FutureUtils.toSingleFutureGeneric(futures).thenApply(unused -> futures.get(0).join());
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> sendUnMuteLogMessage(UnMuteLog muteLogModel, AServer server) {
|
||||
MuteListenerModel model = MuteListenerModel
|
||||
.builder()
|
||||
.mutedUser(muteLogModel.getUnMutedUser())
|
||||
.mutingUser(muteLogModel.getMutingUser())
|
||||
.oldMuteTargetDate(muteLogModel.getMute() != null ? muteLogModel.getMute().getMuteTargetDate() : null)
|
||||
.muteTargetDate(null)
|
||||
.build();
|
||||
if(muteLogModel.getMute() != null) {
|
||||
log.debug("Sending unMute log for mute {} to the mute posttarget in server {}", muteLogModel.getMute().getMuteId().getId(), server.getId());
|
||||
}
|
||||
MessageToSend message = templateService.renderEmbedTemplate(MUTE_LOG_TEMPLATE, model, server.getId());
|
||||
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, server.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CompletableFuture<Void> unMuteUser(ServerUser userToUnmute, ServerUser unMutingUser, Guild guild) {
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(userToUnmute);
|
||||
boolean muteActive = muteManagementService.hasActiveMute(aUserInAServer);
|
||||
if(!muteActive) {
|
||||
return memberService.removeTimeout(guild, userToUnmute, null)
|
||||
.thenCompose(unused -> self.sendUnmuteLog(null, guild, userToUnmute, unMutingUser));
|
||||
return memberService.removeTimeout(guild, userToUnmute, null);
|
||||
} else {
|
||||
Mute mute = muteManagementService.getAMuteOf(aUserInAServer);
|
||||
return endMute(mute, guild);
|
||||
@@ -261,31 +264,34 @@ public class MuteServiceBean implements MuteService {
|
||||
Long muteId = mute.getMuteId().getId();
|
||||
AServer mutingServer = mute.getServer();
|
||||
ServerUser mutedUser = ServerUser.fromAUserInAServer(mute.getMutedUser());
|
||||
ServerUser mutingUser = ServerUser.fromAUserInAServer(mute.getMutedUser());
|
||||
ServerUser mutingUser = ServerUser.fromAUserInAServer(mute.getMutingUser());
|
||||
log.info("UnMuting {} in server {}", mute.getMutedUser().getUserReference().getId(), mutingServer.getId());
|
||||
return memberService.removeTimeout(guild, mutedUser, null)
|
||||
.thenCompose(unused -> self.sendUnmuteLog(muteId, guild, mutedUser, mutingUser));
|
||||
.thenCompose(unused -> self.composeAndLogUnmute(mutedUser, mutingUser, guild))
|
||||
.thenAccept(unused -> {
|
||||
if(muteId != null) {
|
||||
self.endMuteInDatabase(muteId, guild.getIdLong());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Transactional
|
||||
public CompletableFuture<Void> sendUnmuteLog(Long muteId, Guild guild, ServerUser unMutedMember, ServerUser mutingMember) {
|
||||
Mute mute = null;
|
||||
if(muteId != null) {
|
||||
mute = muteManagementService.findMute(muteId, guild.getIdLong());
|
||||
}
|
||||
AServer mutingServer = serverManagementService.loadServer(guild.getIdLong());
|
||||
UnMuteLog unMuteLog = UnMuteLog
|
||||
.builder()
|
||||
.mute(mute)
|
||||
.mutingUser(MemberDisplay.fromServerUser(mutingMember))
|
||||
.unMutedUser(MemberDisplay.fromServerUser(unMutedMember))
|
||||
.build();
|
||||
CompletableFuture<Void> notificationFuture = sendUnMuteLogMessage(unMuteLog, mutingServer);
|
||||
return CompletableFuture.allOf(notificationFuture).thenAccept(aVoid -> {
|
||||
if(muteId != null) {
|
||||
self.endMuteInDatabase(muteId, guild.getIdLong());
|
||||
}
|
||||
public CompletableFuture<Void> composeAndLogUnmute(ServerUser mutedUser, ServerUser mutingUser, Guild guild) {
|
||||
CompletableFuture<Member> mutedMemberFuture = memberService.retrieveMemberInServer(mutedUser);
|
||||
CompletableFuture<Member> mutingMemberFuture = memberService.retrieveMemberInServer(mutingUser);
|
||||
return CompletableFuture.allOf(mutedMemberFuture, mutingMemberFuture).thenCompose(unused -> {
|
||||
Member mutedMember = mutedMemberFuture.join();
|
||||
Member mutingMember = mutingMemberFuture.join();
|
||||
MuteLogModel muteLogModel = MuteLogModel
|
||||
.builder()
|
||||
.muteTargetDate(null)
|
||||
.oldMuteTargetDate(null)
|
||||
.mutingMember(MemberDisplay.fromMember(mutingMember))
|
||||
.mutedMember(MemberDisplay.fromMember(mutedMember))
|
||||
.duration(null)
|
||||
.reason(null)
|
||||
.build();
|
||||
return self.sendMuteLogMessage(muteLogModel, guild.getIdLong());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -310,6 +316,12 @@ public class MuteServiceBean implements MuteService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> sendMuteLogMessage(MuteLogModel model, Long serverId) {
|
||||
MessageToSend message = templateService.renderEmbedTemplate(MuteServiceBean.MUTE_LOG_TEMPLATE, model, serverId);
|
||||
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(message, MutingPostTarget.MUTE_LOG, serverId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completelyUnMuteUser(AUserInAServer aUserInAServer) {
|
||||
log.info("Completely unmuting user {} in server {}.", aUserInAServer.getUserReference().getId(), aUserInAServer.getServerReference().getId());
|
||||
|
||||
@@ -32,23 +32,12 @@ abstracto.postTargets.banLog.name=banLog
|
||||
abstracto.postTargets.unBanLog.name=unBanLog
|
||||
abstracto.postTargets.muteLog.name=muteLog
|
||||
abstracto.postTargets.decayLog.name=decayLog
|
||||
|
||||
abstracto.featureModes.banCommandLogging.featureName=moderation
|
||||
abstracto.featureModes.banCommandLogging.mode=banCommandLogging
|
||||
abstracto.featureModes.banCommandLogging.enabled=true
|
||||
|
||||
abstracto.featureModes.unBanCommandLogging.featureName=moderation
|
||||
abstracto.featureModes.unBanCommandLogging.mode=unBanCommandLogging
|
||||
abstracto.featureModes.unBanCommandLogging.enabled=true
|
||||
abstracto.postTargets.infractionNotification.name=infractionNotification
|
||||
|
||||
abstracto.featureModes.warnDecayLogging.featureName=warnings
|
||||
abstracto.featureModes.warnDecayLogging.mode=warnDecayLogging
|
||||
abstracto.featureModes.warnDecayLogging.enabled=true
|
||||
|
||||
abstracto.featureModes.infractionReporting.featureName=infractions
|
||||
abstracto.featureModes.infractionReporting.mode=infractionReporting
|
||||
abstracto.featureModes.infractionReporting.enabled=true
|
||||
|
||||
abstracto.featureModes.anonymousReportReactions.featureName=reportReactions
|
||||
abstracto.featureModes.anonymousReportReactions.mode=anonymousReportReactions
|
||||
abstracto.featureModes.anonymousReportReactions.enabled=false
|
||||
@@ -61,20 +50,20 @@ abstracto.featureModes.reactionReportActions.featureName=reportReactions
|
||||
abstracto.featureModes.reactionReportActions.mode=reactionReportActions
|
||||
abstracto.featureModes.reactionReportActions.enabled=false
|
||||
|
||||
abstracto.systemConfigs.infractionLvl1.name=infractionLvl1
|
||||
abstracto.systemConfigs.infractionLvl1.longValue=10
|
||||
abstracto.systemConfigs.infractionLevel1.name=infractionLevel1
|
||||
abstracto.systemConfigs.infractionLevel1.longValue=10
|
||||
|
||||
abstracto.systemConfigs.infractionLvl2.name=infractionLvl2
|
||||
abstracto.systemConfigs.infractionLvl2.longValue=20
|
||||
abstracto.systemConfigs.infractionLevel2.name=infractionLevel2
|
||||
abstracto.systemConfigs.infractionLevel2.longValue=20
|
||||
|
||||
abstracto.systemConfigs.infractionLvl3.name=infractionLvl3
|
||||
abstracto.systemConfigs.infractionLvl3.longValue=30
|
||||
abstracto.systemConfigs.infractionLevel3.name=infractionLevel3
|
||||
abstracto.systemConfigs.infractionLevel3.longValue=30
|
||||
|
||||
abstracto.systemConfigs.infractionLvl4.name=infractionLvl4
|
||||
abstracto.systemConfigs.infractionLvl4.longValue=40
|
||||
abstracto.systemConfigs.infractionLevel4.name=infractionLevel4
|
||||
abstracto.systemConfigs.infractionLevel4.longValue=40
|
||||
|
||||
abstracto.systemConfigs.infractionLvl5.name=infractionLvl5
|
||||
abstracto.systemConfigs.infractionLvl5.longValue=50
|
||||
abstracto.systemConfigs.infractionLevel5.name=infractionLevel5
|
||||
abstracto.systemConfigs.infractionLevel5.longValue=50
|
||||
|
||||
abstracto.systemConfigs.infractionLevels.name=infractionLevels
|
||||
abstracto.systemConfigs.infractionLevels.longValue=5
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>moderation</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package dev.sheldan.abstracto.moderation.model.template.command;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
|
||||
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -13,6 +13,6 @@ import lombok.Setter;
|
||||
@Setter
|
||||
public class KickLogModel {
|
||||
private String reason;
|
||||
private MemberDisplay kickedMember;
|
||||
private MemberDisplay kickingMember;
|
||||
private UserDisplay kickedUser;
|
||||
private UserDisplay kickingUser;
|
||||
}
|
||||
|
||||
@@ -10,15 +10,15 @@ import java.time.Instant;
|
||||
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
public class MuteListenerModel {
|
||||
public class MuteLogModel {
|
||||
/**
|
||||
* The {@link Member} being muted
|
||||
*/
|
||||
private MemberDisplay mutedUser;
|
||||
private MemberDisplay mutedMember;
|
||||
/**
|
||||
* The {@link Member} executing the mute
|
||||
*/
|
||||
private MemberDisplay mutingUser;
|
||||
private MemberDisplay mutingMember;
|
||||
/**
|
||||
* The persisted mute object from the database containing the information about the mute
|
||||
*/
|
||||
@@ -1,71 +0,0 @@
|
||||
package dev.sheldan.abstracto.moderation.model.template.command;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.template.display.MemberDisplay;
|
||||
import dev.sheldan.abstracto.core.utils.MessageUtils;
|
||||
import dev.sheldan.abstracto.moderation.model.database.Mute;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* Used when rendering the notification when a member was muted. The template is: "unmute_log_embed"
|
||||
*/
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class UnMuteLog {
|
||||
/**
|
||||
* The un-muted Member, is null if the member left the server
|
||||
*/
|
||||
private MemberDisplay unMutedUser;
|
||||
/**
|
||||
* The user casting the mute, is null if the member left the server
|
||||
*/
|
||||
private MemberDisplay mutingUser;
|
||||
/**
|
||||
* The persisted mute object from the database containing the information about the mute
|
||||
*/
|
||||
private Mute mute;
|
||||
|
||||
/**
|
||||
* The actual duration between the date the mute started and the current time
|
||||
* @return The difference between mute start and now
|
||||
*/
|
||||
public Duration getMuteDuration() {
|
||||
return Duration.between(mute.getMuteDate(), Instant.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* The duration between the date the mute started and the un-mute planned
|
||||
* @return The difference between mute start and the target date
|
||||
*/
|
||||
public Duration getPlannedMuteDuration() {
|
||||
return Duration.between(mute.getMuteDate(), mute.getMuteTargetDate());
|
||||
}
|
||||
|
||||
/**
|
||||
* The un-mute date, which is now, because this is the un-mute log message.
|
||||
* @return The current time stamp
|
||||
*/
|
||||
public Instant getUnmuteDate() {
|
||||
return Instant.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the link to the original message triggering the mute
|
||||
* @return A string containing an URL leading to the message where the mute was triggered
|
||||
*/
|
||||
public String getMessageUrl() {
|
||||
if(this.mute.getMessageId() != null && this.mute.getMutingChannel() != null) {
|
||||
return MessageUtils.buildMessageUrl(this.mute.getServer().getId(), this.mute.getMutingChannel().getId(), this.mute.getMessageId());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
package dev.sheldan.abstracto.moderation.model.template.listener;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class UserBannedListenerModel {
|
||||
private User bannedUser;
|
||||
public class UserBannedLogModel {
|
||||
private UserDisplay bannedUser;
|
||||
private String reason;
|
||||
private User banningUser;
|
||||
private UserDisplay banningUser;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package dev.sheldan.abstracto.moderation.model.template.listener;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class UserUnBannedListenerModel {
|
||||
private User unBannedUser;
|
||||
private User unBanningUser;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.sheldan.abstracto.moderation.model.template.listener;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class UserUnBannedLogModel {
|
||||
private UserDisplay unBannedUser;
|
||||
private String reason;
|
||||
private UserDisplay unBanningUser;
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package dev.sheldan.abstracto.moderation.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||
import dev.sheldan.abstracto.moderation.model.BanResult;
|
||||
import dev.sheldan.abstracto.moderation.model.template.listener.UserBannedLogModel;
|
||||
import dev.sheldan.abstracto.moderation.model.template.listener.UserUnBannedLogModel;
|
||||
import net.dv8tion.jda.api.entities.*;
|
||||
|
||||
import java.time.Duration;
|
||||
@@ -12,8 +14,8 @@ public interface BanService {
|
||||
String BAN_INFRACTION_TYPE = "ban";
|
||||
String INFRACTION_PARAMETER_DELETION_DURATION_KEY = "DELETION_DURATION";
|
||||
CompletableFuture<BanResult> banUserWithNotification(ServerUser userToBeBanned, String reason, ServerUser banningUser, Guild guild, Duration deletionDuration);
|
||||
CompletableFuture<Void> unBanUserWithNotification(Long userId, ServerUser unBanningMember, Guild guild);
|
||||
CompletableFuture<Void> banUser(Guild guild, ServerUser userToBeBanned, Duration deletionDuration, String reason);
|
||||
CompletableFuture<Void> unbanUser(Guild guild, Long userId);
|
||||
CompletableFuture<Void> softBanUser(Guild guild, ServerUser user, Duration delDays);
|
||||
CompletableFuture<Void> unbanUser(Guild guild, User user, Member memberPerforming);
|
||||
CompletableFuture<Void> sendBanLogMessage(UserBannedLogModel model, Long serverId);
|
||||
CompletableFuture<Void> sendUnBanLogMessage(UserUnBannedLogModel model, Long serverId);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package dev.sheldan.abstracto.moderation.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.ServerUser;
|
||||
import dev.sheldan.abstracto.moderation.model.template.command.KickLogModel;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@@ -11,4 +13,5 @@ public interface KickService {
|
||||
String KICK_INFRACTION_TYPE = "kick";
|
||||
CompletableFuture<Void> kickMember(Member kickedMember, Member kickingMember, String reason);
|
||||
CompletableFuture<Void> kickMember(Guild guild, ServerUser kickedUser, String reason, ServerUser kickingUser);
|
||||
CompletableFuture<Message> sendKicklog(KickLogModel kickLogModel, Long serverId);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import dev.sheldan.abstracto.core.models.ServerUser;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.moderation.model.MuteResult;
|
||||
import dev.sheldan.abstracto.moderation.model.database.Mute;
|
||||
import dev.sheldan.abstracto.moderation.model.template.command.MuteLogModel;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
|
||||
import java.time.Duration;
|
||||
@@ -17,11 +18,13 @@ public interface MuteService {
|
||||
String INFRACTION_PARAMETER_DURATION_KEY = "DURATION";
|
||||
CompletableFuture<MuteResult> muteUserInServer(Guild guild, ServerUser userBeingMuted, String reason, Duration duration);
|
||||
CompletableFuture<MuteResult> muteMemberWithLog(ServerUser userToMute, ServerUser mutingUser, String reason, Duration duration, Guild guild, ServerChannelMessage origin);
|
||||
CompletableFuture<MuteResult> muteMemberWithLog(ServerUser userToMute, ServerUser mutingUser, String reason, Duration duration, Guild guild, ServerChannelMessage origin, Instant oldTimeout);
|
||||
String startUnMuteJobFor(Instant unMuteDate, Long muteId, Long serverId);
|
||||
void cancelUnMuteJob(Mute mute);
|
||||
CompletableFuture<Void> unMuteUser(ServerUser userToUnMute, ServerUser memberUnMuting, Guild guild);
|
||||
CompletableFuture<Void> endMute(Mute mute, Guild guild);
|
||||
CompletableFuture<Void> endMute(Long muteId, Long serverId);
|
||||
CompletableFuture<Void> sendMuteLogMessage(MuteLogModel model, Long serverId);
|
||||
void completelyUnMuteUser(AUserInAServer aUserInAServer);
|
||||
void completelyUnMuteMember(ServerUser serverUser);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>abstracto-modules</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>modmail</artifactId>
|
||||
<version>1.5.35</version>
|
||||
<version>1.5.42</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -38,6 +38,12 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>dev.sheldan.abstracto.modules</groupId>
|
||||
<artifactId>moderation-int</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>dev.sheldan.abstracto.core</groupId>
|
||||
<artifactId>metrics-int</artifactId>
|
||||
|
||||
@@ -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.CommandResult;
|
||||
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.config.ModMailFeatureDefinition;
|
||||
import dev.sheldan.abstracto.modmail.exception.ModMailThreadClosedException;
|
||||
@@ -40,7 +40,7 @@ public class AnonReply extends AbstractConditionableCommand {
|
||||
private ModMailThreadManagementService modMailThreadManagementService;
|
||||
|
||||
@Autowired
|
||||
private MemberService memberService;
|
||||
private UserService userService;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
@@ -52,8 +52,8 @@ public class AnonReply extends AbstractConditionableCommand {
|
||||
throw new ModMailThreadClosedException();
|
||||
}
|
||||
Long threadId = modMailThread.getId();
|
||||
return memberService.getMemberInServerAsync(modMailThread.getUser()).thenCompose(member ->
|
||||
modMailThreadService.loadExecutingMemberAndRelay(threadId, text, commandContext.getMessage(), true, member)
|
||||
return userService.retrieveUserForId(modMailThread.getUser().getUserReference().getId()).thenCompose(user ->
|
||||
modMailThreadService.loadExecutingMemberAndRelay(threadId, text, commandContext.getMessage(), true, user, commandContext.getGuild())
|
||||
).thenApply(aVoid -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,6 @@ public class Close extends AbstractConditionableCommand {
|
||||
.builder()
|
||||
.closingMember(commandContext.getAuthor())
|
||||
.notifyUser(true)
|
||||
.channel(commandContext.getChannel())
|
||||
.log(true)
|
||||
.note(note)
|
||||
.build();
|
||||
@@ -112,19 +111,18 @@ public class Close extends AbstractConditionableCommand {
|
||||
ClosingContext context = ClosingContext
|
||||
.builder()
|
||||
.closingMember(event.getMember())
|
||||
.channel(event.getChannel())
|
||||
.notifyUser(!silently)
|
||||
.log(log)
|
||||
.note(note)
|
||||
.build();
|
||||
return interactionService.replyEmbed(CLOSE_RESPONSE, event)
|
||||
.thenCompose(interactionHook -> self.closeThread(context))
|
||||
.thenCompose(interactionHook -> self.closeThread(context, event.getChannelIdLong()))
|
||||
.thenApply(aVoid -> CommandResult.fromIgnored());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CompletableFuture<Void> closeThread(ClosingContext closingContext) {
|
||||
ModMailThread modMailThread = modMailThreadManagementService.getByChannelId(closingContext.getChannel().getIdLong());
|
||||
public CompletableFuture<Void> closeThread(ClosingContext closingContext, Long channelId) {
|
||||
ModMailThread modMailThread = modMailThreadManagementService.getByChannelId(channelId);
|
||||
if(ModMailThreadState.CLOSED.equals(modMailThread.getState()) || ModMailThreadState.CLOSING.equals(modMailThread.getState())) {
|
||||
throw new ModMailThreadClosedException();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import dev.sheldan.abstracto.modmail.service.management.ModMailThreadManagementS
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
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.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.InteractionHook;
|
||||
@@ -44,7 +45,7 @@ import java.util.concurrent.CompletableFuture;
|
||||
@Slf4j
|
||||
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 MODMAIL_THREAD_ALREADY_EXISTS_TEMPLATE = "modmail_thread_already_exists";
|
||||
private static final String CONTACT_RESPONSE = "contact_response";
|
||||
@@ -87,24 +88,21 @@ public class Contact extends AbstractConditionableCommand {
|
||||
List<CompletableFuture<Message>> futures = channelService.sendEmbedTemplateInTextChannelList(MODMAIL_THREAD_ALREADY_EXISTS_TEMPLATE, model, commandContext.getChannel());
|
||||
return FutureUtils.toSingleFutureGeneric(futures).thenApply(aVoid -> CommandResult.fromIgnored());
|
||||
} else {
|
||||
return modMailThreadService.createModMailThreadForUser(targetUser, null, false, commandContext.getUndoActions())
|
||||
.thenCompose(unused -> modMailThreadService.sendContactNotification(targetUser, unused, commandContext.getChannel()))
|
||||
return modMailThreadService.createModMailThreadForUser(targetUser.getUser(), targetUser.getGuild(), null, false, commandContext.getUndoActions())
|
||||
.thenCompose(unused -> modMailThreadService.sendContactNotification(targetUser.getUser(), unused, commandContext.getChannel()))
|
||||
.thenApply(aVoid -> CommandResult.fromSuccess());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
|
||||
Member member = slashCommandParameterService.getCommandOption(USER_PARMETER, event, Member.class);
|
||||
if(!member.getGuild().equals(event.getGuild())) {
|
||||
throw new EntityGuildMismatchException();
|
||||
}
|
||||
AUserInAServer user = userManagementService.loadOrCreateUser(member);
|
||||
User user = slashCommandParameterService.getCommandOption(USER_PARMETER, event, User.class);
|
||||
AUserInAServer userInAServer = userManagementService.loadOrCreateUser(event.getGuild().getIdLong(), user.getIdLong());
|
||||
// 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
|
||||
if(modMailThreadManagementService.hasOpenModMailThreadForUser(user)) {
|
||||
log.info("Modmail thread for user {} in server {} already exists. Notifying user {}.", event.getMember().getId(), event.getGuild().getId(), user.getUserReference().getId());
|
||||
ModMailThread existingThread = modMailThreadManagementService.getOpenModMailThreadForUser(user);
|
||||
if(modMailThreadManagementService.hasOpenModMailThreadForUser(userInAServer)) {
|
||||
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(userInAServer);
|
||||
ModMailThreadExistsModel model = ModMailThreadExistsModel
|
||||
.builder()
|
||||
.existingModMailThread(existingThread)
|
||||
@@ -114,9 +112,9 @@ public class Contact extends AbstractConditionableCommand {
|
||||
.thenApply(interactionHook -> CommandResult.fromSuccess());
|
||||
} else {
|
||||
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<>());
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -126,7 +124,7 @@ public class Contact extends AbstractConditionableCommand {
|
||||
Parameter responseText = Parameter
|
||||
.builder()
|
||||
.name(USER_PARMETER)
|
||||
.type(Member.class)
|
||||
.type(User.class)
|
||||
.templated(true)
|
||||
.build();
|
||||
List<Parameter> parameters = Arrays.asList(responseText);
|
||||
@@ -139,11 +137,11 @@ public class Contact extends AbstractConditionableCommand {
|
||||
.builder()
|
||||
.enabled(true)
|
||||
.rootCommandName(ModMailSlashCommandNames.MODMAIL)
|
||||
.commandName(CONTACT_PARAMETER)
|
||||
.commandName(COMMAND_NAME)
|
||||
.build();
|
||||
|
||||
return CommandConfiguration.builder()
|
||||
.name(CONTACT_PARAMETER)
|
||||
.name(COMMAND_NAME)
|
||||
.module(ModMailModuleDefinition.MODMAIL)
|
||||
.parameters(parameters)
|
||||
.slashCommandConfig(slashCommandConfig)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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.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.config.ModMailFeatureDefinition;
|
||||
import dev.sheldan.abstracto.modmail.exception.ModMailThreadClosedException;
|
||||
@@ -39,7 +40,7 @@ public class Reply extends AbstractConditionableCommand {
|
||||
private ModMailThreadManagementService modMailThreadManagementService;
|
||||
|
||||
@Autowired
|
||||
private MemberService memberService;
|
||||
private UserService userService;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
@@ -50,8 +51,8 @@ public class Reply extends AbstractConditionableCommand {
|
||||
throw new ModMailThreadClosedException();
|
||||
}
|
||||
Long threadId = modMailThread.getId();
|
||||
return memberService.getMemberInServerAsync(modMailThread.getUser()).thenCompose(member ->
|
||||
modMailThreadService.loadExecutingMemberAndRelay(threadId, text, commandContext.getMessage(), false, member)
|
||||
return userService.retrieveUserForId(modMailThread.getUser().getUserReference().getId()).thenCompose(user ->
|
||||
modMailThreadService.loadExecutingMemberAndRelay(threadId, text, commandContext.getMessage(), false, user, commandContext.getGuild())
|
||||
).thenApply(aVoid -> CommandResult.fromSuccess());
|
||||
}
|
||||
|
||||
@@ -65,7 +66,7 @@ public class Reply extends AbstractConditionableCommand {
|
||||
.optional(true)
|
||||
.templated(true)
|
||||
.build();
|
||||
List<Parameter> parameters = Arrays.asList(responseText);
|
||||
List<Parameter> parameters = List.of(responseText);
|
||||
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
|
||||
return CommandConfiguration.builder()
|
||||
.name("reply")
|
||||
|
||||
@@ -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.ModMailThreadServiceBean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -25,7 +26,7 @@ import java.util.ArrayList;
|
||||
public class ModMailInitialButtonListener implements ButtonClickedListener {
|
||||
|
||||
@Autowired
|
||||
private MemberService memberService;
|
||||
private GuildService guildService;
|
||||
|
||||
@Autowired
|
||||
private ModMailThreadService modMailThreadService;
|
||||
@@ -51,20 +52,19 @@ public class ModMailInitialButtonListener implements ButtonClickedListener {
|
||||
Long userId = choices.getUserId();
|
||||
log.debug("Executing action for creationg a modmail thread in server {} for user {}.", chosenServer.getServerId(), userId);
|
||||
ArrayList<UndoActionInstance> undoActions = new ArrayList<>();
|
||||
memberService.getMemberInServerAsync(chosenServer.getServerId(), userId)
|
||||
.thenCompose(member -> channelService.retrieveMessageInChannel(model.getEvent().getChannel(), choices.getMessageId())
|
||||
Guild guild = guildService.getGuildById(chosenServer.getServerId());
|
||||
channelService.retrieveMessageInChannel(model.getEvent().getChannel(), choices.getMessageId())
|
||||
.thenCompose(originalMessage -> {
|
||||
try {
|
||||
return modMailThreadService.createModMailThreadForUser(member, originalMessage, true, undoActions);
|
||||
return modMailThreadService.createModMailThreadForUser(model.getEvent().getUser(), guild, originalMessage, true, undoActions);
|
||||
} catch (Exception ex) {
|
||||
log.error("Failed to setup thread correctly", ex);
|
||||
undoActionService.performActions(undoActions);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.thenAccept(unused -> self.cleanup(model)))
|
||||
.exceptionally(throwable -> {
|
||||
log.error("Failed to setup thread correctly", throwable);
|
||||
.thenAccept(unused -> self.cleanup(model)).exceptionally(throwable -> {
|
||||
log.warn("Failed to setup modmail thread.");
|
||||
undoActionService.performActions(undoActions);
|
||||
return null;
|
||||
});
|
||||
|
||||
@@ -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.service.MemberService;
|
||||
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.model.database.ModMailMessage;
|
||||
import dev.sheldan.abstracto.modmail.model.database.ModMailThread;
|
||||
@@ -33,7 +34,7 @@ public class ModMailMessageDeletedListener implements AsyncMessageDeletedListene
|
||||
private ModMailMessageDeletedListener self;
|
||||
|
||||
@Autowired
|
||||
private MemberService memberService;
|
||||
private UserService userService;
|
||||
|
||||
@Override
|
||||
public DefaultListenerResult execute(MessageDeletedModel model) {
|
||||
@@ -47,8 +48,8 @@ public class ModMailMessageDeletedListener implements AsyncMessageDeletedListene
|
||||
Long channelId = thread.getChannel().getId();
|
||||
Long serverId = thread.getServer().getId();
|
||||
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 -> {
|
||||
CompletableFuture<Void> dmDeletePromise = messageService.deleteMessageInChannelWithUser(member.getUser(), dmMessageId);
|
||||
userService.retrieveUserForId(modMailMessage.getThreadReference().getUser().getUserReference().getId()).thenAccept(user -> {
|
||||
CompletableFuture<Void> dmDeletePromise = messageService.deleteMessageInChannelWithUser(user, dmMessageId);
|
||||
CompletableFuture<Void> channelDeletePromise;
|
||||
if(hasMessageInChannel) {
|
||||
channelDeletePromise = messageService.deleteMessageInChannelInServer(serverId, channelId, channelMessage);
|
||||
|
||||
@@ -10,9 +10,11 @@ import dev.sheldan.abstracto.core.models.FullUserInServer;
|
||||
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.listener.MessageUpdatedModel;
|
||||
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.MemberService;
|
||||
import dev.sheldan.abstracto.core.service.MessageService;
|
||||
import dev.sheldan.abstracto.core.service.UserService;
|
||||
import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition;
|
||||
import dev.sheldan.abstracto.modmail.model.database.ModMailMessage;
|
||||
import dev.sheldan.abstracto.modmail.model.template.ModMailModeratorReplyModel;
|
||||
@@ -24,6 +26,7 @@ import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -45,9 +48,6 @@ public class ModMailMessageEditedListener implements AsyncMessageUpdatedListener
|
||||
@Autowired
|
||||
private CommandService commandService;
|
||||
|
||||
@Autowired
|
||||
private MemberService memberService;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@@ -66,6 +66,12 @@ public class ModMailMessageEditedListener implements AsyncMessageUpdatedListener
|
||||
@Autowired
|
||||
private ModMailThreadService modMailThreadService;
|
||||
|
||||
@Autowired
|
||||
private MemberService memberService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Override
|
||||
public DefaultListenerResult execute(MessageUpdatedModel model) {
|
||||
CachedMessage messageBefore = model.getBefore();
|
||||
@@ -85,7 +91,7 @@ public class ModMailMessageEditedListener implements AsyncMessageUpdatedListener
|
||||
log.info("Edit did not contain the original command to retrieve the parameters for. Resulting to {}.", DEFAULT_COMMAND_FOR_MODMAIL_EDIT);
|
||||
}
|
||||
CompletableFuture<Parameters> parameterParseFuture = commandService.getParametersForCommand(commandName, message);
|
||||
CompletableFuture<Member> loadTargetUser = memberService.getMemberInServerAsync(messageBefore.getServerId(), modMailMessage.getThreadReference().getUser().getUserReference().getId());
|
||||
CompletableFuture<User> loadTargetUser = userService.retrieveUserForId(modMailMessage.getThreadReference().getUser().getUserReference().getId());
|
||||
CompletableFuture<Member> loadEditingUser = memberService.getMemberInServerAsync(messageBefore.getServerId(), modMailMessage.getAuthor().getUserReference().getId());
|
||||
CompletableFuture.allOf(parameterParseFuture, loadTargetUser, loadEditingUser).thenAccept(unused ->
|
||||
self.updateMessageInThread(message, parameterParseFuture.join(), loadTargetUser.join(), loadEditingUser.join())
|
||||
@@ -100,15 +106,10 @@ public class ModMailMessageEditedListener implements AsyncMessageUpdatedListener
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void updateMessageInThread(Message loadedMessage, Parameters parameters, Member targetMember, Member editingUser) {
|
||||
public void updateMessageInThread(Message loadedMessage, Parameters parameters, User user, Member editingUser) {
|
||||
String newText = (String) parameters.getParameters().get(0);
|
||||
Optional<ModMailMessage> messageOptional = modMailMessageManagementService.getByMessageIdOptional(loadedMessage.getIdLong());
|
||||
messageOptional.ifPresent(modMailMessage -> {
|
||||
FullUserInServer fullThreadUser = FullUserInServer
|
||||
.builder()
|
||||
.aUserInAServer(modMailMessage.getThreadReference().getUser())
|
||||
.member(targetMember)
|
||||
.build();
|
||||
List<String> imageUrls = loadedMessage
|
||||
.getAttachments()
|
||||
.stream()
|
||||
@@ -128,7 +129,7 @@ public class ModMailMessageEditedListener implements AsyncMessageUpdatedListener
|
||||
.attachedImageUrls(imageUrls)
|
||||
.remainingAttachments(otherAttachments)
|
||||
.anonymous(modMailMessage.getAnonymous())
|
||||
.threadUser(fullThreadUser);
|
||||
.userDisplay(UserDisplay.fromUser(user));
|
||||
if(modMailMessage.getAnonymous()) {
|
||||
modMailModeratorReplyModelBuilder.moderator(memberService.getBotInGuild(modMailMessage.getThreadReference().getServer()));
|
||||
} else {
|
||||
@@ -143,13 +144,13 @@ public class ModMailMessageEditedListener implements AsyncMessageUpdatedListener
|
||||
log.debug("Editing message {} in mod mail channel {} for thread {} in server {} as well.", modMailMessage.getCreatedMessageInChannel(), channel.getId(), threadId, serverId);
|
||||
channelService.editMessageInAChannel(messageToSend, channel, modMailMessage.getCreatedMessageInChannel());
|
||||
}
|
||||
log.debug("Editing message {} in DM channel with user {} for thread {} in server {}.", modMailMessage.getCreatedMessageInDM(), targetMember.getUser().getIdLong(), threadId, serverId);
|
||||
messageService.editMessageInDMChannel(targetMember.getUser(), messageToSend, modMailMessage.getCreatedMessageInDM());
|
||||
log.debug("Editing message {} in DM channel with user {} for thread {} in server {}.", modMailMessage.getCreatedMessageInDM(), user.getIdLong(), threadId, serverId);
|
||||
messageService.editMessageInDMChannel(user, messageToSend, modMailMessage.getCreatedMessageInDM());
|
||||
});
|
||||
|
||||
if(!messageOptional.isPresent()) {
|
||||
log.warn("Message {} of user {} in channel {} for server {} for thread about user {} could not be found in the mod mail messages when updating the text.",
|
||||
loadedMessage.getIdLong(), editingUser.getIdLong(), loadedMessage.getChannel().getIdLong(), loadedMessage.getGuild().getIdLong(), targetMember.getIdLong());
|
||||
loadedMessage.getIdLong(), editingUser.getIdLong(), loadedMessage.getChannel().getIdLong(), loadedMessage.getGuild().getIdLong(), user.getIdLong());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,9 +36,6 @@ public class ModMailMessageListener implements PrivateMessageReceivedListener {
|
||||
@Autowired
|
||||
private UserManagementService userManagementService;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void execute(Message message) {
|
||||
|
||||
@@ -19,9 +19,6 @@ public class ModMailRoleServiceBean implements ModMailRoleService {
|
||||
@Autowired
|
||||
private CommandService commandService;
|
||||
|
||||
@Autowired
|
||||
private FeatureManagementService featureManagementService;
|
||||
|
||||
@Override
|
||||
public void addRoleToModMailRoles(ARole role) {
|
||||
log.info("Adding role {} to modmail roles in server {}.", role.getId(), role.getServer().getId());
|
||||
|
||||
@@ -8,17 +8,16 @@ import dev.sheldan.abstracto.core.interaction.InteractionService;
|
||||
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
|
||||
import dev.sheldan.abstracto.core.metric.service.MetricService;
|
||||
import dev.sheldan.abstracto.core.metric.service.MetricTag;
|
||||
import dev.sheldan.abstracto.core.models.FeatureValidationResult;
|
||||
import dev.sheldan.abstracto.core.models.FullGuild;
|
||||
import dev.sheldan.abstracto.core.models.FullUserInServer;
|
||||
import dev.sheldan.abstracto.core.models.UndoActionInstance;
|
||||
import dev.sheldan.abstracto.core.models.*;
|
||||
import dev.sheldan.abstracto.core.models.database.*;
|
||||
import dev.sheldan.abstracto.core.models.template.display.UserDisplay;
|
||||
import dev.sheldan.abstracto.core.service.*;
|
||||
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
|
||||
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import dev.sheldan.abstracto.moderation.service.BanService;
|
||||
import dev.sheldan.abstracto.modmail.config.ModMailFeatureConfig;
|
||||
import dev.sheldan.abstracto.modmail.config.ModMailFeatureDefinition;
|
||||
import dev.sheldan.abstracto.modmail.config.ModMailMode;
|
||||
@@ -51,6 +50,7 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -111,6 +111,9 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
@Autowired
|
||||
private GuildService guildService;
|
||||
|
||||
@Autowired
|
||||
private BanService banService;
|
||||
|
||||
@Autowired
|
||||
private MessageService messageService;
|
||||
|
||||
@@ -187,22 +190,21 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
public static final String MODMAIL_INITIAL_ORIGIN = "modmailInitial";
|
||||
|
||||
@Override
|
||||
public CompletableFuture<MessageChannel> createModMailThreadForUser(Member member, Message initialMessage, boolean userInitiated, List<UndoActionInstance> undoActions) {
|
||||
Long serverId = member.getGuild().getIdLong();
|
||||
User user = member.getUser();
|
||||
AServer server = serverManagementService.loadServer(member.getGuild().getIdLong());
|
||||
public CompletableFuture<MessageChannel> createModMailThreadForUser(User user, Guild guild, Message initialMessage, boolean userInitiated, List<UndoActionInstance> undoActions) {
|
||||
Long serverId = guild.getIdLong();
|
||||
AServer server = serverManagementService.loadServer(serverId);
|
||||
metricService.incrementCounter(MODMAIL_THREAD_CREATED_COUNTER);
|
||||
ModMailChannelNameModel model = ModMailChannelNameModel
|
||||
.builder()
|
||||
.serverId(serverId)
|
||||
.userId(member.getIdLong())
|
||||
.userId(user.getIdLong())
|
||||
.randomText(RandomStringUtils.randomAlphanumeric(25))
|
||||
.uuid(UUID.randomUUID().toString())
|
||||
.currentDate(Instant.now())
|
||||
.build();
|
||||
String channelName = templateService.renderTemplate(TEXT_CHANNEL_NAME_TEMPLATE_KEY, model, serverId);
|
||||
if (featureModeService.featureModeActive(ModMailFeatureDefinition.MOD_MAIL, serverId, ModMailMode.THREAD_CONTAINER)) {
|
||||
MessageToSend notificationMessageToSend = getModmailNotificationMessageToSend(member, null, serverId, false);
|
||||
MessageToSend notificationMessageToSend = getModmailNotificationMessageToSend(user, null, serverId, false);
|
||||
Optional<GuildMessageChannel> modmailContainerOptional = postTargetService.getPostTargetChannel(ModMailPostTargets.MOD_MAIL_CONTAINER, serverId);
|
||||
if(modmailContainerOptional.isEmpty()) {
|
||||
throw new AbstractoRunTimeException("Modmail thread container not setup.");
|
||||
@@ -218,7 +220,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
.thenCompose(unused -> channelService.createThreadWithStarterMessage(textChannel, channelName, notificationMessage.get(0).join().getIdLong()))
|
||||
.thenCompose(threadChannel -> {
|
||||
undoActions.add(UndoActionInstance.getChannelDeleteAction(serverId, threadChannel.getIdLong()));
|
||||
return self.performModMailThreadSetup(member, initialMessage, threadChannel, userInitiated, undoActions)
|
||||
return self.performModMailThreadSetup(user, initialMessage, threadChannel, userInitiated, undoActions)
|
||||
.thenCompose(unused -> CompletableFuture.completedFuture(threadChannel));
|
||||
});
|
||||
} else {
|
||||
@@ -227,7 +229,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
CompletableFuture<TextChannel> textChannelFuture = channelService.createTextChannel(channelName, server, categoryId);
|
||||
return textChannelFuture.thenCompose(channel -> {
|
||||
undoActions.add(UndoActionInstance.getChannelDeleteAction(serverId, channel.getIdLong()));
|
||||
return self.performModMailThreadSetup(member, initialMessage, channel, userInitiated, undoActions)
|
||||
return self.performModMailThreadSetup(user, initialMessage, channel, userInitiated, undoActions)
|
||||
.thenCompose(unused -> CompletableFuture.completedFuture(channel));
|
||||
});
|
||||
}
|
||||
@@ -235,21 +237,21 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CompletableFuture<Void> sendContactNotification(Member member, MessageChannel messageChannel, MessageChannel feedBackChannel) {
|
||||
public CompletableFuture<Void> sendContactNotification(User user, MessageChannel messageChannel, MessageChannel feedBackChannel) {
|
||||
ContactNotificationModel model = ContactNotificationModel
|
||||
.builder()
|
||||
.createdChannel(messageChannel)
|
||||
.targetMember(member)
|
||||
.userDisplay(UserDisplay.fromUser(user))
|
||||
.build();
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannelList(MODMAIL_THREAD_CREATED_TEMPLATE_KEY, model, feedBackChannel));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> sendContactNotification(Member member, MessageChannel createdMessageChannel, InteractionHook interactionHook) {
|
||||
public CompletableFuture<Void> sendContactNotification(User user, MessageChannel createdMessageChannel, InteractionHook interactionHook) {
|
||||
ContactNotificationModel model = ContactNotificationModel
|
||||
.builder()
|
||||
.createdChannel(createdMessageChannel)
|
||||
.targetMember(member)
|
||||
.userDisplay(UserDisplay.fromUser(user))
|
||||
.build();
|
||||
return FutureUtils.toSingleFutureGeneric(interactionService.sendMessageToInteraction(MODMAIL_THREAD_CREATED_TEMPLATE_KEY, model, interactionHook));
|
||||
}
|
||||
@@ -257,7 +259,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
/**
|
||||
* This method is responsible for creating the instance in the database, sending the header in the newly created text channel and forwarding the initial message
|
||||
* by the user (if any), after this is complete, this method executes the method to perform the mod mail notification.
|
||||
* @param member The {@link Member} for which a {@link ModMailThread} is being created
|
||||
* @param user The {@link User} for which a {@link ModMailThread} is being created
|
||||
* @param initialMessage The {@link Message} which was sent by the user to open a thread, this is null, if the thread was opened via a command
|
||||
* @param channel The created {@link TextChannel} in which the mod mail thread is dealt with
|
||||
* @param userInitiated Whether the thread was initiated by a member
|
||||
@@ -265,33 +267,33 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
* @return A {@link CompletableFuture future} which completes when the setup is done
|
||||
*/
|
||||
@Transactional
|
||||
public CompletableFuture<Void> performModMailThreadSetup(Member member, Message initialMessage, GuildMessageChannel channel, boolean userInitiated, List<UndoActionInstance> undoActions) {
|
||||
log.info("Performing modmail thread setup for channel {} for user {} in server {}. It was initiated by a user: {}.", channel.getIdLong(), member.getId(), channel.getGuild().getId(), userInitiated);
|
||||
CompletableFuture<Void> headerFuture = sendModMailHeader(channel, member);
|
||||
public CompletableFuture<Void> performModMailThreadSetup(User user, Message initialMessage, GuildMessageChannel channel, boolean userInitiated, List<UndoActionInstance> undoActions) {
|
||||
log.info("Performing modmail thread setup for channel {} for user {} in server {}. It was initiated by a user: {}.", channel.getIdLong(), user.getId(), channel.getGuild().getId(), userInitiated);
|
||||
CompletableFuture<Void> headerFuture = sendModMailHeader(channel, user);
|
||||
CompletableFuture<Message> userReplyMessage;
|
||||
if(initialMessage != null){
|
||||
log.info("Sending initial message {} of user {} to modmail thread {}.", initialMessage.getId(), member.getId(), channel.getId());
|
||||
userReplyMessage = self.sendUserReply(channel, 0L, initialMessage, member, false);
|
||||
log.info("Sending initial message {} of user {} to modmail thread {}.", initialMessage.getId(), user.getId(), channel.getId());
|
||||
userReplyMessage = self.sendUserReply(channel, 0L, initialMessage, false);
|
||||
} else {
|
||||
log.info("No initial message to send.");
|
||||
userReplyMessage = CompletableFuture.completedFuture(null);
|
||||
}
|
||||
CompletableFuture notificationFuture;
|
||||
if (userInitiated) {
|
||||
notificationFuture = self.sendModMailNotification(member, channel);
|
||||
notificationFuture = self.sendModMailNotification(user, channel);
|
||||
} else {
|
||||
notificationFuture = CompletableFuture.completedFuture(null);
|
||||
}
|
||||
return CompletableFuture.allOf(headerFuture, notificationFuture, userReplyMessage).thenAccept(aVoid -> {
|
||||
undoActions.clear();
|
||||
self.setupModMailThreadInDB(initialMessage, channel, member, userReplyMessage.join());
|
||||
self.setupModMailThreadInDB(initialMessage, channel, user, userReplyMessage.join());
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void setupModMailThreadInDB(Message initialMessage, GuildMessageChannel channel, Member member, Message sendMessage) {
|
||||
public void setupModMailThreadInDB(Message initialMessage, GuildMessageChannel channel, User user, Message sendMessage) {
|
||||
log.info("Persisting info about modmail thread {} in database.", channel.getIdLong());
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(channel.getGuild().getIdLong(), user.getIdLong());
|
||||
ModMailThread thread = createThreadObject(channel, aUserInAServer);
|
||||
if(initialMessage != null) {
|
||||
log.debug("Adding initial message {} to modmail thread in channel {}.", initialMessage.getId(), channel.getId());
|
||||
@@ -301,19 +303,19 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
|
||||
/**
|
||||
* Sends the message containing the pings to notify the staff members to handle the opened {@link ModMailThread}
|
||||
* @param member The {@link FullUserInServer} which opened the thread
|
||||
* @param user The {@link FullUserInServer} which opened the thread
|
||||
* @param channel The created {@link GuildMessageChannel} in which the mod mail thread is dealt with
|
||||
* @return A {@link CompletableFuture future} which completes when the notification has been sent
|
||||
*/
|
||||
@Transactional
|
||||
public CompletableFuture<Void> sendModMailNotification(Member member, GuildMessageChannel channel) {
|
||||
Long serverId = member.getGuild().getIdLong();
|
||||
MessageToSend messageToSend = getModmailNotificationMessageToSend(member, channel, serverId, true);
|
||||
public CompletableFuture<Void> sendModMailNotification(User user, GuildMessageChannel channel) {
|
||||
Long serverId = channel.getGuild().getIdLong();
|
||||
MessageToSend messageToSend = getModmailNotificationMessageToSend(user, channel, serverId, true);
|
||||
return FutureUtils.toSingleFutureGeneric(postTargetService.sendEmbedInPostTarget(messageToSend, ModMailPostTargets.MOD_MAIL_PING, serverId));
|
||||
}
|
||||
|
||||
private MessageToSend getModmailNotificationMessageToSend(Member member, GuildMessageChannel channel, Long serverId, boolean pingRole) {
|
||||
log.info("Sending modmail notification for new modmail thread about user {} in server {}.", member.getId(), serverId);
|
||||
private MessageToSend getModmailNotificationMessageToSend(User user, GuildMessageChannel channel, Long serverId, boolean pingRole) {
|
||||
log.info("Sending modmail notification for new modmail thread about user {} in server {}.", user.getId(), serverId);
|
||||
AServer server = serverManagementService.loadServer(serverId);
|
||||
List<ModMailRole> rolesToPing;
|
||||
if(pingRole) {
|
||||
@@ -321,10 +323,10 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
} else {
|
||||
rolesToPing = new ArrayList<>();
|
||||
}
|
||||
log.debug("Pinging {} roles to notify about modmail thread about user {} in server {}.", rolesToPing.size(), member.getId(), serverId);
|
||||
log.debug("Pinging {} roles to notify about modmail thread about user {} in server {}.", rolesToPing.size(), user.getId(), serverId);
|
||||
ModMailNotificationModel modMailNotificationModel = ModMailNotificationModel
|
||||
.builder()
|
||||
.member(member)
|
||||
.userDisplay(UserDisplay.fromUser(user))
|
||||
.roles(rolesToPing)
|
||||
.channel(channel)
|
||||
.build();
|
||||
@@ -369,24 +371,57 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
if(!servers.isEmpty()) {
|
||||
log.info("There are {} shared servers between user and the bot.", servers.size());
|
||||
List<ServerChoice> availableGuilds = new ArrayList<>();
|
||||
Set<Long> alreadyConsideredServers = new HashSet<>();
|
||||
for (AServer server : servers) {
|
||||
// only take the servers in which mod mail is actually enabled, would not make much sense to make the
|
||||
// other servers available
|
||||
if (featureFlagService.isFeatureEnabled(modMailFeatureConfig, server)) {
|
||||
FullGuild guild = FullGuild
|
||||
.builder()
|
||||
.guild(guildService.getGuildById(server.getId()))
|
||||
.server(server)
|
||||
.build();
|
||||
boolean possibleForModmail = featureFlagService.isFeatureEnabled(modMailFeatureConfig, server);
|
||||
if (possibleForModmail) {
|
||||
Guild guild = guildService.getGuildById(server.getId());
|
||||
ServerChoice serverChoice = ServerChoice
|
||||
.builder()
|
||||
.serverId(guild.getGuild().getIdLong())
|
||||
.serverName(guild.getGuild().getName())
|
||||
.serverId(guild.getIdLong())
|
||||
.serverName(guild.getName())
|
||||
.build();
|
||||
availableGuilds.add(serverChoice);
|
||||
}
|
||||
alreadyConsideredServers.add(server.getId());
|
||||
}
|
||||
|
||||
List<AServer> restOfKnownServers = serverManagementService.getAllServers()
|
||||
.stream()
|
||||
.filter(server -> alreadyConsideredServers.contains(server.getId()))
|
||||
.toList();
|
||||
for (AServer server : restOfKnownServers) {
|
||||
boolean possibleForModmail = false;
|
||||
Long actualServerId = 0L;
|
||||
Long potentialMainServer = configService.getLongValue(ModMailFeatureConfig.MOD_MAIL_APPEAL_SERVER, server.getId()); // what _other_ server is the appeal server
|
||||
if(potentialMainServer != 0) {
|
||||
if(featureModeService.featureModeActive(ModMailFeatureDefinition.MOD_MAIL, potentialMainServer, ModMailMode.MOD_MAIL_APPEALS)) {
|
||||
Long configuredAppealServerId = configService.getLongValue(ModMailFeatureConfig.MOD_MAIL_APPEAL_SERVER, potentialMainServer);
|
||||
if(configuredAppealServerId != 0 && configuredAppealServerId.equals(server.getId())) { // if the other server has set the current server as the appeal config
|
||||
Guild otherGuild = guildService.getGuildById(potentialMainServer);
|
||||
if(otherGuild != null) { // check if we are part of that server
|
||||
possibleForModmail = true;
|
||||
actualServerId = potentialMainServer;
|
||||
log.info("Server {} was available, because it is using server {} as a mod mail appeal server.", server.getId(), otherGuild.getIdLong());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.info("Server {} has set the appeal server {}, but that server does not have mod mail appeals enabled.", server.getId(), potentialMainServer);
|
||||
}
|
||||
}
|
||||
if(possibleForModmail) {
|
||||
Guild guild = guildService.getGuildById(actualServerId);
|
||||
ServerChoice serverChoice = ServerChoice
|
||||
.builder()
|
||||
.serverId(guild.getIdLong())
|
||||
.serverName(guild.getName())
|
||||
.build();
|
||||
availableGuilds.add(serverChoice);
|
||||
}
|
||||
}
|
||||
log.info("There were {} shared servers found which have modmail enabled.", availableGuilds.size());
|
||||
log.info("There were {} available servers found.", availableGuilds.size());
|
||||
// if more than 1 server is available, show a choice dialog
|
||||
ArrayList<UndoActionInstance> undoActions = new ArrayList<>();
|
||||
if(availableGuilds.size() > 1) {
|
||||
@@ -414,20 +449,15 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
} else if(availableGuilds.size() == 1) {
|
||||
// if exactly one server is available, open the thread directly
|
||||
Long chosenServerId = availableGuilds.get(0).getServerId();
|
||||
Guild guild = guildService.getGuildById(chosenServerId);
|
||||
log.info("Only one server available to modmail. Directly opening modmail thread for user {} in server {}.", initialMessage.getAuthor().getId(), chosenServerId);
|
||||
memberService.getMemberInServerAsync(chosenServerId, initialMessage.getAuthor().getIdLong()).thenCompose(member -> {
|
||||
try {
|
||||
return self.createModMailThreadForUser(member, initialMessage, true, undoActions).thenApply(messageChannel -> null);
|
||||
} catch (Exception exception) {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
future.completeExceptionally(exception);
|
||||
return future;
|
||||
}
|
||||
}).exceptionally(throwable -> {
|
||||
log.error("Failed to setup thread correctly", throwable);
|
||||
undoActionService.performActions(undoActions);
|
||||
return null;
|
||||
});
|
||||
createModMailThreadForUser(initialMessage.getAuthor(), guild , initialMessage, true, undoActions)
|
||||
.thenAccept(messageChannel -> {
|
||||
log.info("Setup modmail thread for user {} in guild {}.", initialMessage.getAuthor().getIdLong(), guild.getIdLong());
|
||||
}).exceptionally(throwable -> {
|
||||
log.error("Failed to setup modmail channel in guild {} for user {}.", guild.getIdLong(), initialMessage.getAuthor().getIdLong(), throwable);
|
||||
return null;
|
||||
});
|
||||
} else {
|
||||
log.info("No server available to open a modmail thread in.");
|
||||
// in case there is no server available, send an error message
|
||||
@@ -450,18 +480,17 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
* Method used to send the header of a newly created mod mail thread. This message contains information about
|
||||
* the user which the thread is about
|
||||
* @param channel The {@link GuildMessageChannel} in which the mod mail thread is present in
|
||||
* @param member The {@link Member} which the {@link ModMailThread} is about
|
||||
* @param user The {@link User} which the {@link ModMailThread} is about
|
||||
*/
|
||||
private CompletableFuture<Void> sendModMailHeader(GuildMessageChannel channel, Member member) {
|
||||
private CompletableFuture<Void> sendModMailHeader(GuildMessageChannel channel, User user) {
|
||||
log.debug("Sending modmail thread header for tread in channel {} on server {}.", channel.getIdLong(), channel.getGuild().getId());
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(member);
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(channel.getGuild().getIdLong(), user.getIdLong());
|
||||
ModMailThread latestThread = modMailThreadManagementService.getLatestModMailThread(aUserInAServer);
|
||||
List<ModMailThread> oldThreads = modMailThreadManagementService.getModMailThreadForUser(aUserInAServer);
|
||||
ModMailThreaderHeader header = ModMailThreaderHeader
|
||||
.builder()
|
||||
.member(member)
|
||||
.userDisplay(UserDisplay.fromUser(user))
|
||||
.latestModMailThread(latestThread)
|
||||
.memberJoinDate(member.getTimeJoined().toInstant())
|
||||
.pastModMailThreadCount((long)oldThreads.size())
|
||||
.build();
|
||||
List<CompletableFuture<Message>> messages = channelService.sendEmbedTemplateInTextChannelList("modmail_thread_header", header, channel);
|
||||
@@ -475,18 +504,10 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
Long modmailThreadId = modMailThread.getId();
|
||||
metricService.incrementCounter(MDOMAIL_THREAD_MESSAGE_RECEIVED);
|
||||
log.debug("Relaying message {} to modmail thread {} for user {} to server {}.", messageFromUser.getId(), modMailThread.getId(), messageFromUser.getAuthor().getIdLong(), modMailThread.getServer().getId());
|
||||
return memberService.getMemberInServerAsync(modMailThread.getServer().getId(), messageFromUser.getAuthor().getIdLong()).thenCompose(member ->
|
||||
self.relayMessage(messageFromUser, serverId, channelId, modmailThreadId, member)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CompletableFuture<Message> relayMessage(Message messageFromUser, Long serverId, Long channelId, Long modmailThreadId, Member member) {
|
||||
Optional<GuildMessageChannel> textChannelFromServer = channelService.getMessageChannelFromServerOptional(serverId, channelId);
|
||||
if(textChannelFromServer.isPresent()) {
|
||||
GuildMessageChannel guildMessageChannel = textChannelFromServer.get();
|
||||
return self.sendUserReply(guildMessageChannel, modmailThreadId, messageFromUser, member, true);
|
||||
return self.sendUserReply(guildMessageChannel, modmailThreadId, messageFromUser, true);
|
||||
} else {
|
||||
log.warn("Closing mod mail thread {}, because it seems the channel {} in server {} got deleted.", modmailThreadId, channelId, serverId);
|
||||
// in this case there was no text channel on the server associated with the mod mail thread
|
||||
@@ -504,11 +525,10 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
* @param messageChannel The {@link GuildMessageChannel} in which the {@link ModMailThread} is being handled
|
||||
* @param modMailThreadId The id of the modmail thread to which the received {@link Message} is a reply to, can be null, if it is null, its the initial message
|
||||
* @param messageFromUser The received message from the user
|
||||
* @param member The {@link Member} instance from the user the thread is about. It is used as author
|
||||
* @param modMailThreadExists Whether the modmail thread already exists and is persisted.
|
||||
* @return A {@link CompletableFuture} which resolves when the postprocessing of the message is completed (adding read notification, and storing messageIDs)
|
||||
*/
|
||||
public CompletableFuture<Message> sendUserReply(GuildMessageChannel messageChannel, Long modMailThreadId, Message messageFromUser, Member member, boolean modMailThreadExists) {
|
||||
public CompletableFuture<Message> sendUserReply(GuildMessageChannel messageChannel, Long modMailThreadId, Message messageFromUser, boolean modMailThreadExists) {
|
||||
List<CompletableFuture<Member>> subscriberMemberFutures = new ArrayList<>();
|
||||
if(modMailThreadExists) {
|
||||
ModMailThread modMailThread = modMailThreadManagementService.getById(modMailThreadId);
|
||||
@@ -549,7 +569,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
ModMailUserReplyModel modMailUserReplyModel = ModMailUserReplyModel
|
||||
.builder()
|
||||
.postedMessage(messageFromUser)
|
||||
.member(member)
|
||||
.userDisplay(UserDisplay.fromUser(messageFromUser.getAuthor()))
|
||||
.attachedImageUrls(imageUrls)
|
||||
.remainingAttachments(otherAttachments)
|
||||
.subscribers(subscribers)
|
||||
@@ -603,21 +623,16 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CompletableFuture<Void> loadExecutingMemberAndRelay(Long modmailThreadId, String text, Message replyCommandMessage, boolean anonymous, Member targetMember) {
|
||||
log.info("Relaying message {} to user {} in modmail thread {} on server {}.", replyCommandMessage.getId(), targetMember.getId(), modmailThreadId, targetMember.getGuild().getId());
|
||||
public CompletableFuture<Void> loadExecutingMemberAndRelay(Long modmailThreadId, String text, Message replyCommandMessage, boolean anonymous, User user, Guild guild) {
|
||||
log.info("Relaying message {} to user {} in modmail thread {} on server {}.", replyCommandMessage.getId(), user.getId(), modmailThreadId, guild.getId());
|
||||
return memberService.getMemberInServerAsync(replyCommandMessage.getGuild().getIdLong(), replyCommandMessage.getAuthor().getIdLong())
|
||||
.thenCompose(executingMember -> self.relayMessageToDm(modmailThreadId, text, replyCommandMessage, anonymous, targetMember, executingMember));
|
||||
.thenCompose(executingMember -> self.relayMessageToDm(modmailThreadId, text, replyCommandMessage, anonymous, user, executingMember));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CompletableFuture<Void> relayMessageToDm(Long modmailThreadId, String text, Message replyCommandMessage, boolean anonymous, Member targetMember, Member executingMember) {
|
||||
public CompletableFuture<Void> relayMessageToDm(Long modmailThreadId, String text, Message replyCommandMessage, boolean anonymous, User user, Member executingMember) {
|
||||
metricService.incrementCounter(MDOMAIL_THREAD_MESSAGE_SENT);
|
||||
ModMailThread modMailThread = modMailThreadManagementService.getById(modmailThreadId);
|
||||
FullUserInServer fullThreadUser = FullUserInServer
|
||||
.builder()
|
||||
.aUserInAServer(modMailThread.getUser())
|
||||
.member(targetMember)
|
||||
.build();
|
||||
List<String> imageUrls = replyCommandMessage
|
||||
.getAttachments()
|
||||
.stream()
|
||||
@@ -637,7 +652,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
.remainingAttachments(otherAttachments)
|
||||
.attachedImageUrls(imageUrls)
|
||||
.anonymous(anonymous)
|
||||
.threadUser(fullThreadUser);
|
||||
.userDisplay(UserDisplay.fromUser(user));
|
||||
if(anonymous) {
|
||||
log.debug("Message is sent anonymous.");
|
||||
modMailModeratorReplyModelBuilder.moderator(memberService.getBotInGuild(modMailThread.getServer()));
|
||||
@@ -646,7 +661,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
}
|
||||
ModMailModeratorReplyModel modMailUserReplyModel = modMailModeratorReplyModelBuilder.build();
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(MODMAIL_STAFF_MESSAGE_TEMPLATE_KEY, modMailUserReplyModel, modMailThread.getServer().getId());
|
||||
CompletableFuture<Message> future = messageService.sendMessageToSendToUser(targetMember.getUser(), messageToSend);
|
||||
CompletableFuture<Message> future = messageService.sendMessageToSendToUser(user, messageToSend);
|
||||
CompletableFuture<Message> sameThreadMessageFuture;
|
||||
if(featureModeService.featureModeActive(ModMailFeatureDefinition.MOD_MAIL, modMailThread.getServer(), ModMailMode.SEPARATE_MESSAGE)) {
|
||||
sameThreadMessageFuture = channelService.sendMessageEmbedToSendToAChannel(messageToSend, modMailThread.getChannel()).get(0);
|
||||
@@ -678,24 +693,24 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
log.info("Archiving thread {} for modmail thread closing.", modMailThread.getChannel().getId());
|
||||
return loadUserAndSendClosingHeader(modMailThread, closingConfig)
|
||||
.thenCompose(unused -> channelService.archiveThreadChannel(threadChannel))
|
||||
.thenCompose(unused -> memberService.getMemberInServerAsync(serverId, userId))
|
||||
.thenAccept(member -> self.afterSuccessfulLog(modMailThreadId, closingConfig.getNotifyUser(), member, undoActions));
|
||||
.thenCompose(unused -> userService.retrieveUserForId(userId))
|
||||
.thenCompose(user -> self.afterSuccessfulLog(modMailThreadId, closingConfig.getNotifyUser(), user, undoActions));
|
||||
} else {
|
||||
if(closingConfig.getLog()) {
|
||||
if(!modMailMessages.isEmpty()) {
|
||||
return modMailMessageService.loadModMailMessages(modMailMessages)
|
||||
.thenAccept(loadedModmailThreadMessages -> self.logMessagesToModMailLog(closingConfig, modMailThreadId, undoActions, loadedModmailThreadMessages, serverId, userId));
|
||||
.thenCompose(loadedModmailThreadMessages -> self.logMessagesToModMailLog(closingConfig, modMailThreadId, undoActions, loadedModmailThreadMessages, serverId, userId));
|
||||
} else {
|
||||
log.info("Modmail thread {} in server {} has no messages. Only logging header.", modMailThreadId, serverId);
|
||||
return loadUserAndSendClosingHeader(modMailThread, closingConfig)
|
||||
.thenAccept(unused -> memberService.getMemberInServerAsync(modMailThread.getUser()).thenCompose(member ->
|
||||
self.afterSuccessfulLog(modMailThreadId, closingConfig.getNotifyUser(), member, undoActions)
|
||||
.thenCompose(unused -> userService.retrieveUserForId(modMailThread.getUser().getUserReference().getId()).thenCompose(user ->
|
||||
self.afterSuccessfulLog(modMailThreadId, closingConfig.getNotifyUser(), user, undoActions)
|
||||
));
|
||||
}
|
||||
} else {
|
||||
log.debug("Not logging modmail thread {}.", modMailThreadId);
|
||||
return memberService.getMemberInServerAsync(modMailThread.getUser()).thenCompose(member ->
|
||||
self.afterSuccessfulLog(modMailThreadId, closingConfig.getNotifyUser(), member, undoActions)
|
||||
return userService.retrieveUserForId(modMailThread.getUser().getUserReference().getId()).thenCompose(user ->
|
||||
self.afterSuccessfulLog(modMailThreadId, closingConfig.getNotifyUser(), user, undoActions)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -735,8 +750,8 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
undoActions.add(UndoActionInstance.getMessageDeleteAction(message.getGuild().getIdLong(), message.getChannel().getIdLong(), message.getIdLong()));
|
||||
}
|
||||
});
|
||||
return memberService.getMemberInServerAsync(serverId, userId).thenCompose(member ->
|
||||
self.afterSuccessfulLog(modMailThreadId, closingContext.getNotifyUser(), member, undoActions)
|
||||
return userService.retrieveUserForId(userId).thenCompose(user ->
|
||||
self.afterSuccessfulLog(modMailThreadId, closingContext.getNotifyUser(), user, undoActions)
|
||||
).exceptionally(throwable -> {
|
||||
log.warn("Failed to retrieve member for closing the modmail thread. Closing without member information.", throwable);
|
||||
self.afterSuccessfulLog(modMailThreadId, false, null, undoActions);
|
||||
@@ -755,12 +770,12 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
* @param modMailThreadId The ID of the {@link ModMailThread} which is being closed.
|
||||
* @param notifyUser Whether the user should be notified
|
||||
* @param undoActions The list of {@link UndoActionInstance} to execute in case of exceptions
|
||||
* @param modMailThreaduser The {@link Member member} for which the {@link ModMailThread thread} was for
|
||||
* @param modMailThreaduser The {@link User member} for which the {@link ModMailThread thread} was for
|
||||
* @throws ModMailThreadNotFoundException in case the {@link ModMailThread} is not found by the ID
|
||||
* @return A {@link CompletableFuture future} which completes after the messages have been logged
|
||||
*/
|
||||
@Transactional
|
||||
public CompletableFuture<Void> afterSuccessfulLog(Long modMailThreadId, Boolean notifyUser, Member modMailThreaduser, List<UndoActionInstance> undoActions) {
|
||||
public CompletableFuture<Void> afterSuccessfulLog(Long modMailThreadId, Boolean notifyUser, User modMailThreaduser, List<UndoActionInstance> undoActions) {
|
||||
log.debug("Mod mail logging for thread {} has completed. Starting post logging activities.", modMailThreadId);
|
||||
Optional<ModMailThread> modMailThreadOpt = modMailThreadManagementService.getByIdOptional(modMailThreadId);
|
||||
if(modMailThreadOpt.isPresent()) {
|
||||
@@ -770,7 +785,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
HashMap<String, String> closingMessage = new HashMap<>();
|
||||
String defaultValue = templateService.renderSimpleTemplate("modmail_closing_user_message_description");
|
||||
closingMessage.put("closingMessage", configService.getStringValue(MOD_MAIL_CLOSING_TEXT_SYSTEM_CONFIG_KEY, modMailThread.getServer().getId(), defaultValue));
|
||||
return messageService.sendEmbedToUser(modMailThreaduser.getUser(), "modmail_closing_user_message", closingMessage).thenAccept(message ->
|
||||
return messageService.sendEmbedToUser(modMailThreaduser, "modmail_closing_user_message", closingMessage).thenCompose(message ->
|
||||
self.deleteChannelAndClose(modMailThreadId, undoActions)
|
||||
).exceptionally(throwable -> {
|
||||
self.deleteChannelAndClose(modMailThreadId, undoActions);
|
||||
@@ -914,7 +929,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
.build();
|
||||
Long modmailThreadId = modMailThread.getId();
|
||||
return userService.retrieveUserForId(modMailThread.getUser().getUserReference().getId()).thenApply(user -> {
|
||||
headerModel.setUser(user);
|
||||
headerModel.setUser(UserDisplay.fromUser(user));
|
||||
return self.sendClosingHeader(headerModel, modmailThreadId).get(0);
|
||||
}).thenCompose(Function.identity());
|
||||
}
|
||||
@@ -1001,6 +1016,28 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CompletableFuture<Void> banUserFromAppealServer(Long mainServerId, Long userId, String reason) {
|
||||
Long configuredAppealServerId = configService.getLongValue(ModMailFeatureConfig.MOD_MAIL_APPEAL_SERVER, mainServerId);
|
||||
Guild appealGuild = guildService.getGuildById(configuredAppealServerId);
|
||||
return banService.banUser(appealGuild, ServerUser.fromId(configuredAppealServerId, userId), Duration.ZERO, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> rejectAppeal(ModMailThread modMailThread, String reason, Member memberPerforming) {
|
||||
ClosingContext context = ClosingContext
|
||||
.builder()
|
||||
.closingMember(memberPerforming)
|
||||
.notifyUser(true)
|
||||
.log(true)
|
||||
.note(reason)
|
||||
.build();
|
||||
Long mainServerId = modMailThread.getServer().getId();
|
||||
Long userToBanId = modMailThread.getUser().getUserReference().getId();
|
||||
return closeModMailThread(modMailThread, context, new ArrayList<>())
|
||||
.thenCompose((nul) -> self.banUserFromAppealServer(mainServerId, userToBanId , reason));
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
metricService.registerCounter(MODMAIL_THREAD_CREATED_COUNTER, "Mod mail threads created");
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.26.xsd" >
|
||||
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user