added unmute command to end all mutes, and remove the current one, this also cancels all running jobs, so we need to store the triggers

enabled voice state cache, as its necessary for voice state operations
kick user out of voice chat, if they are in any
changed unmute date to the the effective date of the unmute, instead of the planned date
added possibility to stop a job via the trigger key
This commit is contained in:
Sheldan
2020-04-25 11:10:59 +02:00
parent d4eeb2dadb
commit 7ac2f2ce08
14 changed files with 174 additions and 16 deletions

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.moderation.commands;
package dev.sheldan.abstracto.moderation.commands.mute;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.moderation.commands;
package dev.sheldan.abstracto.moderation.commands.mute;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;

View File

@@ -0,0 +1,64 @@
package dev.sheldan.abstracto.moderation.commands.mute;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.moderation.Moderation;
import dev.sheldan.abstracto.moderation.config.ModerationFeatures;
import dev.sheldan.abstracto.moderation.exception.MuteException;
import dev.sheldan.abstracto.moderation.models.database.Mute;
import dev.sheldan.abstracto.moderation.service.MuteService;
import dev.sheldan.abstracto.moderation.service.management.MuteManagementService;
import net.dv8tion.jda.api.entities.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class UnMute extends AbstractConditionableCommand {
@Autowired
private MuteService muteService;
@Autowired
private MuteManagementService muteManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
Member member = (Member) parameters.get(0);
Mute mute = muteManagementService.getAMuteOf(member);
if(mute == null) {
throw new MuteException("User has no active mutes");
}
muteService.unmuteUser(mute);
muteService.cancelUnmuteJob(mute);
muteService.completelyUnmuteUser(member);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("user").type(Member.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(false).build();
return CommandConfiguration.builder()
.name("unMute")
.module(Moderation.MODERATION)
.templated(false)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public String getFeature() {
return ModerationFeatures.MUTING;
}
}

View File

@@ -5,7 +5,11 @@ import dev.sheldan.abstracto.moderation.models.database.Mute;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface MuteRepository extends JpaRepository<Mute, Long> {
boolean existsByMutedUserAndMuteEndedFalse(AUserInAServer userInAServer);
Mute findTopByMutedUserAndMuteEndedFalse(AUserInAServer userInAServer);
List<Mute> findAllByMutedUserAndMuteEndedFalseOrderByIdDesc(AUserInAServer aUserInAServer);
}

View File

@@ -32,6 +32,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -112,8 +113,9 @@ public class MuteServiceBean implements MuteService {
@Override
public Mute muteUser(FullUser userBeingMuted, FullUser userMuting, String reason, Instant unmuteDate, Message message) {
Member memberBeingMuted = userBeingMuted.getMember();
log.info("User {} mutes {} until {}",
userBeingMuted.getMember().getIdLong(), userMuting.getMember().getIdLong(), unmuteDate);
memberBeingMuted.getIdLong(), userMuting.getMember().getIdLong(), unmuteDate);
if(message != null) {
log.trace("because of message {} in channel {} in server {}", message.getId(), message.getChannel().getId(), message.getGuild().getId());
} else {
@@ -136,14 +138,19 @@ public class MuteServiceBean implements MuteService {
.build();
}
Mute mute = muteManagementService.createMute(userInServerBeingMuted, userMuting.getAUserInAServer(), reason, unmuteDate, origin);
Guild guild = memberBeingMuted.getGuild();
if(memberBeingMuted.getVoiceState() != null && memberBeingMuted.getVoiceState().getChannel() != null) {
guild.kickVoiceMember(memberBeingMuted).queue();
}
log.trace("Notifying the user about the mute.");
MuteNotification muteNotification = MuteNotification.builder().mute(mute).serverName(userBeingMuted.getMember().getGuild().getName()).build();
MuteNotification muteNotification = MuteNotification.builder().mute(mute).serverName(guild.getName()).build();
String muteNotificationMessage = templateService.renderTemplate(MUTE_NOTIFICATION_TEMPLATE, muteNotification);
TextChannel textChannel = message != null ? message.getTextChannel() : null;
messageService.sendMessageToUser(userBeingMuted.getMember().getUser(), muteNotificationMessage, textChannel);
messageService.sendMessageToUser(memberBeingMuted.getUser(), muteNotificationMessage, textChannel);
startUnmuteJobFor(unmuteDate, mute);
String triggerKey = startUnmuteJobFor(unmuteDate, mute);
mute.setTriggerKey(triggerKey);
muteManagementService.saveMute(mute);
return mute;
}
@@ -154,7 +161,7 @@ public class MuteServiceBean implements MuteService {
}
@Override
public void startUnmuteJobFor(Instant unmuteDate, Mute mute) {
public String startUnmuteJobFor(Instant unmuteDate, Mute mute) {
Duration muteDuration = Duration.between(Instant.now(), unmuteDate);
if(muteDuration.getSeconds() < 60) {
log.trace("Directly scheduling the unmute, because it was below the threshold.");
@@ -166,11 +173,19 @@ public class MuteServiceBean implements MuteService {
log.error("Failed to remind immediately.", exception);
}
}, muteDuration.toNanos(), TimeUnit.NANOSECONDS);
return null;
} else {
log.trace("Starting scheduled job to execute unmute.");
JobDataMap parameters = new JobDataMap();
parameters.putAsString("muteId", mute.getId());
schedulerService.executeJobWithParametersOnce("unMuteJob", "moderation", parameters, Date.from(unmuteDate));
return schedulerService.executeJobWithParametersOnce("unMuteJob", "moderation", parameters, Date.from(unmuteDate));
}
}
@Override
public void cancelUnmuteJob(Mute mute) {
if(mute.getTriggerKey() != null) {
schedulerService.stopTrigger(mute.getTriggerKey());
}
}
@@ -198,6 +213,15 @@ public class MuteServiceBean implements MuteService {
@Transactional
public void unmuteUser(Mute mute) {
AServer mutingServer = mute.getMutingServer();
Mute updatedMute = muteManagementService.findMute(mute.getId());
// we do not store any reference to the instant unmutes (<=60sec), so we cannot cancel it
// but if the person gets unmuted immediately, via command, this might still execute of the instant unmute
// so we need to load the mute, and check if the mute was unmuted already, because the mute object we have at
// hand was loaded earlier, and does not reflect the true state
if(updatedMute.getMuteEnded()) {
log.info("Mute {} has ended already, {} does not need to be unmuted anymore.", mute.getId(), mute.getMutedUser().getUserReference().getId());
return;
}
log.info("Unmuting {} in server {}", mutingServer.getId(), mute.getMutedUser().getUserReference().getId());
MuteRole muteRole = muteRoleManagementService.retrieveMuteRoleForServer(mutingServer);
log.trace("Using the mute role {} mapping to role {}", muteRole.getId(), muteRole.getRole().getId());
@@ -227,4 +251,19 @@ public class MuteServiceBean implements MuteService {
Mute mute = muteManagementService.findMute(muteId);
unmuteUser(mute);
}
@Override
public void completelyUnmuteUser(AUserInAServer aUserInAServer) {
List<Mute> allMutesOfUser = muteManagementService.getAllMutesOf(aUserInAServer);
allMutesOfUser.forEach(mute -> {
mute.setMuteEnded(true);
cancelUnmuteJob(mute);
muteManagementService.saveMute(mute);
});
}
@Override
public void completelyUnmuteUser(Member member) {
completelyUnmuteUser(userManagementService.loadUser(member));
}
}

View File

@@ -2,13 +2,16 @@ package dev.sheldan.abstracto.moderation.service.management;
import dev.sheldan.abstracto.core.models.AServerAChannelMessage;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.UserManagementService;
import dev.sheldan.abstracto.moderation.models.database.Mute;
import dev.sheldan.abstracto.moderation.repository.MuteRepository;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.List;
@Component
@Slf4j
@@ -17,6 +20,9 @@ public class MuteManagementServiceBean implements MuteManagementService {
@Autowired
private MuteRepository muteRepository;
@Autowired
private UserManagementService userManagementService;
@Override
public Mute createMute(AUserInAServer aUserInAServer, AUserInAServer mutingUser, String reason, Instant unmuteDate, AServerAChannelMessage origin) {
log.trace("Creating mute for user {} executed by user {} in server {}, user will be unmuted at {}",
@@ -53,5 +59,20 @@ public class MuteManagementServiceBean implements MuteManagementService {
return muteRepository.existsByMutedUserAndMuteEndedFalse(userInAServer);
}
@Override
public Mute getAMuteOf(AUserInAServer userInAServer) {
return muteRepository.findTopByMutedUserAndMuteEndedFalse(userInAServer);
}
@Override
public Mute getAMuteOf(Member userInAServer) {
return getAMuteOf(userManagementService.loadUser(userInAServer));
}
@Override
public List<Mute> getAllMutesOf(AUserInAServer aUserInAServer) {
return muteRepository.findAllByMutedUserAndMuteEndedFalseOrderByIdDesc(aUserInAServer);
}
}

View File

@@ -51,5 +51,5 @@
"footer": {
"text": "Mute #${mute.id}"
},
"timeStamp": "${mute.muteTargetDate}"
"timeStamp": "${unmuteDate}"
}

View File

@@ -48,5 +48,7 @@ public class Mute {
@JoinColumn(name = "mutingChannel")
private AChannel mutingChannel;
private String triggerKey;
}

View File

@@ -10,6 +10,7 @@ import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
import java.time.Duration;
import java.time.Instant;
@Getter
@@ -22,9 +23,17 @@ public class UnMuteLog extends ServerContext {
private Mute mute;
public Duration getMuteDuration() {
return Duration.between(mute.getMuteDate(), Instant.now());
}
public Duration getPlannedMuteDuration() {
return Duration.between(mute.getMuteDate(), mute.getMuteTargetDate());
}
public Instant getUnmuteDate() {
return Instant.now();
}
public String getMessageUrl() {
return MessageUtils.buildMessageUrl(this.mute.getMutingServer().getId() ,this.getMute().getMutingChannel().getId(), this.mute.getMessageId());
}

View File

@@ -15,7 +15,10 @@ public interface MuteService {
Mute muteUser(FullUser userToMute, FullUser userMuting, String reason, Instant unmuteDate, Message message);
void applyMuteRole(AUserInAServer aUserInAServer);
void muteMemberWithLog(Member memberToMute, Member memberMuting, String reason, Instant unmuteDate, MuteLog log, Message message);
void startUnmuteJobFor(Instant unmuteDate, Mute mute);
String startUnmuteJobFor(Instant unmuteDate, Mute mute);
void cancelUnmuteJob(Mute mute);
void unmuteUser(Mute mute);
void endMute(Long muteId);
void completelyUnmuteUser(AUserInAServer aUserInAServer);
void completelyUnmuteUser(Member member);
}

View File

@@ -3,12 +3,17 @@ package dev.sheldan.abstracto.moderation.service.management;
import dev.sheldan.abstracto.core.models.AServerAChannelMessage;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.moderation.models.database.Mute;
import net.dv8tion.jda.api.entities.Member;
import java.time.Instant;
import java.util.List;
public interface MuteManagementService {
Mute createMute(AUserInAServer aUserInAServer, AUserInAServer mutingUser, String reason, Instant unmuteDate, AServerAChannelMessage creation);
Mute findMute(Long muteId);
Mute saveMute(Mute mute);
boolean hasActiveMute(AUserInAServer userInAServer);
Mute getAMuteOf(AUserInAServer userInAServer);
Mute getAMuteOf(Member userInAServer);
List<Mute> getAllMutesOf(AUserInAServer aUserInAServer);
}