added Arole to command received handler in order to handle it

restructured channel service calls a little bit
moved dm sending to message service
fixed log level configuration
added full user dto, to be used as a combination of a AUserInServer and Member, for operations which need both, to avoid converting and reloading the user
added mute command and mute role command
added mute table
added mute role table
added job to automatically unmute people at the given time period
restructured warn service
removed simple message log for warnings
added method to templating to support formatting instants
This commit is contained in:
Sheldan
2020-04-24 18:02:05 +02:00
parent cf37d4adef
commit b41a596acd
44 changed files with 1032 additions and 69 deletions

View File

@@ -17,6 +17,12 @@
<artifactId>moderation-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.scheduling</groupId>
<artifactId>scheduling-int</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,64 @@
package dev.sheldan.abstracto.moderation.commands;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.moderation.Moderation;
import dev.sheldan.abstracto.moderation.config.ModerationFeatures;
import dev.sheldan.abstracto.moderation.models.template.commands.MuteLog;
import dev.sheldan.abstracto.moderation.service.MuteService;
import net.dv8tion.jda.api.entities.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@Component
public class Mute extends AbstractConditionableCommand {
@Autowired
private MuteService muteService;
@Override
public CommandResult execute(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
Member member = (Member) parameters.get(0);
Duration duration = (Duration) parameters.get(1);
String reason = (String) parameters.get(2);
MuteLog muteLogModel = (MuteLog) ContextConverter.fromCommandContext(commandContext, MuteLog.class);
muteLogModel.setMessage(commandContext.getMessage());
muteLogModel.setMutedUser(member);
muteLogModel.setMutingUser(commandContext.getAuthor());
muteService.muteMemberWithLog(member, commandContext.getAuthor(), reason, Instant.now().plus(duration), muteLogModel, commandContext.getMessage());
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("user").type(Member.class).build());
parameters.add(Parameter.builder().name("duration").type(Duration.class).build());
parameters.add(Parameter.builder().name("reason").type(String.class).optional(true).remainder(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(false).build();
return CommandConfiguration.builder()
.name("mute")
.module(Moderation.MODERATION)
.templated(false)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public String getFeature() {
return ModerationFeatures.MUTING;
}
}

View File

@@ -0,0 +1,51 @@
package dev.sheldan.abstracto.moderation.commands;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.moderation.Moderation;
import dev.sheldan.abstracto.moderation.config.ModerationFeatures;
import dev.sheldan.abstracto.moderation.service.management.MuteRoleManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class SetMuteRole extends AbstractConditionableCommand {
@Autowired
private MuteRoleManagementService muteRoleManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
ARole role = (ARole) commandContext.getParameters().getParameters().get(0);
muteRoleManagementService.setMuteRoleForServer(commandContext.getUserInitiatedContext().getServer(), role);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("roleId").type(ARole.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(false).build();
return CommandConfiguration.builder()
.name("setMuteRole")
.module(Moderation.MODERATION)
.templated(false)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public String getFeature() {
return ModerationFeatures.MUTING;
}
}

View File

@@ -43,7 +43,7 @@ public class Warn extends AbstractConditionableCommand {
warnLogModel.setMessage(commandContext.getMessage());
warnLogModel.setReason(reason);
warnLogModel.setWarningUser(commandContext.getAuthor());
warnService.warnUser(member, commandContext.getAuthor(), reason, warnLogModel);
warnService.warnUserWithLog(member, commandContext.getAuthor(), reason, warnLogModel, commandContext.getChannel());
return CommandResult.fromSuccess();
}

View File

@@ -4,4 +4,5 @@ public class ModerationFeatures {
public static String MODERATION = "moderation";
public static String WARNINGS = "warnings";
public static String LOGGING = "logging";
public static String MUTING = "mutes";
}

View File

@@ -0,0 +1,37 @@
package dev.sheldan.abstracto.moderation.job;
import dev.sheldan.abstracto.moderation.service.MuteService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;
@Slf4j
@DisallowConcurrentExecution
@Component
@PersistJobDataAfterExecution
public class UnMuteJob extends QuartzJobBean {
private Long muteId;
@Autowired
private MuteService muteService;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
log.info("Executing unmute job for mute {}", muteId);
muteService.endMute(muteId);
}
public Long getMuteId() {
return muteId;
}
public void setMuteId(Long muteId) {
this.muteId = muteId;
}
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.moderation.repository;
import dev.sheldan.abstracto.moderation.models.database.Mute;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface MuteRepository extends JpaRepository<Mute, Long> {
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.abstracto.moderation.repository;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.moderation.models.database.MuteRole;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface MuteRoleRepository extends JpaRepository<MuteRole, Long> {
MuteRole findByRoleServer(AServer server);
List<MuteRole> findAllByRoleServer(AServer server);
}

View File

@@ -0,0 +1,217 @@
package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.models.AServerAChannelMessage;
import dev.sheldan.abstracto.core.models.FullUser;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.UserManagementService;
import dev.sheldan.abstracto.moderation.exception.MuteException;
import dev.sheldan.abstracto.moderation.models.database.Mute;
import dev.sheldan.abstracto.moderation.models.database.MuteRole;
import dev.sheldan.abstracto.moderation.models.template.commands.MuteLog;
import dev.sheldan.abstracto.moderation.models.template.commands.MuteNotification;
import dev.sheldan.abstracto.moderation.models.template.commands.UnMuteLog;
import dev.sheldan.abstracto.moderation.service.management.MuteManagementService;
import dev.sheldan.abstracto.moderation.service.management.MuteRoleManagementService;
import dev.sheldan.abstracto.scheduling.service.SchedulerService;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.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.TextChannel;
import org.quartz.JobDataMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Component
@Slf4j
public class MuteServiceBean implements MuteService {
@Autowired
private MuteRoleManagementService muteRoleManagementService;
@Autowired
private RoleService roleService;
@Autowired
private UserManagementService userManagementService;
@Autowired
private SchedulerService schedulerService;
@Autowired
private MuteManagementService muteManagementService;
@Autowired
private TemplateService templateService;
@Autowired
private BotService botService;
@Autowired
private MessageService messageService;
@Autowired
private PostTargetService postTargetService;
@Autowired
private MuteService self;
@Autowired
private ChannelManagementService channelManagementService;
private static final String MUTE_LOG_TEMPLATE = "mute_log";
private static final String UNMUTE_LOG_TEMPLATE = "unmute_log";
private static final String MUTE_LOG_TARGET = "muteLog";
private static final String MUTE_NOTIFICATION_TEMPLATE = "mute_notification";
@Override
public Mute muteMember(Member memberToMute, Member mutingMember, String reason, Instant unmuteDate, Message message) {
FullUser mutedUser = FullUser
.builder()
.aUserInAServer(userManagementService.loadUser(memberToMute))
.member(memberToMute)
.build();
FullUser mutingUser = FullUser
.builder()
.aUserInAServer(userManagementService.loadUser(memberToMute))
.member(mutingMember)
.build();
return muteUser(mutedUser, mutingUser, reason, unmuteDate, message);
}
@Override
public Mute muteMember(AUserInAServer userBeingMuted, AUserInAServer userMuting, String reason, Instant unmuteDate, Message message) {
FullUser mutedUser = FullUser
.builder()
.aUserInAServer(userBeingMuted)
.member(botService.getMemberInServer(userBeingMuted))
.build();
FullUser mutingUser = FullUser
.builder()
.aUserInAServer(userMuting)
.member(botService.getMemberInServer(userMuting))
.build();
return muteUser(mutedUser, mutingUser, reason, unmuteDate, message);
}
@Override
public Mute muteUser(FullUser userBeingMuted, FullUser userMuting, String reason, Instant unmuteDate, Message message) {
log.info("User {} mutes {} until {}",
userBeingMuted.getMember().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 {
log.trace("This mute was not triggered by a message.");
}
if(!muteRoleManagementService.muteRoleForServerExists(userBeingMuted.getAUserInAServer().getServerReference())) {
log.error("Mute role for server {} has not been setup.", userBeingMuted.getAUserInAServer().getServerReference().getId());
throw new MuteException("Mute role for server has not been setup");
}
AUserInAServer userInServerBeingMuted = userBeingMuted.getAUserInAServer();
MuteRole muteRole = muteRoleManagementService.retrieveMuteRoleForServer(userInServerBeingMuted.getServerReference());
roleService.addRoleToUser(userInServerBeingMuted, muteRole.getRole());
AServerAChannelMessage origin = null;
if(message != null) {
AChannel channel = channelManagementService.loadChannel(message.getChannel().getIdLong());
origin = AServerAChannelMessage
.builder()
.channel(channel)
.server(channel.getServer())
.messageId(message.getIdLong())
.build();
}
Mute mute = muteManagementService.createMute(userInServerBeingMuted, userMuting.getAUserInAServer(), reason, unmuteDate, origin);
log.trace("Notifying the user about the mute.");
MuteNotification muteNotification = MuteNotification.builder().mute(mute).serverName(userBeingMuted.getMember().getGuild().getName()).build();
String muteNotificationMessage = templateService.renderTemplate(MUTE_NOTIFICATION_TEMPLATE, muteNotification);
TextChannel textChannel = message != null ? message.getTextChannel() : null;
messageService.sendMessageToUser(userBeingMuted.getMember().getUser(), muteNotificationMessage, textChannel);
startUnmuteJobFor(unmuteDate, mute);
return mute;
}
@Override
public void 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.");
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.schedule(() -> {
try {
self.unmuteUser(mute);
} catch (Exception exception) {
log.error("Failed to remind immediately.", exception);
}
}, muteDuration.toNanos(), TimeUnit.NANOSECONDS);
} 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));
}
}
@Override
public void muteMemberWithLog(Member memberToMute, Member memberMuting, String reason, Instant unmuteDate, MuteLog muteLog, Message message) {
log.trace("Muting member with sending a mute log");
Mute mute = muteMember(memberToMute, memberMuting, reason, unmuteDate, message);
muteLog.setMute(mute);
sendMuteLog(muteLog);
}
private void sendMuteLog(MuteLog muteLogModel) {
log.trace("Sending mute log to the mute posttarget");
MessageToSend message = templateService.renderEmbedTemplate(MUTE_LOG_TEMPLATE, muteLogModel);
postTargetService.sendEmbedInPostTarget(message, MUTE_LOG_TARGET, muteLogModel.getServer().getId());
}
private void sendUnmuteLog(UnMuteLog muteLogModel) {
log.trace("Sending unmute log to the mute posttarget");
MessageToSend message = templateService.renderEmbedTemplate(UNMUTE_LOG_TEMPLATE, muteLogModel);
postTargetService.sendEmbedInPostTarget(message, MUTE_LOG_TARGET, muteLogModel.getServer().getId());
}
@Override
@Transactional
public void unmuteUser(Mute mute) {
AServer mutingServer = mute.getMutingServer();
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());
roleService.removeRoleFromUser(mute.getMutedUser(), muteRole.getRole());
UnMuteLog unMuteLog = UnMuteLog
.builder()
.mute(mute)
.mutingUser(botService.getMemberInServer(mute.getMutingUser()))
.unMutedUser(botService.getMemberInServer(mute.getMutedUser()))
.guild(botService.getGuildById(mute.getMutingServer().getId()).orElseGet(null))
.server(mute.getMutingServer())
.build();
sendUnmuteLog(unMuteLog);
}
@Override
@Transactional
public void endMute(Long muteId) {
log.info("Unmuting the mute {}", muteId);
Mute mute = muteManagementService.findMute(muteId);
unmuteUser(mute);
}
}

View File

@@ -1,9 +1,8 @@
package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.exception.UserException;
import dev.sheldan.abstracto.core.models.FullUser;
import dev.sheldan.abstracto.core.models.context.ServerContext;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.moderation.models.template.commands.WarnLog;
import dev.sheldan.abstracto.moderation.models.template.commands.WarnNotification;
@@ -16,17 +15,10 @@ import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.JDA;
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.User;
import net.dv8tion.jda.api.entities.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Slf4j
@Component
public class WarnServiceBean implements WarnService {
@@ -50,56 +42,63 @@ public class WarnServiceBean implements WarnService {
@Autowired
private BotService botService;
@Autowired
private MessageService messageService;
private static final String WARN_LOG_TEMPLATE = "warn_log";
private static final String WARN_NOTIFICATION_TEMPLATE = "warn_notification";
@Override
public void warnUser(AUserInAServer warnedAUserInAServer, AUserInAServer warningAUserInAServer, String reason, WarnLog warnLog) {
AUser warningAUser = warningAUserInAServer.getUserReference();
AUser warnedAUser = warnedAUserInAServer.getUserReference();
AServer serverOfWarning = warnedAUserInAServer.getServerReference();
log.info("User {} is warning {} in server {} because of {}", warningAUser.getId(), warnedAUser.getId(), serverOfWarning.getId(), reason);
Warning warning = warnManagementService.createWarning(warnedAUserInAServer, warningAUserInAServer, reason);
JDA instance = botService.getInstance();
User userBeingWarned = instance.getUserById(warnedAUser.getId());
Optional<Guild> guildById = botService.getGuildById(serverOfWarning.getId());
String guildName = "<defaultName>";
if(guildById.isPresent()) {
guildName = guildById.get().getName();
}
warnLog.setWarning(warning);
this.sendWarnLog(warnLog);
WarnNotification warnNotification = WarnNotification.builder().warning(warning).serverName(guildName).build();
if(userBeingWarned != null) {
String warnLogMessage = templateService.renderTemplate(WARN_NOTIFICATION_TEMPLATE, warnNotification);
CompletableFuture<Message> messageFuture = new CompletableFuture<>();
public Warning warnUser(AUserInAServer warnedAUserInAServer, AUserInAServer warningAUserInAServer, String reason, TextChannel feedbackChannel) {
FullUser warnedUser = FullUser
.builder()
.aUserInAServer(warnedAUserInAServer)
.member(botService.getMemberInServer(warnedAUserInAServer))
.build();
// TODO the person executing this, is unaware that the message failed
userBeingWarned.openPrivateChannel().queue(privateChannel -> {
log.info("Messaging user {} about warn {}", warnedAUser.getId(), warning.getId());
privateChannel.sendMessage(warnLogMessage).queue(messageFuture::complete, messageFuture::completeExceptionally);
});
messageFuture.exceptionally(e -> {
log.warn("Failed to send message. ", e);
return null;
});
} else {
log.warn("Unable to find user {} in guild {} to warn.", warnedAUser.getId(), serverOfWarning.getId());
throw new UserException(String.format("Unable to find user %s.", warnedAUser.getId()));
}
FullUser warningUser = FullUser
.builder()
.aUserInAServer(warningAUserInAServer)
.member(botService.getMemberInServer(warningAUserInAServer))
.build();
return warnUser(warnedUser, warningUser, reason, feedbackChannel);
}
@Override
public void warnUser(Member warnedMember, Member warningMember, String reason, WarnLog warnLog) {
AUserInAServer warnedAUser = userManagementService.loadUser(warnedMember);
AUserInAServer warningAUser = userManagementService.loadUser(warningMember);
this.warnUser(warnedAUser, warningAUser, reason, warnLog);
public Warning warnUser(Member warnedMember, Member warningMember, String reason, TextChannel feedbackChannel) {
FullUser warnedUser = FullUser
.builder()
.aUserInAServer(userManagementService.loadUser(warnedMember))
.member(warnedMember)
.build();
FullUser warningUser = FullUser
.builder()
.aUserInAServer(userManagementService.loadUser(warningMember))
.member(warningMember)
.build();
return warnUser(warnedUser, warningUser, reason, feedbackChannel);
}
@Override
public Warning warnUser(FullUser warnedMember, FullUser warningMember, String reason, TextChannel feedbackChannel) {
Guild guild = warnedMember.getMember().getGuild();
log.info("User {} is warning {} in server {} because of {}", warnedMember.getMember().getId(), warningMember.getMember().getId(), guild.getIdLong(), reason);
Warning warning = warnManagementService.createWarning(warnedMember.getAUserInAServer(), warningMember.getAUserInAServer(), reason);
WarnNotification warnNotification = WarnNotification.builder().warning(warning).serverName(guild.getName()).build();
String warnNotificationMessage = templateService.renderTemplate(WARN_NOTIFICATION_TEMPLATE, warnNotification);
messageService.sendMessageToUser(warnedMember.getMember().getUser(), warnNotificationMessage, feedbackChannel);
return warning;
}
@Override
public void warnUserWithLog(Member warnedMember, Member warningMember, String reason, WarnLog warnLog, TextChannel feedbackChannel) {
Warning warning = warnUser(warnedMember, warningMember, reason, feedbackChannel);
warnLog.setWarning(warning);
this.sendWarnLog(warnLog);
}
private void sendWarnLog(ServerContext warnLogModel) {
String warnLogMessage = templateService.renderTemplate(WARN_LOG_TEMPLATE, warnLogModel);
postTargetService.sendTextInPostTarget(warnLogMessage, WARN_LOG_TARGET, warnLogModel.getServer().getId());
MessageToSend message = templateService.renderEmbedTemplate("warn_log", warnLogModel);
postTargetService.sendEmbedInPostTarget(message, WARN_LOG_TARGET, warnLogModel.getServer().getId());
}

View File

@@ -0,0 +1,43 @@
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 dev.sheldan.abstracto.moderation.repository.MuteRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Instant;
@Component
@Slf4j
public class MuteManagementServiceBean implements MuteManagementService {
@Autowired
private MuteRepository muteRepository;
@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 {}",
aUserInAServer.getUserReference().getId(), mutingUser.getUserReference().getId(), aUserInAServer.getServerReference().getId(), unmuteDate);
Mute mute = Mute
.builder()
.muteDate(Instant.now())
.mutedUser(aUserInAServer)
.mutingUser(mutingUser)
.muteTargetDate(unmuteDate)
.mutingServer(aUserInAServer.getServerReference())
.mutingChannel(origin.getChannel())
.messageId(origin.getMessageId())
.reason(reason)
.build();
muteRepository.save(mute);
return mute;
}
@Override
public Mute findMute(Long muteId) {
return muteRepository.getOne(muteId);
}
}

View File

@@ -0,0 +1,59 @@
package dev.sheldan.abstracto.moderation.service.management;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.moderation.models.database.MuteRole;
import dev.sheldan.abstracto.moderation.repository.MuteRoleRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@Slf4j
public class MuteRoleManagementServiceBean implements MuteRoleManagementService {
@Autowired
private MuteRoleRepository muteRoleRepository;
@Override
public MuteRole retrieveMuteRoleForServer(AServer server) {
return muteRoleRepository.findByRoleServer(server);
}
@Override
public MuteRole createMuteRoleForServer(AServer server, ARole role) {
log.trace("Creating mute role for server {} to be role {}", server.getId(), role.getId());
MuteRole muteRole = MuteRole
.builder()
.role(role)
.roleServer(server)
.build();
muteRoleRepository.save(muteRole);
return muteRole;
}
@Override
public List<MuteRole> retrieveMuteRolesForServer(AServer server) {
return muteRoleRepository.findAllByRoleServer(server);
}
@Override
public MuteRole setMuteRoleForServer(AServer server, ARole role) {
log.info("Setting muted role for server {} to role {}", server.getId(), role.getId());
MuteRole existing = retrieveMuteRoleForServer(server);
if(existing == null) {
return createMuteRoleForServer(server, role);
} else {
log.trace("Updating mute role for server {} to be role {} instead.", server.getId(), role.getId());
existing.setRole(role);
return existing;
}
}
@Override
public boolean muteRoleForServerExists(AServer server) {
return retrieveMuteRoleForServer(server) != null;
}
}

View File

@@ -1,4 +1,12 @@
abstracto.postTargets.moderation=joinLog,leaveLog,warnLog,kickLog,banLog,editLog,deleteLog
abstracto.postTargets.moderation=joinLog,leaveLog,warnLog,kickLog,banLog,editLog,deleteLog,muteLog
abstracto.features.moderation=false
abstracto.features.warnings=false
abstracto.features.logging=true
abstracto.features.logging=true
abstracto.features.mutes=true
abstracto.scheduling.jobs.unMuteJob.name=unMuteJob
abstracto.scheduling.jobs.unMuteJob.group=moderation
abstracto.scheduling.jobs.unMuteJob.clazz=dev.sheldan.abstracto.moderation.job.UnMuteJob
abstracto.scheduling.jobs.unMuteJob.standAlone=false
abstracto.scheduling.jobs.unMuteJob.active=true
abstracto.scheduling.jobs.unMuteJob.recovery=false

View File

@@ -0,0 +1,47 @@
{
"author": {
"name": "${mutedUser.effectiveName}",
"avatar": "${mutedUser.user.effectiveAvatarUrl}"
},
"title": {
"title": "User has been muted"
},
"color" : {
"r": 200,
"g": 0,
"b": 255
},
"fields": [
{
"name": "Muted User",
"value": "${mutedUser.effectiveName} ${mutedUser.asMention} (${mutedUser.idLong?c})"
},
{
"name": "Muted by",
"value": "${mutingUser.effectiveName} ${mutingUser.asMention} (${mutingUser.idLong?c})"
},
{
"name": "Location of the mute",
"value": "[${messageChannel.name}](${message.jumpUrl})"
},
{
"name": "Reason",
"value": "${mute.reason}"
},
{
"name": "Muted from",
"value": "${formatInstant(mute.muteDate, "yyyy-MM-dd HH:mm:ss")}"
},
{
"name": "Muted for",
"value": "${fmtDuration(muteDuration)}"
},
{
"name": "Muted until",
"value": "${formatInstant(mute.muteTargetDate, "yyyy-MM-dd HH:mm:ss")}"
}
],
"footer": {
"text": "Mute #${mute.id}"
}
}

View File

@@ -0,0 +1 @@
You were muted on the server ${serverName} for the following reason: ${mute.reason}.

View File

@@ -0,0 +1,43 @@
{
"author": {
"name": "${unMutedUser.effectiveName}",
"avatar": "${unMutedUser.user.effectiveAvatarUrl}"
},
"title": {
"title": "User has been unmuted"
},
"color" : {
"r": 200,
"g": 0,
"b": 255
},
"fields": [
{
"name": "Unmuted User",
"value": "${unMutedUser.effectiveName} ${unMutedUser.asMention} (${unMutedUser.idLong?c})"
},
{
"name": "Muted by",
"value": "${mutingUser.effectiveName} ${mutingUser.asMention} (${mutingUser.idLong?c})"
},
{
"name": "Location of the mute",
"value": "[Link](${messageUrl})"
},
{
"name": "Muted since",
"value": "${formatInstant(mute.muteDate, "yyyy-MM-dd HH:mm:ss")}"
},
{
"name": "Muted for",
"value": "${fmtDuration(muteDuration)}"
},
{
"name": "Reason",
"value": "${mute.reason}"
}
],
"footer": {
"text": "Mute #${mute.id}"
}
}

View File

@@ -1 +0,0 @@
User ${warnedUser.effectiveName} (${warnedUser.asMention}) has been warned with reason ${reason}.

View File

@@ -0,0 +1,11 @@
package dev.sheldan.abstracto.moderation.exception;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
public class MuteException extends AbstractoRunTimeException {
public MuteException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,50 @@
package dev.sheldan.abstracto.moderation.models.database;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
import javax.persistence.*;
import java.time.Instant;
@Entity
@Table(name="mute")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class Mute {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "mutedUser", nullable = false)
private AUserInAServer mutedUser;
@ManyToOne
@JoinColumn(name = "mutingUser", nullable = false)
private AUserInAServer mutingUser;
private String reason;
private Instant muteDate;
private Instant muteTargetDate;
@Column
private Long messageId;
@ManyToOne
@JoinColumn(name = "mutingServer", nullable = false)
private AServer mutingServer;
@ManyToOne
@JoinColumn(name = "mutingChannel")
private AChannel mutingChannel;
}

View File

@@ -0,0 +1,43 @@
package dev.sheldan.abstracto.moderation.models.database;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import lombok.*;
import javax.persistence.*;
/**
* Represents a role to be used for muting users on a certain server
*/
@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "mute_role")
@Getter
@Setter
public class MuteRole {
/**
* The abstracto unique id of this mute role.
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* Reference to the {@link AServer} at which this role is used as an mute role.
*/
@ManyToOne(fetch = FetchType.LAZY)
@Getter
@Setter
@JoinColumn(name = "server_id", nullable = false)
private AServer roleServer;
/**
* Reference to the actual {@link ARole} being used to mute.
*/
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "role_id", nullable = false)
private ARole role;
}

View File

@@ -6,7 +6,9 @@ import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
@Getter @SuperBuilder @Setter
@Getter
@SuperBuilder
@Setter
public class BanIdLog extends UserInitiatedServerContext {
private String reason;
private Member banningUser;

View File

@@ -6,7 +6,9 @@ import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
@Getter @SuperBuilder @Setter
@Getter
@SuperBuilder
@Setter
public class BanLog extends UserInitiatedServerContext {
private String reason;

View File

@@ -6,7 +6,9 @@ import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
@Getter @SuperBuilder @Setter
@Getter
@SuperBuilder
@Setter
public class KickLogModel extends UserInitiatedServerContext {
private String reason;
private Member kickingUser;

View File

@@ -0,0 +1,27 @@
package dev.sheldan.abstracto.moderation.models.template.commands;
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
import dev.sheldan.abstracto.moderation.models.database.Mute;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import java.time.Duration;
@Getter
@SuperBuilder
@Setter
public class MuteLog extends UserInitiatedServerContext {
private Member mutedUser;
private Member mutingUser;
private Message message;
private Mute mute;
public Duration getMuteDuration() {
return Duration.between(mute.getMuteDate(), mute.getMuteTargetDate());
}
}

View File

@@ -0,0 +1,12 @@
package dev.sheldan.abstracto.moderation.models.template.commands;
import dev.sheldan.abstracto.moderation.models.database.Mute;
import lombok.Builder;
import lombok.Value;
@Value
@Builder
public class MuteNotification {
private Mute mute;
private String serverName;
}

View File

@@ -0,0 +1,31 @@
package dev.sheldan.abstracto.moderation.models.template.commands;
import dev.sheldan.abstracto.core.models.context.ServerContext;
import dev.sheldan.abstracto.core.utils.MessageUtils;
import dev.sheldan.abstracto.moderation.models.database.Mute;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
import java.time.Duration;
@Getter
@SuperBuilder
@Setter
@NoArgsConstructor
public class UnMuteLog extends ServerContext {
private Member unMutedUser;
private Member mutingUser;
private Mute mute;
public Duration getMuteDuration() {
return Duration.between(mute.getMuteDate(), mute.getMuteTargetDate());
}
public String getMessageUrl() {
return MessageUtils.buildMessageUrl(this.mute.getMutingServer().getId() ,this.getMute().getMutingChannel().getId(), this.mute.getMessageId());
}
}

View File

@@ -8,8 +8,9 @@ import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
@Getter @SuperBuilder @Setter
@Getter
@SuperBuilder
@Setter
public class WarnLog extends UserInitiatedServerContext {
private String reason;

View File

@@ -4,7 +4,8 @@ import dev.sheldan.abstracto.moderation.models.database.Warning;
import lombok.Builder;
import lombok.Value;
@Value @Builder
@Value
@Builder
public class WarnNotification {
private Warning warning;
private String serverName;

View File

@@ -0,0 +1,20 @@
package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.models.FullUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.moderation.models.database.Mute;
import dev.sheldan.abstracto.moderation.models.template.commands.MuteLog;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import java.time.Instant;
public interface MuteService {
Mute muteMember(Member memberToMute, Member userMuting, String reason, Instant unmuteDate, Message message);
Mute muteMember(AUserInAServer member, AUserInAServer userMuting, String reason, Instant unmuteDate, Message message);
Mute muteUser(FullUser userToMute, FullUser userMuting, String reason, Instant unmuteDate, Message message);
void muteMemberWithLog(Member memberToMute, Member memberMuting, String reason, Instant unmuteDate, MuteLog log, Message message);
void startUnmuteJobFor(Instant unmuteDate, Mute mute);
void unmuteUser(Mute mute);
void endMute(Long muteId);
}

View File

@@ -1,11 +1,16 @@
package dev.sheldan.abstracto.moderation.service;
import dev.sheldan.abstracto.core.models.FullUser;
import dev.sheldan.abstracto.moderation.models.database.Warning;
import dev.sheldan.abstracto.moderation.models.template.commands.WarnLog;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.TextChannel;
public interface WarnService {
void warnUser(AUserInAServer warnedAUser, AUserInAServer warningAUser, String reason, WarnLog warnLog);
void warnUser(Member warnedUser, Member warningUser, String reason, WarnLog warnLog);
Warning warnUser(AUserInAServer warnedAUser, AUserInAServer warningAUser, String reason, TextChannel feedbackChannel);
Warning warnUser(Member warnedMember, Member warningMember, String reason, TextChannel feedbackChannel);
Warning warnUser(FullUser warnedUser, FullUser warningUser, String reason, TextChannel feedbackChannel);
void warnUserWithLog(Member warnedMember, Member warningMember, String reason, WarnLog warnLog, TextChannel feedbackChannel);
}

View File

@@ -0,0 +1,12 @@
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 java.time.Instant;
public interface MuteManagementService {
Mute createMute(AUserInAServer aUserInAServer, AUserInAServer mutingUser, String reason, Instant unmuteDate, AServerAChannelMessage creation);
Mute findMute(Long muteId);
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.moderation.service.management;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.moderation.models.database.MuteRole;
import java.util.List;
public interface MuteRoleManagementService {
MuteRole retrieveMuteRoleForServer(AServer server);
MuteRole createMuteRoleForServer(AServer server, ARole role);
List<MuteRole> retrieveMuteRolesForServer(AServer server);
MuteRole setMuteRoleForServer(AServer server, ARole role);
boolean muteRoleForServerExists(AServer server);
}

View File

@@ -84,8 +84,6 @@ public class RemindServiceBean implements ReminderService {
log.error("Failed to remind immediately.", exception);
}
}, remindIn.toNanos(), TimeUnit.NANOSECONDS);
} else {
log.trace("Starting scheduled job to execute reminder.");
JobDataMap parameters = new JobDataMap();

View File

@@ -15,7 +15,9 @@ import dev.sheldan.abstracto.core.command.execution.UnParsedCommandParameter;
import dev.sheldan.abstracto.core.Constants;
import dev.sheldan.abstracto.core.command.service.management.CommandManagementService;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.RoleManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserManagementService;
import dev.sheldan.abstracto.core.models.database.AChannel;
@@ -70,6 +72,9 @@ public class CommandReceivedHandler extends ListenerAdapter {
@Autowired
private CommandManagementService commandManagementService;
@Autowired
private RoleManagementService roleManagementService;
@Override
@Async
@Transactional
@@ -188,6 +193,8 @@ public class CommandReceivedHandler extends ListenerAdapter {
} else {
parsedParameters.add(value);
}
} else if(param.getType().equals(ARole.class)) {
parsedParameters.add(roleManagementService.findRole(Long.parseLong(value)));
} else if(param.getType().equals(Boolean.class)) {
parsedParameters.add(Boolean.valueOf(value));
} else if (param.getType().equals(Duration.class)) {

View File

@@ -6,6 +6,7 @@ import dev.sheldan.abstracto.core.models.GuildChannelMember;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
@@ -71,6 +72,11 @@ public class BotServiceBean implements BotService {
}
}
@Override
public Member getMemberInServer(AUserInAServer aUserInAServer) {
return getMemberInServer(aUserInAServer.getServerReference().getId(), aUserInAServer.getUserReference().getId());
}
@Override
public Member getMemberInServer(AServer server, AUser member) {
return getMemberInServer(server.getId(), member.getId());

View File

@@ -3,6 +3,7 @@ package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.exception.ChannelException;
import dev.sheldan.abstracto.core.exception.GuildException;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.models.database.AChannel;
import lombok.extern.slf4j.Slf4j;
@@ -27,18 +28,26 @@ public class ChannelServiceBean implements ChannelService {
@Autowired
private BotService botService;
@Autowired
private ChannelManagementService channelManagementService;
@Override
public void sendTextInAChannel(String text, AChannel channel) {
sendTextInAChannelFuture(text, channel);
}
@Override
public void sendTextInAChannel(String text, TextChannel channel) {
sendTextInAChannelFuture(text, channel);
}
@Override
public CompletableFuture<Message> sendTextInAChannelFuture(String text, AChannel channel) {
Guild guild = botService.getInstance().getGuildById(channel.getServer().getId());
if (guild != null) {
TextChannel textChannel = guild.getTextChannelById(channel.getId());
if(textChannel != null) {
return textChannel.sendMessage(text).submit();
return sendTextInAChannelFuture(text, textChannel);
} else {
log.error("Channel {} to post towards was not found in server {}", channel.getId(), channel.getServer().getId());
throw new ChannelException(String.format("Channel %s to post to not found.", channel.getId()));
@@ -49,6 +58,11 @@ public class ChannelServiceBean implements ChannelService {
}
}
@Override
public CompletableFuture<Message> sendTextInAChannelFuture(String text, TextChannel channel) {
return channel.sendMessage(text).submit();
}
@Override
public CompletableFuture<Message> sendEmbedInAChannelFuture(MessageEmbed embed, AChannel channel) {
Guild guild = botService.getInstance().getGuildById(channel.getServer().getId());

View File

@@ -3,15 +3,15 @@ package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.EmoteException;
import dev.sheldan.abstracto.core.exception.GuildException;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
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.Optional;
import java.util.concurrent.CompletableFuture;
@@ -32,6 +32,9 @@ public class MessageServiceBean implements MessageService {
@Autowired
private ChannelService channelService;
@Autowired
private MessageServiceBean self;
@Override
public void addReactionToMessage(String emoteKey, Long serverId, Message message) {
Optional<Guild> guildByIdOptional = botService.getGuildById(serverId);
@@ -74,4 +77,32 @@ public class MessageServiceBean implements MessageService {
public void updateStatusMessage(AChannel channel, Long messageId, MessageToSend messageToSend) {
channelService.editMessageInAChannel(messageToSend, channel, messageId);
}
@Override
public void sendMessageToUser(AUserInAServer userInAServer, String text, TextChannel feedbackChannel) {
Member memberInServer = botService.getMemberInServer(userInAServer);
sendMessageToUser(memberInServer.getUser(), text, feedbackChannel);
}
@Override
public void sendMessageToUser(User user, String text, TextChannel feedbackChannel) {
CompletableFuture<Message> messageFuture = new CompletableFuture<>();
user.openPrivateChannel().queue(privateChannel -> {
privateChannel.sendMessage(text).queue(messageFuture::complete, messageFuture::completeExceptionally);
});
messageFuture.exceptionally(e -> {
log.warn("Failed to send message. ", e);
if(feedbackChannel != null){
self.sendFeedbackAboutException(e, feedbackChannel);
}
return null;
});
}
@Transactional
public void sendFeedbackAboutException(Throwable e, TextChannel feedbackChannel) {
channelService.sendTextInAChannel(String.format("Failed to send message: %s", e.getMessage()), feedbackChannel);
}
}

View File

@@ -10,7 +10,7 @@ spring.jpa.hibernate.naming_strategy = org.hibernate.cfg.ImprovedNamingStrategy
log4j.logger.org.hibernate.SQL=info
log4j.logger.org.hibernate.type.descriptor.sql=trace
log4j.logger.org.hibernate.type=trace
log4j.logger.dev.sheldan=info
logging.level.dev.sheldan=info
spring.cache.cache-names=messages
spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.core.models;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Member;
@Setter
@Getter
@Builder
public class FullUser {
private AUserInAServer aUserInAServer;
private Member member;
}

View File

@@ -4,6 +4,7 @@ import dev.sheldan.abstracto.core.models.GuildChannelMember;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.*;
import org.springframework.stereotype.Service;
@@ -18,6 +19,7 @@ public interface BotService {
JDA getInstance();
GuildChannelMember getServerChannelUser(Long serverId, Long channelId, Long userId);
Member getMemberInServer(Long serverId, Long memberId);
Member getMemberInServer(AUserInAServer aUserInAServer);
Member getMemberInServer(AServer server, AUser member);
CompletableFuture<Void> deleteMessage(Long serverId, Long channelId, Long messageId);
Optional<Emote> getEmote(Long serverId, AEmote emote);

View File

@@ -12,7 +12,9 @@ import java.util.concurrent.CompletableFuture;
public interface ChannelService {
void sendTextInAChannel(String text, AChannel channel);
void sendTextInAChannel(String text, TextChannel channel);
CompletableFuture<Message> sendTextInAChannelFuture(String text, AChannel channel);
CompletableFuture<Message> sendTextInAChannelFuture(String text, TextChannel channel);
CompletableFuture<Message> sendEmbedInAChannelFuture(MessageEmbed embed, AChannel channel);
CompletableFuture<Message> sendEmbedInAChannelFuture(MessageEmbed embed, TextChannel channel);
List<CompletableFuture<Message>> sendMessageToEndInAChannel(MessageToSend messageToSend, AChannel channel);

View File

@@ -1,8 +1,11 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.User;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -12,4 +15,6 @@ public interface MessageService {
CompletableFuture<Void> deleteMessageInChannelInServer(Long serverId, Long channelId, Long messageId);
CompletableFuture<Message> createStatusMessage(MessageToSend messageToSend, AChannel channel);
void updateStatusMessage(AChannel channel, Long messageId, MessageToSend messageToSend);
void sendMessageToUser(AUserInAServer userInAServer, String text, TextChannel feedbackChannel);
void sendMessageToUser(User user, String text, TextChannel feedbackChannel);
}

View File

@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.templating.config;
import dev.sheldan.abstracto.templating.loading.DatabaseTemplateLoader;
import dev.sheldan.abstracto.templating.methods.DurationMethod;
import dev.sheldan.abstracto.templating.methods.InstantMethod;
import freemarker.template.Configuration;
import freemarker.template.TemplateException;
import org.springframework.beans.factory.annotation.Autowired;
@@ -20,12 +21,16 @@ public class FreemarkerConfiguration {
@Autowired
private DurationMethod durationMethod;
@Autowired
private InstantMethod instantMethod;
@Bean
public Configuration freeMarkerConfiguration() throws IOException, TemplateException {
FreeMarkerConfigurationFactory factory = new FreeMarkerConfigurationFactory();
factory.setPreTemplateLoaders(templateLoader);
Configuration configuration = factory.createConfiguration();
configuration.setSharedVariable("fmtDuration", durationMethod);
configuration.setSharedVariable("formatInstant", instantMethod);
configuration.setEncoding(Locale.getDefault(), "utf-8");
// needed to support default methods in interfaces
configuration.setIncompatibleImprovements(Configuration.VERSION_2_3_29);

View File

@@ -0,0 +1,45 @@
package dev.sheldan.abstracto.templating.methods;
import dev.sheldan.abstracto.templating.service.TemplateService;
import freemarker.ext.beans.StringModel;
import freemarker.template.SimpleScalar;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModelException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
@Component
public class InstantMethod implements TemplateMethodModelEx {
@Autowired
private TemplateService service;
@Override
public Object exec(List arguments) throws TemplateModelException {
if (arguments.size() != 2) {
throw new TemplateModelException("Incorrect parameters passed.");
}
Object wrappedObject = ((StringModel) arguments.get(0)).getWrappedObject();
if(!(wrappedObject instanceof Instant)) {
throw new TemplateModelException("Passed argument was not a instant object");
}
String formatString = ((SimpleScalar) arguments.get(1)).getAsString();
Instant duration = (Instant) wrappedObject;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatString)
.withZone(ZoneId.systemDefault());
return formatter.format(duration);
}
private HashMap<String, Object> getParam(Long value) {
HashMap<String, Object> params = new HashMap<>();
params.put("amount", value);
return params;
}
}