added jda utilities dependency

added modmail with some simple features (chat between user and moderator), and initial server selection (currently allows all servers, and does not consider the modmail feature)
added the initial commands, not all of them hold logic currently
added some checks to not crash when a command has null configuration
changed systemconfig to also have a long value, because double is not fit for all cases
added locking mechanism, to effectively lock the whole table without using the ... proper locking mechanism (needs to be reviewed)
changed interface in channel service to be a message channel instead of a textChannel
changed starboard post to be a user in a server instead of only user
This commit is contained in:
Sheldan
2020-05-06 14:17:44 +02:00
parent cf713cc561
commit a06006d763
99 changed files with 1506 additions and 48 deletions

View File

@@ -26,9 +26,11 @@ public class CommandConfigListener implements ServerConfigListener {
@Override
public void updateServerConfig(AServer server) {
commandList.forEach(command -> {
ACommand aCommand = commandManagementService.findCommandByName(command.getConfiguration().getName());
if(!commandInServerManagementService.doesCommandExistInServer(aCommand, server)) {
commandInServerManagementService.crateCommandInServer(aCommand, server);
if(command.getConfiguration() != null) {
ACommand aCommand = commandManagementService.findCommandByName(command.getConfiguration().getName());
if(!commandInServerManagementService.doesCommandExistInServer(aCommand, server)) {
commandInServerManagementService.crateCommandInServer(aCommand, server);
}
}
});
}

View File

@@ -4,6 +4,7 @@ import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.service.CommandService;
import dev.sheldan.abstracto.core.command.service.management.FeatureManagementService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
@@ -13,6 +14,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Component
@Slf4j
public class CommandCreationListener {
@Autowired
@@ -38,6 +40,10 @@ public class CommandCreationListener {
}
});
commandList.forEach(command -> {
if(command.getConfiguration() == null) {
log.warn("Command {} has null configuration.", command);
return;
}
if(!commandService.doesCommandExist(command.getConfiguration().getName())) {
commandService.createCommand(command.getConfiguration().getName(), command.getConfiguration().getModule(), command.getFeature());
}

View File

@@ -33,6 +33,9 @@ public class CommandManager implements CommandRegistry {
public Command findCommandByParameters(String name, UnParsedCommandParameter unParsedCommandParameter) {
Optional<Command> commandOptional = commands.stream().filter((Command o )-> {
CommandConfiguration commandConfiguration = o.getConfiguration();
if(commandConfiguration == null) {
return false;
}
if(!commandNameMatches(name, commandConfiguration)) {
return false;
}
@@ -88,7 +91,8 @@ public class CommandManager implements CommandRegistry {
public List<Command> getAllCommandsFromModule(ModuleInterface moduleInterface) {
List<Command> commandsFromModule = new ArrayList<>();
this.getAllCommands().forEach(command -> {
if(command.getConfiguration().getModule().equals(moduleInterface.getInfo().getName())){
CommandConfiguration configuration = command.getConfiguration();
if(configuration != null && configuration.getModule().equals(moduleInterface.getInfo().getName())){
commandsFromModule.add(command);
}
});

View File

@@ -11,10 +11,12 @@ import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.database.AFeature;
import dev.sheldan.abstracto.core.models.database.ARole;
import dev.sheldan.abstracto.core.models.database.AServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class CommandServiceBean implements CommandService {
@Autowired
@@ -32,6 +34,10 @@ public class CommandServiceBean implements CommandService {
@Override
public ACommand createCommand(String name, String moduleName, FeatureEnum featureEnum) {
AModule module = moduleManagementService.getOrCreate(moduleName);
if(featureEnum == null) {
log.warn("Command {} in module {} has no feature.", name, moduleName);
return null;
}
AFeature feature = featureManagementService.getFeature(featureEnum.getKey());
return commandManagementService.createCommand(name, module, feature);
}

View File

@@ -16,7 +16,7 @@ import java.util.Arrays;
import java.util.List;
@Component
public class SetNumber extends AbstractConditionableCommand {
public class SetFloat extends AbstractConditionableCommand {
@Autowired
private ConfigService configService;
@@ -37,7 +37,7 @@ public class SetNumber extends AbstractConditionableCommand {
List<Parameter> parameters = Arrays.asList(channelGroupName, channelToAdd);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("setNumber")
.name("setFloat")
.module(ConfigModuleInterface.CONFIG)
.parameters(parameters)
.templated(true)

View File

@@ -0,0 +1,53 @@
package dev.sheldan.abstracto.core.commands.config;
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.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.service.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class SetLong extends AbstractConditionableCommand {
@Autowired
private ConfigService configService;
@Override
public CommandResult execute(CommandContext commandContext) {
String key = (String) commandContext.getParameters().getParameters().get(0);
Long value = (Long) commandContext.getParameters().getParameters().get(1);
configService.setLongValue(key, commandContext.getGuild().getIdLong(), value);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter channelGroupName = Parameter.builder().name("key").type(String.class).description("The key to change.").build();
Parameter channelToAdd = Parameter.builder().name("value").type(Long.class).description("The numeric value to use for the config.").build();
List<Parameter> parameters = Arrays.asList(channelGroupName, channelToAdd);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("setLong")
.module(ConfigModuleInterface.CONFIG)
.parameters(parameters)
.templated(true)
.help(helpInfo)
.causesReaction(true)
.build();
}
@Override
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -39,7 +39,7 @@ public class ChannelListener extends ListenerAdapter {
log.info("Handling channel created event. Channel {}, Server {}", event.getChannel().getIdLong(), event.getGuild().getIdLong());
AServer serverObject = serverRepository.getOne(event.getGuild().getIdLong());
TextChannel createdChannel = event.getChannel();
AChannelType type = AChannel.getAChannelType(createdChannel.getType());
AChannelType type = AChannelType.getAChannelType(createdChannel.getType());
channelManagementService.createChannel(createdChannel.getIdLong(), type, serverObject);
}
}

View File

@@ -1,14 +1,15 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.service.BotService;
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.events.message.priv.PrivateMessageReceivedEvent;
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;
@@ -23,9 +24,15 @@ public class MessageReceivedListenerBean extends ListenerAdapter {
@Autowired
private List<MessageReceivedListener> listenerList;
@Autowired
private List<PrivateMessageReceivedListener> privateMessageReceivedListeners;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private BotService botService;
@Override
public void onGuildMessageReceived(@Nonnull GuildMessageReceivedEvent event) {
messageCache.putMessageInCache(event.getMessage());
@@ -42,5 +49,17 @@ public class MessageReceivedListenerBean extends ListenerAdapter {
});
}
@Override
public void onPrivateMessageReceived(@Nonnull PrivateMessageReceivedEvent event) {
if(event.getAuthor().getId().equals(botService.getInstance().getSelfUser().getId())) {
return;
}
privateMessageReceivedListeners.forEach(messageReceivedListener -> {
try {
messageReceivedListener.execute(event.getMessage());
} catch (Exception e) {
log.error("Listener {} had exception when executing.", messageReceivedListener, e);
}
});
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.model.database;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Entity
@Table(name = "lock")
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class ALock {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}

View File

@@ -0,0 +1,18 @@
package dev.sheldan.abstracto.core.repository;
import dev.sheldan.abstracto.core.model.database.ALock;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import javax.persistence.LockModeType;
@Repository
public interface LockRepository extends JpaRepository<ALock, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select a from ALock a where a.id = :id")
ALock findArticleForRead(@Param("id") Long id);
}

View File

@@ -8,6 +8,7 @@ import org.springframework.data.jpa.repository.QueryHints;
import org.springframework.stereotype.Repository;
import javax.persistence.QueryHint;
import java.util.List;
@Repository
public interface UserInServerRepository extends JpaRepository<AUserInAServer, Long> {
@@ -17,4 +18,7 @@ public interface UserInServerRepository extends JpaRepository<AUserInAServer, Lo
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
boolean existsByServerReferenceAndUserReference(AServer server, AUser user);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
List<AUserInAServer> findByUserReference(AUser user);
}

View File

@@ -3,13 +3,11 @@ 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.models.database.AServer;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.models.database.AChannel;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.requests.restaction.MessageAction;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -33,7 +31,7 @@ public class ChannelServiceBean implements ChannelService {
}
@Override
public void sendTextInAChannel(String text, TextChannel channel) {
public void sendTextInAChannel(String text, MessageChannel channel) {
sendTextInAChannelFuture(text, channel);
}
@@ -55,7 +53,7 @@ public class ChannelServiceBean implements ChannelService {
}
@Override
public CompletableFuture<Message> sendTextInAChannelFuture(String text, TextChannel channel) {
public CompletableFuture<Message> sendTextInAChannelFuture(String text, MessageChannel channel) {
return channel.sendMessage(text).submit();
}
@@ -77,7 +75,7 @@ public class ChannelServiceBean implements ChannelService {
}
@Override
public CompletableFuture<Message> sendEmbedInAChannelFuture(MessageEmbed embed, TextChannel channel) {
public CompletableFuture<Message> sendEmbedInAChannelFuture(MessageEmbed embed, MessageChannel channel) {
return channel.sendMessage(embed).submit();
}
@@ -91,7 +89,7 @@ public class ChannelServiceBean implements ChannelService {
}
@Override
public List<CompletableFuture<Message>> sendMessageToEndInTextChannel(MessageToSend messageToSend, TextChannel textChannel) {
public List<CompletableFuture<Message>> sendMessageToEndInTextChannel(MessageToSend messageToSend, MessageChannel textChannel) {
String messageText = messageToSend.getMessage();
List<CompletableFuture<Message>> futures = new ArrayList<>();
if(StringUtils.isBlank(messageText)) {
@@ -130,7 +128,7 @@ public class ChannelServiceBean implements ChannelService {
}
@Override
public void editMessageInAChannel(MessageToSend messageToSend, TextChannel channel, Long messageId) {
public void editMessageInAChannel(MessageToSend messageToSend, MessageChannel channel, Long messageId) {
MessageAction messageAction;
if(!StringUtils.isBlank(messageToSend.getMessage())) {
messageAction = channel.editMessageById(messageId, messageToSend.getMessage());
@@ -146,4 +144,18 @@ public class ChannelServiceBean implements ChannelService {
}
messageAction.queue();
}
@Override
public CompletableFuture<TextChannel> createTextChannel(String name, AServer server, Long categoryId) {
Optional<Guild> guildById = botService.getGuildById(server.getId());
if(guildById.isPresent()) {
Guild guild = guildById.get();
Category categoryById = guild.getCategoryById(categoryId);
if(categoryById != null) {
return categoryById.createTextChannel(name).submit();
}
return null;
}
return null;
}
}

View File

@@ -17,6 +17,11 @@ public class ConfigServiceBean implements ConfigService{
return getDoubleValue(name, serverId, 0D);
}
@Override
public Long getLongValue(String name, Long serverId) {
return getLongValue(name, serverId, 0L);
}
@Override
public Double getDoubleValue(String name, Long serverId, Double defaultValue) {
AConfig config = configManagementService.loadConfig(serverId, name);
@@ -35,6 +40,15 @@ public class ConfigServiceBean implements ConfigService{
return config.getStringValue();
}
@Override
public Long getLongValue(String name, Long serverId, Long defaultValue) {
AConfig config = configManagementService.loadConfig(serverId, name);
if(config == null) {
return defaultValue;
}
return config.getLongValue();
}
@Override
public void setDoubleValue(String name, Long serverId, Double value) {
if(configManagementService.configExists(serverId, name)) {
@@ -44,6 +58,15 @@ public class ConfigServiceBean implements ConfigService{
}
}
@Override
public void setLongValue(String name, Long serverId, Long value) {
if(configManagementService.configExists(serverId, name)) {
configManagementService.setLongValue(serverId, name, value);
} else {
throw new ConfigurationException(String.format("Key %s does not exist.", name));
}
}
@Override
public void setStringValue(String name, Long serverId, String value) {
if(configManagementService.configExists(serverId, name)) {

View File

@@ -0,0 +1,19 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.command.models.TableLocks;
import dev.sheldan.abstracto.core.repository.LockRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class LockServiceBean implements LockService {
@Autowired
private LockRepository lockRepository;
@Override
public void lockTable(TableLocks toLock) {
int ordinal = toLock.ordinal();
lockRepository.findArticleForRead((long) ordinal);
}
}

View File

@@ -103,7 +103,7 @@ public class StartupServiceBean implements Startup {
newChannels.forEach(aLong -> {
GuildChannel channel1 = available.stream().filter(channel -> channel.getIdLong() == aLong).findFirst().get();
log.trace("Adding new channel: {}", aLong);
AChannelType type = AChannel.getAChannelType(channel1.getType());
AChannelType type = AChannelType.getAChannelType(channel1.getType());
channelManagementService.createChannel(channel1.getIdLong(), type, existingServer);
});

View File

@@ -1,9 +1,11 @@
package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.command.models.TableLocks;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AChannelType;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.repository.ChannelRepository;
import dev.sheldan.abstracto.core.service.LockService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -15,6 +17,9 @@ public class ChannelManagementServiceBean implements ChannelManagementService {
@Autowired
private ChannelRepository repository;
@Autowired
private LockService lockService;
@Override
public AChannel loadChannel(Long id) {
return repository.getOne(id);
@@ -22,15 +27,20 @@ public class ChannelManagementServiceBean implements ChannelManagementService {
@Override
public AChannel createChannel(Long id, AChannelType type, AServer server) {
log.info("Creating channel {} with type {}", id, type);
AChannel build = AChannel
.builder()
.id(id)
.type(type)
.server(server)
.deleted(false)
.build();
return repository.save(build);
lockService.lockTable(TableLocks.CHANNELS);
if(!channelExists(id)) {
log.info("Creating channel {} with type {}", id, type);
AChannel build = AChannel
.builder()
.id(id)
.type(type)
.server(server)
.deleted(false)
.build();
return repository.save(build);
} else {
return loadChannel(id);
}
}
@Override
@@ -40,6 +50,11 @@ public class ChannelManagementServiceBean implements ChannelManagementService {
return channel;
}
@Override
public boolean channelExists(Long id) {
return repository.existsById(id);
}
@Override
public void removeChannel(Long id) {
log.info("Deleting channel {}", id);

View File

@@ -65,6 +65,19 @@ public class ConfigManagementServiceBean implements ConfigManagementService {
return config;
}
@Override
public AConfig createConfig(Long serverId, String name, Long value) {
AServer server = serverManagementService.loadOrCreate(serverId);
AConfig config = AConfig
.builder()
.longValue(value)
.server(server)
.name(name)
.build();
configRepository.save(config);
return config;
}
@Override
public AConfig createIfNotExists(Long serverId, String name, String value) {
AConfig config = loadConfig(serverId, name);
@@ -100,6 +113,13 @@ public class ConfigManagementServiceBean implements ConfigManagementService {
return config;
}
@Override
public AConfig setLongValue(Long serverId, String name, Long value) {
AConfig config = loadConfig(serverId, name);
config.setLongValue(value);
return config;
}
@Override
public AConfig setStringValue(Long serverId, String name, String value) {
AConfig config = loadConfig(serverId, name);

View File

@@ -9,6 +9,8 @@ import net.dv8tion.jda.api.entities.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@Slf4j
public class UserInServerManagementServiceBean implements UserInServerManagementService {
@@ -55,4 +57,10 @@ public class UserInServerManagementServiceBean implements UserInServerManagement
userInServerRepository.save(aUserInAServer);
return aUserInAServer;
}
@Override
public List<AUserInAServer> getUserInAllServers(Long userId) {
AUser user = userManagementService.loadUser(userId);
return userInServerRepository.findByUserReference(user);
}
}