reworked exception handling, different exceptions whether or why it failed, all of which are unchecked

added feature flags for commands/listeners
added enable/disable command
added feature flag handling to command received handler
added boolean parameter support
fixed 1 parameter commands
fixed posttargets not working on multiple servers, removed unique constraint on posttarget names
added setup validation to suggestions
added quartz property loader
moved join/leave logger to moderation
reworked the way the message listener are handled (separate listener around)
This commit is contained in:
Sheldan
2020-04-04 12:55:01 +02:00
parent c9557fccc2
commit bf94af66d5
132 changed files with 1378 additions and 530 deletions

View File

@@ -0,0 +1,5 @@
package dev.sheldan.abstracto.config;
public class AbstractoFeatures {
public static String CORE = "core";
}

View File

@@ -0,0 +1,16 @@
package dev.sheldan.abstracto.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.HashMap;
@Component
@Getter
@Setter
@ConfigurationProperties(prefix = "abstracto")
public class FeatureFlagConfig {
private HashMap<String, Boolean> features = new HashMap<>();
}

View File

@@ -0,0 +1,29 @@
package dev.sheldan.abstracto.config;
import dev.sheldan.abstracto.core.listener.ServerConfigListener;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.FeatureFlagManagementService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class FeatureFlagListener implements ServerConfigListener {
@Autowired
private FeatureFlagConfig featureFlagConfig;
@Autowired
private FeatureFlagManagementService service;
@Override
public void updateServerConfig(AServer server) {
log.info("Setting up feature flags if necessary.");
featureFlagConfig.getFeatures().forEach((featureFlagKey, featureFlagValue) -> {
if(!service.getFeatureFlag(featureFlagKey, server.getId()).isPresent()) {
service.createFeatureFlag(featureFlagKey, server.getId(), featureFlagValue);
}
});
}
}

View File

@@ -0,0 +1,47 @@
package dev.sheldan.abstracto.core.commands;
import dev.sheldan.abstracto.command.Command;
import dev.sheldan.abstracto.command.execution.CommandConfiguration;
import dev.sheldan.abstracto.command.execution.CommandContext;
import dev.sheldan.abstracto.command.execution.CommandResult;
import dev.sheldan.abstracto.command.execution.Parameter;
import dev.sheldan.abstracto.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.commands.utility.UtilityModule;
import dev.sheldan.abstracto.core.service.management.FeatureFlagManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class Disable implements Command {
@Autowired
private FeatureFlagManagementService featureFlagManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
String flagKey = (String) commandContext.getParameters().getParameters().get(0);
featureFlagManagementService.updateOrCreateFeatureFlag(flagKey, commandContext.getGuild().getIdLong(), false);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter featureName = Parameter.builder().name("featureName").type(String.class).description("The feature to disable.").build();
List<Parameter> parameters = Arrays.asList(featureName);
return CommandConfiguration.builder()
.name("disable")
.module(UtilityModule.UTILITY)
.parameters(parameters)
.description("Disables features for this server.")
.causesReaction(true)
.build();
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
}
}

View File

@@ -0,0 +1,47 @@
package dev.sheldan.abstracto.core.commands;
import dev.sheldan.abstracto.command.Command;
import dev.sheldan.abstracto.command.execution.CommandConfiguration;
import dev.sheldan.abstracto.command.execution.CommandContext;
import dev.sheldan.abstracto.command.execution.CommandResult;
import dev.sheldan.abstracto.command.execution.Parameter;
import dev.sheldan.abstracto.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.commands.utility.UtilityModule;
import dev.sheldan.abstracto.core.service.management.FeatureFlagManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class Enable implements Command {
@Autowired
private FeatureFlagManagementService featureFlagManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
String flagKey = (String) commandContext.getParameters().getParameters().get(0);
featureFlagManagementService.updateOrCreateFeatureFlag(flagKey, commandContext.getGuild().getIdLong(), true);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter featureName = Parameter.builder().name("featureName").type(String.class).description("The feature to enable.").build();
List<Parameter> parameters = Arrays.asList(featureName);
return CommandConfiguration.builder()
.name("enable")
.module(UtilityModule.UTILITY)
.parameters(parameters)
.description("Enables features for this server.")
.causesReaction(true)
.build();
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
}
}

View File

@@ -2,10 +2,11 @@ package dev.sheldan.abstracto.core.commands.channels;
import dev.sheldan.abstracto.command.Command;
import dev.sheldan.abstracto.command.execution.*;
import dev.sheldan.abstracto.core.management.ChannelManagementService;
import dev.sheldan.abstracto.core.management.PostTargetManagement;
import dev.sheldan.abstracto.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.models.command.PostTargetErrorModel;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.PostTargetManagement;
import dev.sheldan.abstracto.templating.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
@@ -19,7 +20,7 @@ import java.util.List;
@Service
@Slf4j
public class PostTargetCommand implements Command {
public class PostTarget implements Command {
public static final String POST_TARGET_NO_TARGET_TEMPLATE = "posttarget_no_target";
public static final String POST_TARGET_INVALID_TARGET_TEMPLATE = "posttarget_invalid_target";
@@ -36,25 +37,25 @@ public class PostTargetCommand implements Command {
private TemplateService templateService;
@Override
public Result execute(CommandContext commandContext) {
public CommandResult execute(CommandContext commandContext) {
if(commandContext.getParameters().getParameters().isEmpty()) {
PostTargetErrorModel postTargetErrorModel = (PostTargetErrorModel) ContextConverter.fromCommandContext(commandContext, PostTargetErrorModel.class);
postTargetErrorModel.setValidPostTargets(postTargetService.getAvailablePostTargets());
String errorMessage = templateService.renderTemplate(POST_TARGET_NO_TARGET_TEMPLATE, postTargetErrorModel);
return Result.fromError(errorMessage);
return CommandResult.fromError(errorMessage);
}
String targetName = (String) commandContext.getParameters().getParameters().get(0);
if(!postTargetService.validPostTarget(targetName)) {
PostTargetErrorModel postTargetErrorModel = (PostTargetErrorModel) ContextConverter.fromCommandContext(commandContext, PostTargetErrorModel.class);
postTargetErrorModel.setValidPostTargets(postTargetService.getAvailablePostTargets());
String errorMessage = templateService.renderTemplate(POST_TARGET_INVALID_TARGET_TEMPLATE, postTargetErrorModel);
return Result.fromError(errorMessage);
return CommandResult.fromError(errorMessage);
}
GuildChannel channel = (GuildChannel) commandContext.getParameters().getParameters().get(1);
Guild guild = channel.getGuild();
postTargetManagement.createOrUpdate(targetName, guild.getIdLong(), channel.getIdLong());
log.info("Setting posttarget {} in {} to {}", targetName, guild.getIdLong(), channel.getId());
return Result.fromSuccess();
return CommandResult.fromSuccess();
}
@Override
@@ -70,4 +71,9 @@ public class PostTargetCommand implements Command {
.causesReaction(true)
.build();
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
}
}

View File

@@ -3,12 +3,14 @@ package dev.sheldan.abstracto.core.commands.help;
import dev.sheldan.abstracto.command.*;
import dev.sheldan.abstracto.command.execution.*;
import dev.sheldan.abstracto.command.module.ModuleInfo;
import dev.sheldan.abstracto.config.AbstractoFeatures;
import dev.sheldan.abstracto.templating.TemplateService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
@Service
@@ -22,7 +24,7 @@ public class Help implements Command {
private TemplateService templateService;
@Override
public Result execute(CommandContext commandContext) {
public CommandResult execute(CommandContext commandContext) {
CommandHierarchy commandStructure = registry.getDetailedModules();
StringBuilder sb = new StringBuilder();
if(commandContext.getParameters().getParameters().isEmpty()){
@@ -53,7 +55,7 @@ public class Help implements Command {
}
commandContext.getChannel().sendMessage(sb.toString()).queue();
return Result.fromSuccess();
return CommandResult.fromSuccess();
}
private String getCommand(Command command){
@@ -132,4 +134,8 @@ public class Help implements Command {
.build();
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
}
}

View File

@@ -1,17 +1,20 @@
package dev.sheldan.abstracto.core.commands.utility;
import dev.sheldan.abstracto.command.Command;
import dev.sheldan.abstracto.command.CommandCondition;
import dev.sheldan.abstracto.command.HelpInfo;
import dev.sheldan.abstracto.command.execution.CommandConfiguration;
import dev.sheldan.abstracto.command.execution.CommandContext;
import dev.sheldan.abstracto.command.execution.Parameter;
import dev.sheldan.abstracto.command.execution.Result;
import dev.sheldan.abstracto.command.execution.CommandResult;
import dev.sheldan.abstracto.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.models.command.EchoModel;
import dev.sheldan.abstracto.templating.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Service
@@ -23,14 +26,14 @@ public class Echo implements Command {
private TemplateService templateService;
@Override
public Result execute(CommandContext commandContext) {
public CommandResult execute(CommandContext commandContext) {
StringBuilder sb = new StringBuilder();
commandContext.getParameters().getParameters().forEach(o -> {
sb.append(o.toString());
});
EchoModel model = EchoModel.builder().text(sb.toString()).build();
commandContext.getChannel().sendMessage(templateService.renderTemplate(TEMPLATE_NAME, model)).queue();
return Result.fromSuccess();
return CommandResult.fromSuccess();
}
@Override
@@ -47,4 +50,9 @@ public class Echo implements Command {
.help(helpInfo)
.build();
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
}
}

View File

@@ -1,14 +1,19 @@
package dev.sheldan.abstracto.core.commands.utility;
import dev.sheldan.abstracto.command.Command;
import dev.sheldan.abstracto.command.CommandCondition;
import dev.sheldan.abstracto.command.execution.CommandConfiguration;
import dev.sheldan.abstracto.command.execution.CommandContext;
import dev.sheldan.abstracto.command.execution.Result;
import dev.sheldan.abstracto.command.execution.CommandResult;
import dev.sheldan.abstracto.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.models.command.PingModel;
import dev.sheldan.abstracto.templating.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
@Service
public class Ping implements Command {
@@ -18,12 +23,12 @@ public class Ping implements Command {
private TemplateService templateService;
@Override
public Result execute(CommandContext commandContext) {
public CommandResult execute(CommandContext commandContext) {
long ping = commandContext.getJda().getGatewayPing();
PingModel model = PingModel.builder().latency(ping).build();
String text = templateService.renderTemplate(PING_TEMPLATE, model);
commandContext.getChannel().sendMessage(text).queue();
return Result.fromSuccess();
return CommandResult.fromSuccess();
}
@Override
@@ -36,4 +41,9 @@ public class Ping implements Command {
.build();
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
}
}

View File

@@ -3,11 +3,11 @@ package dev.sheldan.abstracto.core.commands.utility;
import dev.sheldan.abstracto.command.Command;
import dev.sheldan.abstracto.command.execution.CommandConfiguration;
import dev.sheldan.abstracto.command.execution.CommandContext;
import dev.sheldan.abstracto.command.execution.CommandResult;
import dev.sheldan.abstracto.command.execution.Parameter;
import dev.sheldan.abstracto.command.execution.Result;
import dev.sheldan.abstracto.core.exception.ConfigurationException;
import dev.sheldan.abstracto.core.management.EmoteManagementService;
import dev.sheldan.abstracto.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
import net.dv8tion.jda.api.entities.Emote;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -25,7 +25,7 @@ public class SetEmote implements Command {
private EmoteService emoteService;
@Override
public Result execute(CommandContext commandContext) {
public CommandResult execute(CommandContext commandContext) {
String emoteKey = (String) commandContext.getParameters().getParameters().get(0);
Object o = commandContext.getParameters().getParameters().get(1);
if(o instanceof String) {
@@ -33,13 +33,10 @@ public class SetEmote implements Command {
emoteManagementService.setEmoteToDefaultEmote(emoteKey, emote, commandContext.getGuild().getIdLong());
} else {
Emote emote = (Emote) o;
if(emoteService.isEmoteUsableByBot(emote)) {
emoteManagementService.setEmoteToCustomEmote(emoteKey, emote, commandContext.getGuild().getIdLong());
} else {
throw new ConfigurationException("Emote is not usable by bot.");
}
// todo check if usable
emoteManagementService.setEmoteToCustomEmote(emoteKey, emote, commandContext.getGuild().getIdLong());
}
return Result.fromSuccess();
return CommandResult.fromSuccess();
}
@Override
@@ -55,4 +52,9 @@ public class SetEmote implements Command {
.causesReaction(true)
.build();
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
}
}

View File

@@ -1,7 +0,0 @@
package dev.sheldan.abstracto.core.exception;
public class ConfigurationException extends RuntimeException {
public ConfigurationException(String message) {
super(message);
}
}

View File

@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.NotFoundException;
import dev.sheldan.abstracto.core.exception.ChannelException;
import dev.sheldan.abstracto.core.exception.GuildException;
import dev.sheldan.abstracto.core.models.ServerChannelUser;
import dev.sheldan.abstracto.core.models.database.AEmote;
import lombok.extern.slf4j.Slf4j;
@@ -39,7 +40,7 @@ public class BotService implements Bot {
}
@Override
public ServerChannelUser getServerChannelUser(Long serverId, Long channelId, Long userId) {
public ServerChannelUser getServerChannelUser(Long serverId, Long channelId, Long userId) {
Optional<Guild> guildOptional = getGuildById(serverId);
if(guildOptional.isPresent()) {
Guild guild = guildOptional.get();
@@ -49,16 +50,14 @@ public class BotService implements Bot {
Member member = guild.getMemberById(userId);
return ServerChannelUser.builder().guild(guild).textChannel(textChannel).member(member).build();
} else {
throw new NotFoundException(String.format("Text channel %s not found in guild %s", channelId, serverId));
throw new ChannelException(String.format("Text channel %s not found in guild %s", channelId, serverId));
}
}
else {
throw new NotFoundException(String.format("Guild %s not found.", serverId));
throw new GuildException(String.format("Guild %s not found.", serverId));
}
}
@Override
public Member getMemberInServer(Long serverId, Long memberId) {
Guild guildById = instance.getGuildById(serverId);
@@ -70,7 +69,7 @@ public class BotService implements Bot {
}
@Override
public void deleteMessage(Long serverId, Long channelId, Long messageId) {
public void deleteMessage(Long serverId, Long channelId, Long messageId) {
Optional<TextChannel> textChannelOptional = getTextChannelFromServer(serverId, channelId);
if(textChannelOptional.isPresent()) {
TextChannel textChannel = textChannelOptional.get();
@@ -83,16 +82,17 @@ public class BotService implements Bot {
}
@Override
public Optional<Emote> getEmote(Long serverId, AEmote emote) {
public Optional<Emote> getEmote(Long serverId, AEmote emote) {
if(!emote.getCustom()) {
return Optional.empty();
}
Optional<Guild> guildById = getGuildById(serverId);
if(guildById.isPresent()) {
Guild guild = guildById.get();
return Optional.ofNullable(guild.getEmoteById(emote.getEmoteId()));
Emote emoteById = guild.getEmoteById(emote.getEmoteId());
return Optional.ofNullable(emoteById);
}
throw new NotFoundException(String.format("Not able to find emote %s in server %s", emote.getId(), serverId));
throw new GuildException(String.format("Not able to find server %s", serverId));
}
@Override
@@ -101,13 +101,13 @@ public class BotService implements Bot {
}
@Override
public Optional<TextChannel> getTextChannelFromServer(Long serverId, Long textChannelId) {
public Optional<TextChannel> getTextChannelFromServer(Long serverId, Long textChannelId) {
Optional<Guild> guildOptional = getGuildById(serverId);
if(guildOptional.isPresent()) {
Guild guild = guildOptional.get();
return Optional.ofNullable(guild.getTextChannelById(textChannelId));
}
throw new NotFoundException(String.format("Not able to find guild %s", serverId));
throw new GuildException(String.format("Not able to find guild %s", serverId));
}
@Override

View File

@@ -1,5 +1,7 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.ChannelException;
import dev.sheldan.abstracto.core.exception.GuildException;
import dev.sheldan.abstracto.core.models.database.AChannel;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
@@ -22,10 +24,12 @@ public class ChannelServiceBean implements ChannelService {
if(textChannel != null) {
textChannel.sendMessage(text).queue();
} else {
log.warn("Channel {} to post towards was not found in server {}", channel.getId(), channel.getServer().getId());
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()));
}
} else {
log.warn("Guild {} was not found when trying to post a message", channel.getServer().getId());
log.error("Guild {} was not found when trying to post a message", channel.getServer().getId());
throw new GuildException(String.format("Guild %s to post in channel %s was not found.", channel.getServer().getId(), channel.getId()));
}
}
}

View File

@@ -1,6 +1,6 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.management.ConfigManagementService;
import dev.sheldan.abstracto.core.service.management.ConfigManagementService;
import dev.sheldan.abstracto.core.models.database.AConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

View File

@@ -1,6 +1,8 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.EmoteException;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.Guild;
@@ -17,6 +19,9 @@ public class EmoteServiceBean implements EmoteService {
@Autowired
private Bot botService;
@Autowired
private EmoteManagementService emoteManagementService;
@Override
public boolean isEmoteUsableByBot(Emote emote) {
for (Guild guild : botService.getInstance().getGuilds()) {
@@ -38,7 +43,7 @@ public class EmoteServiceBean implements EmoteService {
}
@Override
public String getEmoteAsMention(AEmote emote, Long serverId, String defaultText) {
public String getEmoteAsMention(AEmote emote, Long serverId, String defaultText) {
if(emote != null && emote.getCustom()) {
Optional<Emote> emoteOptional = botService.getEmote(serverId, emote);
if (emoteOptional.isPresent()) {
@@ -56,7 +61,14 @@ public class EmoteServiceBean implements EmoteService {
}
@Override
public String getEmoteAsMention(AEmote emote, Long serverId) {
public String getEmoteAsMention(AEmote emote, Long serverId) {
return this.getEmoteAsMention(emote, serverId, " ");
}
@Override
public void throwIfEmoteDoesNotExist(String emoteKey, Long serverId) {
if(!emoteManagementService.loadEmoteByName(emoteKey, serverId).isPresent()) {
throw new EmoteException(String.format("Emote %s not defined.", emoteKey));
}
}
}

View File

@@ -0,0 +1,17 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.service.management.FeatureFlagManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class FeatureFlagServiceBean implements FeatureFlagService {
@Autowired
private FeatureFlagManagementService managementService;
@Override
public boolean isFeatureEnabled(String name, Long serverId) {
return managementService.getFeatureFlagValue(name, serverId);
}
}

View File

@@ -1,8 +1,9 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.NotFoundException;
import dev.sheldan.abstracto.core.management.EmoteManagementService;
import dev.sheldan.abstracto.core.management.UserManagementService;
import dev.sheldan.abstracto.core.exception.ChannelException;
import dev.sheldan.abstracto.core.exception.GuildException;
import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
import dev.sheldan.abstracto.core.service.management.UserManagementService;
import dev.sheldan.abstracto.core.models.CachedMessage;
import dev.sheldan.abstracto.core.models.CachedReaction;
import dev.sheldan.abstracto.core.models.database.AUser;
@@ -89,12 +90,12 @@ public class MessageCacheBean implements MessageCache {
buildCachedMessageFromMessage(future, message);
});
} else {
log.warn("Not able to load message {} in channel {} in guild {}. Text channel not found.", messageId, textChannelId, guildId);
future.completeExceptionally(new NotFoundException(String.format("Not able to load message %s. Text channel %s not found in guild %s", messageId, textChannelId, guildId)));
log.error("Not able to load message {} in channel {} in guild {}. Text channel not found.", messageId, textChannelId, guildId);
future.completeExceptionally(new ChannelException(String.format("Not able to load message %s. Text channel %s not found in guild %s", messageId, textChannelId, guildId)));
}
} else {
log.warn("Not able to load message {} in channel {} in guild {}. Guild not found.", messageId, textChannelId, guildId);
future.completeExceptionally(new NotFoundException(String.format("Not able to load message %s. Guild %s not found.", messageId, guildId)));
log.error("Not able to load message {} in channel {} in guild {}. Guild not found.", messageId, textChannelId, guildId);
future.completeExceptionally(new GuildException(String.format("Not able to load message %s. Guild %s not found.", messageId, guildId)));
}
}

View File

@@ -1,7 +1,8 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.NotFoundException;
import dev.sheldan.abstracto.core.management.EmoteManagementService;
import dev.sheldan.abstracto.core.exception.EmoteException;
import dev.sheldan.abstracto.core.exception.GuildException;
import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
import dev.sheldan.abstracto.core.models.database.AEmote;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Emote;
@@ -31,23 +32,23 @@ public class MessageServiceBean implements MessageService {
if(aEmote.isPresent()) {
AEmote emote = aEmote.get();
if(emote.getCustom()) {
Emote emoteById = guild.getEmoteById(emote.getEmoteId());
Emote emoteById = bot.getInstance().getEmoteById(emote.getEmoteId());
if(emoteById != null) {
message.addReaction(emoteById).queue();
} else {
log.warn("Emote with key {} and id {} for guild {} was not found.", emoteKey, emote.getEmoteId(), guild.getId());
throw new NotFoundException(String.format("Emote with key `%s` and id %s in guild %s was not found. Check whether or not the configured emote is available.", emoteKey, emote.getEmoteId(), guild.getIdLong()));
log.error("Emote with key {} and id {} for guild {} was not found.", emoteKey, emote.getEmoteId(), guild.getId());
throw new EmoteException(String.format("Emote with key `%s` and id %s in guild %s was not found. Check whether or not the configured emote is available.", emoteKey, emote.getEmoteId(), guild.getIdLong()));
}
} else {
message.addReaction(emote.getEmoteKey()).queue();
}
} else {
log.warn("Cannot add reaction, emote {} not defined for server {}.", emoteKey, serverId);
throw new NotFoundException(String.format("Cannot add reaction. Emote `%s` not defined in server %s. Define the emote via the setEmote command.", emoteKey, serverId));
log.error("Cannot add reaction, emote {} not defined for server {}.", emoteKey, serverId);
throw new EmoteException(String.format("Cannot add reaction. Emote `%s` not defined in server %s. Define the emote via the setEmote command.", emoteKey, serverId));
}
} else {
log.warn("Cannot add reaction, guild not found {}", serverId);
throw new NotFoundException(String.format("Cannot add reaction, guild %s not found.", serverId));
log.error("Cannot add reaction, guild not found {}", serverId);
throw new GuildException(String.format("Cannot add reaction, guild %s not found.", serverId));
}
}
}

View File

@@ -1,9 +1,10 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.DynamicKeyLoader;
import dev.sheldan.abstracto.core.exception.ConfigurationException;
import dev.sheldan.abstracto.core.management.PostTargetManagement;
import dev.sheldan.abstracto.core.management.ServerManagementService;
import dev.sheldan.abstracto.core.exception.ChannelException;
import dev.sheldan.abstracto.core.exception.GuildException;
import dev.sheldan.abstracto.core.service.management.PostTargetManagement;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.models.database.PostTarget;
import dev.sheldan.abstracto.core.models.embed.MessageToSend;
import lombok.extern.slf4j.Slf4j;
@@ -36,31 +37,32 @@ public class PostTargetServiceBean implements PostTargetService {
private DynamicKeyLoader dynamicKeyLoader;
@Override
public CompletableFuture<Message> sendTextInPostTarget(String text, PostTarget target) {
public CompletableFuture<Message> sendTextInPostTarget(String text, PostTarget target) {
TextChannel textChannelForPostTarget = getTextChannelForPostTarget(target);
return textChannelForPostTarget.sendMessage(text).submit();
}
@Override
public CompletableFuture<Message> sendEmbedInPostTarget(MessageEmbed embed, PostTarget target) {
public CompletableFuture<Message> sendEmbedInPostTarget(MessageEmbed embed, PostTarget target) {
TextChannel textChannelForPostTarget = getTextChannelForPostTarget(target);
return textChannelForPostTarget.sendMessage(embed).submit();
}
private TextChannel getTextChannelForPostTarget(PostTarget target) {
private TextChannel getTextChannelForPostTarget(PostTarget target) {
Guild guild = botService.getInstance().getGuildById(target.getServerReference().getId());
if(guild != null) {
TextChannel textChannelById = guild.getTextChannelById(target.getChannelReference().getId());
if(textChannelById != null) {
return textChannelById;
} else {
log.warn("Incorrect post target configuration: {} points to {} on server {}", target.getName(),
log.error("Incorrect post target configuration: {} points to {} on server {}", target.getName(),
target.getChannelReference().getId(), target.getServerReference().getId());
throw new ChannelException(String.format("Incorrect post target configuration. The channel %s of target %s cannot be found",
target.getChannelReference().getId(), target.getChannelReference().getId()));
}
} else {
log.warn("Incorrect post target configuration: Guild id {} was not found.", target.getServerReference().getId());
throw new GuildException(String.format("Incorrect post target configuration. Guild %s cannot be found.", target.getServerReference().getId()));
}
throw new ConfigurationException("Incorrect post target configuration.");
}
private PostTarget getPostTarget(String postTargetName, Long serverId) {
@@ -68,31 +70,31 @@ public class PostTargetServiceBean implements PostTargetService {
if(postTarget != null) {
return postTarget;
} else {
log.warn("PostTarget {} in server {} was not found!", postTargetName, serverId);
throw new ConfigurationException(String.format("Incorrect post target configuration: Post target %s was not found.", postTargetName));
log.error("PostTarget {} in server {} was not found!", postTargetName, serverId);
throw new ChannelException(String.format("Incorrect post target configuration: Post target %s was not found.", postTargetName));
}
}
@Override
public CompletableFuture<Message> sendTextInPostTarget(String text, String postTargetName, Long serverId) {
public CompletableFuture<Message> sendTextInPostTarget(String text, String postTargetName, Long serverId) {
PostTarget postTarget = this.getPostTarget(postTargetName, serverId);
return this.sendTextInPostTarget(text, postTarget);
}
@Override
public CompletableFuture<Message> sendEmbedInPostTarget(MessageEmbed embed, String postTargetName, Long serverId) {
public CompletableFuture<Message> sendEmbedInPostTarget(MessageEmbed embed, String postTargetName, Long serverId) {
PostTarget postTarget = this.getPostTarget(postTargetName, serverId);
return this.sendEmbedInPostTarget(embed, postTarget);
}
@Override
public CompletableFuture<Message> sendEmbedInPostTarget(MessageToSend message, String postTargetName, Long serverId) {
public CompletableFuture<Message> sendEmbedInPostTarget(MessageToSend message, String postTargetName, Long serverId) {
PostTarget postTarget = this.getPostTarget(postTargetName, serverId);
return this.sendEmbedInPostTarget(message, postTarget);
}
@Override
public CompletableFuture<Message> sendEmbedInPostTarget(MessageToSend message, PostTarget target) {
public CompletableFuture<Message> sendEmbedInPostTarget(MessageToSend message, PostTarget target) {
TextChannel textChannelForPostTarget = getTextChannelForPostTarget(target);
String messageText = message.getMessage();
if(StringUtils.isBlank(messageText)) {
@@ -103,7 +105,7 @@ public class PostTargetServiceBean implements PostTargetService {
}
@Override
public CompletableFuture<Message> editEmbedInPostTarget(Long messageId, MessageToSend message, PostTarget target) {
public CompletableFuture<Message> editEmbedInPostTarget(Long messageId, MessageToSend message, PostTarget target) {
TextChannel textChannelForPostTarget = getTextChannelForPostTarget(target);
String messageText = message.getMessage();
if(StringUtils.isBlank(messageText)) {
@@ -114,7 +116,7 @@ public class PostTargetServiceBean implements PostTargetService {
}
@Override
public void editOrCreatedInPostTarget(Long messageId, MessageToSend messageToSend, PostTarget target, CompletableFuture<Message> future) {
public void editOrCreatedInPostTarget(Long messageId, MessageToSend messageToSend, PostTarget target, CompletableFuture<Message> future) {
TextChannel textChannelForPostTarget = getTextChannelForPostTarget(target);
if(StringUtils.isBlank(messageToSend.getMessage().trim())) {
textChannelForPostTarget
@@ -123,8 +125,10 @@ public class PostTargetServiceBean implements PostTargetService {
existingMessage -> existingMessage
.editMessage(messageToSend.getEmbed())
.submit().thenAccept(future::complete),
throwable -> sendEmbedInPostTarget(messageToSend, target)
.thenAccept(future::complete));
throwable -> {
sendEmbedInPostTarget(messageToSend, target)
.thenAccept(future::complete);
});
} else {
textChannelForPostTarget
.retrieveMessageById(messageId)
@@ -133,19 +137,29 @@ public class PostTargetServiceBean implements PostTargetService {
.editMessage(messageToSend.getMessage())
.embed(messageToSend.getEmbed())
.submit().thenAccept(future::complete),
throwable -> sendEmbedInPostTarget(messageToSend, target)
.thenAccept(future::complete));
throwable -> {
sendEmbedInPostTarget(messageToSend, target)
.thenAccept(future::complete);
});
}
}
@Override
public void editOrCreatedInPostTarget(Long messageId, MessageToSend messageToSend, String postTargetName, Long serverId, CompletableFuture<Message> future) {
public void editOrCreatedInPostTarget(Long messageId, MessageToSend messageToSend, String postTargetName, Long serverId, CompletableFuture<Message> future) {
PostTarget postTarget = this.getPostTarget(postTargetName, serverId);
this.editOrCreatedInPostTarget(messageId, messageToSend, postTarget, future);
}
@Override
public CompletableFuture<Message> editEmbedInPostTarget(Long messageId, MessageToSend message, String postTargetName, Long serverId) {
public void throwIfPostTargetIsNotDefined(String name, Long serverId) throws ChannelException {
PostTarget postTarget = postTargetManagement.getPostTarget(name, serverId);
if(postTarget == null) {
throw new ChannelException(String.format("Post target %s is not defined.", name));
}
}
@Override
public CompletableFuture<Message> editEmbedInPostTarget(Long messageId, MessageToSend message, String postTargetName, Long serverId) {
PostTarget postTarget = this.getPostTarget(postTargetName, serverId);
return editEmbedInPostTarget(messageId, message, postTarget);
}

View File

@@ -2,9 +2,9 @@ package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.listener.ServerConfigListener;
import dev.sheldan.abstracto.core.utils.SnowflakeUtils;
import dev.sheldan.abstracto.core.management.ChannelManagementService;
import dev.sheldan.abstracto.core.management.RoleManagementService;
import dev.sheldan.abstracto.core.management.ServerManagementService;
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.models.database.AChannel;
import dev.sheldan.abstracto.core.models.AChannelType;
import dev.sheldan.abstracto.core.models.database.ARole;

View File

@@ -2,7 +2,6 @@ package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.AChannelType;
import dev.sheldan.abstracto.core.management.ChannelManagementService;
import dev.sheldan.abstracto.repository.ChannelRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -1,9 +1,7 @@
package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.management.ServerManagementService;
import dev.sheldan.abstracto.core.models.database.AConfig;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.management.ConfigManagementService;
import dev.sheldan.abstracto.repository.ConfigRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

View File

@@ -1,11 +1,10 @@
package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.DynamicKeyLoader;
import dev.sheldan.abstracto.core.exception.ConfigurationException;
import dev.sheldan.abstracto.core.management.EmoteManagementService;
import dev.sheldan.abstracto.core.management.ServerManagementService;
import dev.sheldan.abstracto.core.exception.EmoteException;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.Bot;
import dev.sheldan.abstracto.repository.EmoteRepository;
import net.dv8tion.jda.api.entities.Emote;
import org.springframework.beans.factory.annotation.Autowired;
@@ -26,19 +25,22 @@ public class EmoteManagementServiceBean implements EmoteManagementService {
@Autowired
private DynamicKeyLoader dynamicKeyLoader;
@Autowired
private Bot botService;
@Override
public AEmote loadEmote(Long id) {
return repository.getOne(id);
}
@Override
public AEmote createCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, Long serverId) {
public AEmote createCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, Long serverId) {
AServer server = serverManagementService.loadOrCreate(serverId);
return this.createCustomEmote(name, emoteKey, emoteId, animated, server);
}
@Override
public AEmote createCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, AServer server) {
public AEmote createCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, AServer server) {
validateEmoteName(name);
AEmote emoteToCreate = AEmote
.builder()
@@ -54,13 +56,13 @@ public class EmoteManagementServiceBean implements EmoteManagementService {
}
@Override
public AEmote createDefaultEmote(String name, String emoteKey, Long serverId) {
public AEmote createDefaultEmote(String name, String emoteKey, Long serverId) {
AServer server = serverManagementService.loadOrCreate(serverId);
return createDefaultEmote(name, emoteKey, server);
}
@Override
public AEmote createDefaultEmote(String name, String emoteKey, AServer server) {
public AEmote createDefaultEmote(String name, String emoteKey, AServer server) {
validateEmoteName(name);
AEmote emoteToCreate = AEmote
.builder()
@@ -85,7 +87,7 @@ public class EmoteManagementServiceBean implements EmoteManagementService {
}
@Override
public AEmote setEmoteToCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, Long serverId) {
public AEmote setEmoteToCustomEmote(String name, String emoteKey, Long emoteId, Boolean animated, Long serverId) {
AServer server = serverManagementService.loadOrCreate(serverId);
AEmote emote;
Optional<AEmote> emoteOptional = loadEmoteByName(name, server);
@@ -103,7 +105,7 @@ public class EmoteManagementServiceBean implements EmoteManagementService {
}
@Override
public AEmote setEmoteToCustomEmote(String name, Emote emote, Long serverId) {
public AEmote setEmoteToCustomEmote(String name, Emote emote, Long serverId) {
AServer server = serverManagementService.loadOrCreate(serverId);
AEmote emoteBeingSet;
Optional<AEmote> emoteOptional = loadEmoteByName(name, serverId);
@@ -121,7 +123,7 @@ public class EmoteManagementServiceBean implements EmoteManagementService {
}
@Override
public AEmote setEmoteToDefaultEmote(String name, String emoteKey, Long serverId) {
public AEmote setEmoteToDefaultEmote(String name, String emoteKey, Long serverId) {
AServer server = serverManagementService.loadOrCreate(serverId);
AEmote emoteBeingSet;
Optional<AEmote> emoteOptional = loadEmoteByName(name, serverId);
@@ -171,10 +173,10 @@ public class EmoteManagementServiceBean implements EmoteManagementService {
return emote;
}
private void validateEmoteName(String name) {
private void validateEmoteName(String name) {
List<String> possibleEmotes = dynamicKeyLoader.getEmoteNamesAsList();
if(!possibleEmotes.contains(name)) {
throw new ConfigurationException("Emote `" + name + "` is not defined. Possible values are: " + String.join(", ", possibleEmotes));
throw new EmoteException("Emote `" + name + "` is not defined. Possible values are: " + String.join(", ", possibleEmotes));
}
}
}

View File

@@ -0,0 +1,60 @@
package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.models.database.AFeatureFlag;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.repository.FeatureFlagRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
public class FeatureFlagManagementServiceBean implements FeatureFlagManagementService {
@Autowired
private FeatureFlagRepository repository;
@Autowired
private ServerManagementService serverManagementService;
@Override
public void createFeatureFlag(String key, Long serverId, Boolean newValue) {
AServer server = serverManagementService.loadOrCreate(serverId);
createFeatureFlag(key, server, newValue);
}
@Override
public void createFeatureFlag(String key, AServer server, Boolean newValue) {
AFeatureFlag featureFlag = AFeatureFlag
.builder()
.enabled(newValue)
.key(key)
.server(server)
.build();
repository.save(featureFlag);
}
@Override
public boolean getFeatureFlagValue(String key, Long serverId) {
Optional<AFeatureFlag> featureFlag = getFeatureFlag(key, serverId);
return featureFlag.isPresent() && featureFlag.get().isEnabled();
}
@Override
public void updateOrCreateFeatureFlag(String key, Long serverId, Boolean newValue) {
Optional<AFeatureFlag> existing = getFeatureFlag(key, serverId);
if(existing.isPresent()) {
AFeatureFlag flag = existing.get();
flag.setEnabled(newValue);
repository.save(flag);
} else {
createFeatureFlag(key, serverId, newValue);
}
}
@Override
public Optional<AFeatureFlag> getFeatureFlag(String key, Long serverId) {
AServer server = serverManagementService.loadOrCreate(serverId);
return Optional.ofNullable(repository.findByServerAndKey(server, key));
}
}

View File

@@ -2,12 +2,10 @@ package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.DynamicKeyLoader;
import dev.sheldan.abstracto.core.exception.ConfigurationException;
import dev.sheldan.abstracto.core.exception.PostTargetException;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.PostTarget;
import dev.sheldan.abstracto.core.management.ChannelManagementService;
import dev.sheldan.abstracto.core.management.PostTargetManagement;
import dev.sheldan.abstracto.core.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.repository.PostTargetRepository;
import lombok.extern.slf4j.Slf4j;
@@ -36,7 +34,7 @@ public class PostTargetManagementBean implements PostTargetManagement {
@Override
public void createPostTarget(String name, AServer server, AChannel targetChannel) {
if(!postTargetService.validPostTarget(name)) {
throw new ConfigurationException("PostTarget not found. Possible values are: " + String.join(", ", dynamicKeyLoader.getPostTargetsAsList()));
throw new PostTargetException("PostTarget not found. Possible values are: " + String.join(", ", dynamicKeyLoader.getPostTargetsAsList()));
}
log.info("Creating post target {} pointing towards {}", name, targetChannel);
postTargetRepository.save(PostTarget.builder().name(name).channelReference(targetChannel).serverReference(server).build());
@@ -44,7 +42,7 @@ public class PostTargetManagementBean implements PostTargetManagement {
@Override
public void createOrUpdate(String name, AServer server, AChannel targetChannel) {
PostTarget existing = postTargetRepository.findPostTargetByName(name);
PostTarget existing = postTargetRepository.findPostTargetByNameAndServerReference(name, server);
if(existing == null){
this.createPostTarget(name, server, targetChannel);
} else {
@@ -68,7 +66,7 @@ public class PostTargetManagementBean implements PostTargetManagement {
@Override
@Cacheable("posttargets")
public PostTarget getPostTarget(String name, AServer server) {
return postTargetRepository.findPostTargetByName(name);
return postTargetRepository.findPostTargetByNameAndServerReference(name, server);
}
@Override

View File

@@ -1,7 +1,6 @@
package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.management.RoleManagementService;
import dev.sheldan.abstracto.repository.RoleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

View File

@@ -1,9 +1,5 @@
package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.management.ChannelManagementService;
import dev.sheldan.abstracto.core.management.PostTargetManagement;
import dev.sheldan.abstracto.core.management.ServerManagementService;
import dev.sheldan.abstracto.core.management.UserManagementService;
import dev.sheldan.abstracto.core.models.database.*;
import dev.sheldan.abstracto.repository.ServerRepository;
import lombok.extern.slf4j.Slf4j;

View File

@@ -1,7 +1,5 @@
package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.management.ServerManagementService;
import dev.sheldan.abstracto.core.management.UserManagementService;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;

View File

@@ -1,7 +1,7 @@
package dev.sheldan.abstracto.listener;
import dev.sheldan.abstracto.core.management.ChannelManagementService;
import dev.sheldan.abstracto.core.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.models.AChannelType;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;

View File

@@ -1,63 +0,0 @@
package dev.sheldan.abstracto.listener;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.management.ServerManagementService;
import dev.sheldan.abstracto.templating.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent;
import net.dv8tion.jda.api.events.guild.member.GuildMemberLeaveEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.util.HashMap;
@Service
@Slf4j
public class JoinLeaveListener extends ListenerAdapter {
private static final String USER_JOIN_TEMPLATE = "user_join";
private static final String USER_LEAVE_TEMPLATE = "user_leave";
private static final String JOIN_LOG_TARGET = "joinLog";
private static final String LEAVE_LOG_TARGET = "leaveLog";
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private TemplateService templateService;
@Autowired
private PostTargetService postTargetService;
@Override
@Transactional
public void onGuildMemberJoin(@Nonnull GuildMemberJoinEvent event) {
String text = getRenderedEvent(event.getUser(), USER_JOIN_TEMPLATE);
postTargetService.sendTextInPostTarget(text, JOIN_LOG_TARGET, event.getGuild().getIdLong());
}
@Override
@Transactional
public void onGuildMemberLeave(@Nonnull GuildMemberLeaveEvent event) {
String text = getRenderedEvent(event.getUser(), USER_LEAVE_TEMPLATE);
postTargetService.sendTextInPostTarget(text, LEAVE_LOG_TARGET, event.getGuild().getIdLong());
}
@NotNull
private HashMap<String, Object> getUserParameter(@Nonnull User user) {
HashMap<String, Object> parameters = new HashMap<>();
parameters.put("user", user);
parameters.put("userMention", user.getAsMention());
return parameters;
}
private String getRenderedEvent(User user, String templateName) {
HashMap<String, Object> parameters = getUserParameter(user);
return templateService.renderTemplate(templateName, parameters);
}
}

View File

@@ -0,0 +1,40 @@
package dev.sheldan.abstracto.listener;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.listener.JoinListener;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.util.List;
@Service
@Slf4j
public class JoinListenerBean extends ListenerAdapter {
@Autowired
private List<JoinListener> listenerList;
@Autowired
private FeatureFlagService featureFlagService;
@Override
@Transactional
public void onGuildMemberJoin(@Nonnull GuildMemberJoinEvent event) {
listenerList.forEach(joinListener -> {
if (!featureFlagService.isFeatureEnabled(joinListener.getFeature(), event.getGuild().getIdLong())) {
return;
}
try {
joinListener.execute(event.getMember(), event.getGuild());
} catch (AbstractoRunTimeException e) {
log.error("Listener {} failed with exception:", joinListener.getClass().getName(), e);
}
});
}
}

View File

@@ -0,0 +1,40 @@
package dev.sheldan.abstracto.listener;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.listener.LeaveListener;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.guild.member.GuildMemberLeaveEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.util.List;
@Service
@Slf4j
public class LeaveListenerBean extends ListenerAdapter {
@Autowired
private List<LeaveListener> listenerList;
@Autowired
private FeatureFlagService featureFlagService;
@Override
@Transactional
public void onGuildMemberLeave(@Nonnull GuildMemberLeaveEvent event) {
listenerList.forEach(leaveListener -> {
if(!featureFlagService.isFeatureEnabled(leaveListener.getFeature(), event.getGuild().getIdLong())) {
return;
}
try {
leaveListener.execute(event.getMember(), event.getGuild());
} catch (AbstractoRunTimeException e) {
log.error("Listener {} failed with exception:", leaveListener.getClass().getName(), e);
}
});
}
}

View File

@@ -1,21 +0,0 @@
package dev.sheldan.abstracto.listener;
import dev.sheldan.abstracto.core.service.MessageCache;
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Nonnull;
@Component
public class MessageCacheListener extends ListenerAdapter {
@Autowired
private MessageCache messageCache;
@Override
public void onGuildMessageReceived(@Nonnull GuildMessageReceivedEvent event) {
messageCache.putMessageInCache(event.getMessage());
}
}

View File

@@ -1,7 +1,9 @@
package dev.sheldan.abstracto.listener;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.listener.MessageDeletedListener;
import dev.sheldan.abstracto.core.models.CachedMessage;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.MessageCache;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.guild.GuildMessageDeleteEvent;
@@ -25,6 +27,9 @@ public class MessageDeletedListenerBean extends ListenerAdapter {
@Autowired
private MessageDeletedListenerBean self;
@Autowired
private FeatureFlagService featureFlagService;
@Override
@Transactional
public void onGuildMessageDelete(@Nonnull GuildMessageDeleteEvent event) {
@@ -36,10 +41,13 @@ public class MessageDeletedListenerBean extends ListenerAdapter {
@Transactional
public void executeListener(CachedMessage cachedMessage) {
listener.forEach(messageDeletedListener -> {
if(!featureFlagService.isFeatureEnabled(messageDeletedListener.getFeature(), cachedMessage.getServerId())) {
return;
}
try {
messageDeletedListener.execute(cachedMessage);
} catch (Exception e) {
log.warn("Listener {} failed with exception:", messageDeletedListener.getClass().getName(), e);
} catch (AbstractoRunTimeException e) {
log.error("Listener {} failed with exception:", messageDeletedListener.getClass().getName(), e);
}
});
}

View File

@@ -1,119 +0,0 @@
package dev.sheldan.abstracto.listener;
import dev.sheldan.abstracto.core.management.ChannelManagementService;
import dev.sheldan.abstracto.core.management.ServerManagementService;
import dev.sheldan.abstracto.core.management.UserManagementService;
import dev.sheldan.abstracto.core.models.CachedMessage;
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.models.listener.MessageEmbeddedModel;
import dev.sheldan.abstracto.core.service.Bot;
import dev.sheldan.abstracto.core.service.MessageCache;
import dev.sheldan.abstracto.core.models.embed.MessageToSend;
import dev.sheldan.abstracto.templating.TemplateService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Component
@Slf4j
public class MessageEmbedListener extends ListenerAdapter {
@Autowired
private MessageCache messageCache;
public static final String MESSAGE_EMBED_TEMPLATE = "message";
private Pattern messageRegex = Pattern.compile("(?<whole>https://discordapp.com/channels/(?<server>\\d+)/(?<channel>\\d+)/(?<message>\\d+)(?:.*?))+");
@Autowired
private MessageEmbedListener self;
@Autowired
private ChannelManagementService channelManagementService;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private UserManagementService userManagementService;
@Autowired
private Bot bot;
@Autowired
private TemplateService templateService;
@Override
@Transactional
public void onGuildMessageReceived(@Nonnull GuildMessageReceivedEvent event) {
String messageRaw = event.getMessage().getContentRaw();
Matcher matcher = messageRegex.matcher(messageRaw);
boolean matched = false;
while(matcher.find()) {
matched = true;
String serverId = matcher.group("server");
String channelId = matcher.group("channel");
String messageId = matcher.group("message");
String wholeLink = matcher.group("whole");
if(event.getMessage().getGuild().getId().equals(serverId)) {
Long serverIdLong = Long.parseLong(serverId);
Long channelIdLong = Long.parseLong(channelId);
Long messageIdLong = Long.parseLong(messageId);
messageRaw = messageRaw.replace(wholeLink, "");
messageCache.getMessageFromCache(serverIdLong, channelIdLong, messageIdLong).thenAccept(cachedMessage -> {
self.createEmbedAndPostEmbed(event, cachedMessage);
});
}
}
if(StringUtils.isBlank(messageRaw) && matched) {
event.getMessage().delete().queue();
}
}
@NotNull
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createEmbedAndPostEmbed(@Nonnull GuildMessageReceivedEvent event, CachedMessage message) {
MessageEmbeddedModel messageEmbeddedModel = buildTemplateParameter(event, message);
MessageToSend embed = templateService.renderEmbedTemplate(MESSAGE_EMBED_TEMPLATE, messageEmbeddedModel);
if(StringUtils.isBlank(embed.getMessage())) {
event.getChannel().sendMessage(embed.getEmbed()).queue();
} else {
event.getChannel().sendMessage(embed.getMessage()).embed(embed.getEmbed()).queue();
}
}
private MessageEmbeddedModel buildTemplateParameter(GuildMessageReceivedEvent event, CachedMessage embeddedMessage) {
AChannel channel = channelManagementService.loadChannel(event.getChannel().getIdLong());
AServer server = serverManagementService.loadOrCreate(event.getGuild().getIdLong());
AUserInAServer user = userManagementService.loadUser(event.getMember());
Member author = bot.getMemberInServer(embeddedMessage.getServerId(), embeddedMessage.getAuthorId());
return MessageEmbeddedModel
.builder()
.channel(channel)
.server(server)
.member(event.getMember())
.aUserInAServer(user)
.author(author)
.sourceChannel(event.getChannel())
.embeddingUser(event.getMember())
.user(user.getUserReference())
.textChannel(event.getChannel())
.guild(event.getGuild())
.embeddedMessage(embeddedMessage)
.build();
}
}

View File

@@ -0,0 +1,41 @@
package dev.sheldan.abstracto.listener;
import dev.sheldan.abstracto.core.listener.MessageReceivedListener;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.MessageCache;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import java.util.List;
@Component
@Slf4j
public class MessageReceivedListenerBean extends ListenerAdapter {
@Autowired
private MessageCache messageCache;
@Autowired
private List<MessageReceivedListener> listenerList;
@Autowired
private FeatureFlagService featureFlagService;
@Override
@Transactional
public void onGuildMessageReceived(@Nonnull GuildMessageReceivedEvent event) {
messageCache.putMessageInCache(event.getMessage());
listenerList.forEach(messageReceivedListener -> {
if(!featureFlagService.isFeatureEnabled(messageReceivedListener.getFeature(), event.getGuild().getIdLong())) {
return;
}
messageReceivedListener.execute(event.getMessage());
});
}
}

View File

@@ -1,7 +1,9 @@
package dev.sheldan.abstracto.listener;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.listener.MessageTextUpdatedListener;
import dev.sheldan.abstracto.core.models.CachedMessage;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.MessageCache;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
@@ -27,6 +29,9 @@ public class MessageUpdatedListener extends ListenerAdapter {
@Autowired
private MessageUpdatedListener self;
@Autowired
private FeatureFlagService featureFlagService;
@Override
public void onGuildMessageUpdate(@Nonnull GuildMessageUpdateEvent event) {
Message message = event.getMessage();
@@ -40,7 +45,14 @@ public class MessageUpdatedListener extends ListenerAdapter {
@Transactional
public void executeListener(Message message, CachedMessage cachedMessage) {
listener.forEach(messageTextUpdatedListener -> {
messageTextUpdatedListener.execute(cachedMessage, message);
if(!featureFlagService.isFeatureEnabled(messageTextUpdatedListener.getFeature(), message.getGuild().getIdLong())) {
return;
}
try {
messageTextUpdatedListener.execute(cachedMessage, message);
} catch (AbstractoRunTimeException e) {
log.error(String.format("Failed to execute listener. %s", messageTextUpdatedListener.getClass().getName()), e);
}
});
}
}

View File

@@ -1,8 +1,10 @@
package dev.sheldan.abstracto.listener;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.listener.ReactedAddedListener;
import dev.sheldan.abstracto.core.listener.ReactedRemovedListener;
import dev.sheldan.abstracto.core.management.UserManagementService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.management.UserManagementService;
import dev.sheldan.abstracto.core.models.CachedMessage;
import dev.sheldan.abstracto.core.models.CachedReaction;
import dev.sheldan.abstracto.core.models.database.AUser;
@@ -46,6 +48,8 @@ public class ReactionUpdatedListener extends ListenerAdapter {
@Autowired
private ReactionUpdatedListener self;
@Autowired
private FeatureFlagService featureFlagService;
@Override
@Transactional
@@ -91,13 +95,16 @@ public class ReactionUpdatedListener extends ListenerAdapter {
public void callAddedListeners(@Nonnull GuildMessageReactionAddEvent event, CachedMessage cachedMessage, CachedReaction reaction) {
AUserInAServer userInAServer = userManagementService.loadUser(event.getGuild().getIdLong(), event.getUserIdLong());
addReactionIfNotThere(cachedMessage, reaction, userInAServer.getUserReference());
try {
addedListenerList.forEach(reactedAddedListener -> {
addedListenerList.forEach(reactedAddedListener -> {
if(!featureFlagService.isFeatureEnabled(reactedAddedListener.getFeature(), event.getGuild().getIdLong())) {
return;
}
try {
reactedAddedListener.executeReactionAdded(cachedMessage, event.getReaction(), userInAServer);
});
} catch (Exception e) {
log.warn("Exception on reaction added handler:", e);
}
} catch (AbstractoRunTimeException e) {
log.warn(String.format("Failed to execute reaction added listener %s.", reactedAddedListener.getClass().getName()), e);
}
});
}
@Override
@@ -120,7 +127,14 @@ public class ReactionUpdatedListener extends ListenerAdapter {
AUserInAServer userInAServer = userManagementService.loadUser(event.getGuild().getIdLong(), event.getUserIdLong());
removeReactionIfThere(cachedMessage, reaction, userInAServer.getUserReference());
reactionRemovedListener.forEach(reactedAddedListener -> {
reactedAddedListener.executeReactionRemoved(cachedMessage, event.getReaction(), userInAServer);
if(!featureFlagService.isFeatureEnabled(reactedAddedListener.getFeature(), event.getGuild().getIdLong())) {
return;
}
try {
reactedAddedListener.executeReactionRemoved(cachedMessage, event.getReaction(), userInAServer);
} catch (AbstractoRunTimeException e) {
log.warn(String.format("Failed to execute reaction removed listener %s.", reactedAddedListener.getClass().getName()), e);
}
});
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.repository;
import dev.sheldan.abstracto.core.models.database.AFeatureFlag;
import dev.sheldan.abstracto.core.models.database.AServer;
import org.springframework.data.jpa.repository.JpaRepository;
public interface FeatureFlagRepository extends JpaRepository<AFeatureFlag, Long> {
AFeatureFlag findByServerAndKey(AServer server, String key);
}

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.repository;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.PostTarget;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@@ -7,6 +8,6 @@ import org.springframework.stereotype.Repository;
@Repository
public interface PostTargetRepository extends JpaRepository<PostTarget, Long> {
PostTarget findPostTargetByName(String name);
PostTarget findPostTargetByNameAndServerReference(String name, AServer server);
}