[AB-150] creating repost detection feature including configuration and documentation

adding http and hash service
adding ability to add default emotes to a message to message service
adding message embedded listener to wrap the embedded event
adding custom channel groups which can be defined by modules, in case a change on a channel group (only created and updated) happens a listener is available in order to sync the state in dependant areas
changing command receiver re-throwing abstracto runtime exceptions in order to display them better
changing channel group parameter handler to throw an exception in case the channel group was not found
adding User in a server parameter handler
split channel not found exception to be able to differentiate between not found in database and not found in guild
changing exception handling in command received handler to handle the case for only one parameter handler future which failed (the whole single future failed, which was not reported)
changing parameter type of `removeFromChannelGroup` to AChannel in order to be able to delete channels in the database via ID
moving method to mock utils for mocking consumer
removing parameter validation from commands, as it should be done in the command received handler and parameter handlers anyway
This commit is contained in:
Sheldan
2020-12-04 00:38:18 +01:00
parent e966c710ce
commit 325264a325
249 changed files with 5310 additions and 686 deletions

View File

@@ -14,6 +14,7 @@ import dev.sheldan.abstracto.core.command.service.ExceptionService;
import dev.sheldan.abstracto.core.command.service.PostCommandExecution;
import dev.sheldan.abstracto.core.command.execution.*;
import dev.sheldan.abstracto.core.command.execution.UnParsedCommandParameter;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.database.*;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.RoleService;
@@ -108,10 +109,6 @@ public class CommandReceivedHandler extends ListenerAdapter {
parsingFuture.thenAccept(parsedParameters ->
self.executeCommand(event, foundCommand, parsedParameters)
).exceptionally(throwable -> {
reportException(event, foundCommand, throwable, "Exception when executing command.");
return null;
});
parsingFuture.exceptionally(throwable -> {
self.reportException(event, foundCommand, throwable, "Exception when parsing command.");
return null;
});
@@ -255,9 +252,9 @@ public class CommandReceivedHandler extends ListenerAdapter {
boolean handlerMatched = false;
for (CommandParameterHandler handler : orderedHandlers) {
try {
if(handler.handles(param.getType())) {
if (handler.handles(param.getType())) {
handlerMatched = true;
if(handler.async()) {
if (handler.async()) {
CompletableFuture future = handler.handleAsync(value, iterators, param.getType(), message);
futures.add(future);
parsedParameters.add(future);
@@ -266,6 +263,8 @@ public class CommandReceivedHandler extends ListenerAdapter {
}
break;
}
} catch (AbstractoRunTimeException abstractoRunTimeException) {
throw abstractoRunTimeException;
} catch (Exception e) {
log.warn("Failed to parse parameter with exception.", e);
throw new IncorrectParameterException(command, param.getName());
@@ -286,7 +285,9 @@ public class CommandReceivedHandler extends ListenerAdapter {
}
if(!futures.isEmpty()) {
return FutureUtils.toSingleFuture(futures).thenApply(aVoid -> {
CompletableFuture<Parameters> multipleFuturesFuture = new CompletableFuture<>();
CompletableFuture<Void> combinedFuture = FutureUtils.toSingleFuture(futures);
combinedFuture.thenAccept(aVoid -> {
List<Object> usableParameters = parsedParameters.stream().map(o -> {
if(o instanceof CompletableFuture) {
return ((CompletableFuture) o).join();
@@ -294,8 +295,14 @@ public class CommandReceivedHandler extends ListenerAdapter {
return o;
}
}).collect(Collectors.toList());
return Parameters.builder().parameters(usableParameters).build();
multipleFuturesFuture.complete(Parameters.builder().parameters(usableParameters).build());
});
combinedFuture.exceptionally(throwable -> {
multipleFuturesFuture.completeExceptionally(throwable);
return null;
});
return multipleFuturesFuture;
} else {
Parameters parameters = Parameters.builder().parameters(parsedParameters).build();
return CompletableFuture.completedFuture(parameters);

View File

@@ -1,17 +1,18 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.handler.provided.AChanelParameterHandler;
import dev.sheldan.abstracto.core.command.handler.provided.AChannelParameterHandler;
import dev.sheldan.abstracto.core.command.handler.provided.TextChannelParameterHandler;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class AChannelParameterHandlerImpl implements AChanelParameterHandler {
public class AChannelParameterHandlerImpl implements AChannelParameterHandler {
@Autowired
private TextChannelParameterHandler textChannelParameterHandler;
@@ -19,6 +20,9 @@ public class AChannelParameterHandlerImpl implements AChanelParameterHandler {
@Autowired
private ChannelService channelService;
@Autowired
private ChannelManagementService channelManagementService;
@Override
public boolean handles(Class clazz) {
return clazz.equals(AChannel.class);
@@ -27,7 +31,13 @@ public class AChannelParameterHandlerImpl implements AChanelParameterHandler {
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
TextChannel textChannel = (TextChannel) textChannelParameterHandler.handle(input, iterators, clazz, context);
return channelService.getFakeChannelFromTextChannel(textChannel);
if(textChannel == null) {
Long channelId = Long.parseLong(input);
AChannel actualInstance = channelManagementService.loadChannel(channelId);
return AChannel.builder().fake(true).id(actualInstance.getId()).build();
} else {
return channelService.getFakeChannelFromTextChannel(textChannel);
}
}
@Override

View File

@@ -0,0 +1,62 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.handler.provided.AUserInAServerParameterHandler;
import dev.sheldan.abstracto.core.command.handler.provided.MemberParameterHandler;
import dev.sheldan.abstracto.core.exception.UserInServerNotFoundException;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.CompletableFuture;
@Component
public class AUserInAServerParameterHandlerImpl implements AUserInAServerParameterHandler {
@Autowired
private MemberParameterHandler memberParameterHandler;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Override
public CompletableFuture handleAsync(String input, CommandParameterIterators iterators, Class clazz, Message context) {
CompletableFuture<AUserInAServer> future = new CompletableFuture<>();
memberParameterHandler.handleAsync(input, iterators, Member.class, context).whenComplete((o, throwable) -> {
try {
AUserInAServer actualInstance;
if (throwable == null) {
Member member = (Member) o;
actualInstance = userInServerManagementService.loadUser(member);
} else {
Long userId = Long.parseLong(input);
actualInstance = userInServerManagementService.loadAUserInAServerOptional(context.getGuild().getIdLong(), userId).orElseThrow(() -> new UserInServerNotFoundException(0L));
}
future.complete(AUserInAServer.builder().userInServerId(actualInstance.getUserInServerId()).build());
} catch (Exception e) {
// we need to do it like this, because when complete only returns the exception it got in case two exceptions happen
// so if the first exception happens in handleAsync, and we also throw one, it will _not_ get reported, because the other exception overshadows it
future.completeExceptionally(e);
}
});
return future;
}
@Override
public boolean handles(Class clazz) {
return clazz.equals(AUserInAServer.class);
}
@Override
public boolean async() {
return true;
}
@Override
public Integer getPriority() {
return CommandConstants.CORE_HANDLER_PRIORITY;
}
}

View File

@@ -0,0 +1,51 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.exception.ChannelGroupNotFoundException;
import dev.sheldan.abstracto.core.command.handler.provided.ChannelGroupParameterHandler;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.ChannelGroupType;
import dev.sheldan.abstracto.core.service.management.ChannelGroupManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ChannelGroupParameterHandlerImpl implements ChannelGroupParameterHandler {
@Autowired
private ChannelGroupManagementService channelGroupManagementService;
@Autowired
private ServerManagementService serverManagementService;
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
AServer server = serverManagementService.loadServer(context.getGuild().getIdLong());
AChannelGroup actualInstance = channelGroupManagementService.findByNameAndServerOptional(input, server)
.orElseThrow(() -> new ChannelGroupNotFoundException(input, channelGroupManagementService.getAllAvailableAsString(server)));
ChannelGroupType channelGroupType = ChannelGroupType
.builder()
.id(actualInstance.getChannelGroupType().getId())
.groupTypeKey(actualInstance.getChannelGroupType().getGroupTypeKey())
.build();
return AChannelGroup
.builder()
.id(actualInstance.getId())
.groupName(actualInstance.getGroupName())
.channelGroupType(channelGroupType)
.build();
}
@Override
public boolean handles(Class clazz) {
return clazz.equals(AChannelGroup.class);
}
@Override
public Integer getPriority() {
return CommandConstants.CORE_HANDLER_PRIORITY;
}
}

View File

@@ -0,0 +1,40 @@
package dev.sheldan.abstracto.core.command.handler;
import dev.sheldan.abstracto.core.command.CommandConstants;
import dev.sheldan.abstracto.core.command.handler.provided.ChannelGroupTypeParameterHandler;
import dev.sheldan.abstracto.core.models.database.ChannelGroupType;
import dev.sheldan.abstracto.core.service.management.ChannelGroupTypeManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ChannelGroupTypeParameterHandlerImpl implements ChannelGroupTypeParameterHandler {
@Autowired
private ChannelGroupTypeManagementService channelGroupTypeManagementService;
@Autowired
private ServerManagementService serverManagementService;
@Override
public boolean handles(Class clazz) {
return clazz.equals(ChannelGroupType.class);
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
ChannelGroupType actualGroupType = channelGroupTypeManagementService.findChannelGroupTypeByKey(input);
return ChannelGroupType
.builder()
.groupTypeKey(actualGroupType.getGroupTypeKey())
.id(actualGroupType.getId())
.build();
}
@Override
public Integer getPriority() {
return CommandConstants.CORE_HANDLER_PRIORITY;
}
}

View File

@@ -3,9 +3,9 @@ package dev.sheldan.abstracto.core.command.service;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.config.ModuleInterface;
import dev.sheldan.abstracto.core.command.exception.CommandNotFoundException;
import dev.sheldan.abstracto.core.command.exception.InsufficientParametersException;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.exception.InsufficientParametersException;
import dev.sheldan.abstracto.core.command.execution.UnParsedCommandParameter;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.service.management.DefaultConfigManagementService;

View File

@@ -8,7 +8,9 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.models.database.ChannelGroupType;
import dev.sheldan.abstracto.core.service.ChannelGroupService;
import dev.sheldan.abstracto.core.service.management.ChannelGroupTypeManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -21,17 +23,24 @@ public class CreateChannelGroup extends AbstractConditionableCommand {
@Autowired
private ChannelGroupService channelGroupService;
@Autowired
private ChannelGroupTypeManagementService channelGroupTypeManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
String groupName = (String) commandContext.getParameters().getParameters().get(0);
channelGroupService.createChannelGroup(groupName, commandContext.getGuild().getIdLong());
List<Object> parameters = commandContext.getParameters().getParameters();
String groupName = (String) parameters.get(0);
ChannelGroupType fakeChannelGroupType = (ChannelGroupType) parameters.get(1);
ChannelGroupType actualChannelGroupType = channelGroupTypeManagementService.findChannelGroupTypeByKey(fakeChannelGroupType.getGroupTypeKey());
channelGroupService.createChannelGroup(groupName, commandContext.getGuild().getIdLong(), actualChannelGroupType);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter channelGroupName = Parameter.builder().name("name").type(String.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(channelGroupName);
Parameter channelGroupType = Parameter.builder().name("groupType").type(ChannelGroupType.class).templated(true).build();
List<Parameter> parameters = Arrays.asList(channelGroupName, channelGroupType);
List<String> aliases = Arrays.asList("+ChGroup");
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()

View File

@@ -65,6 +65,7 @@ public class ListChannelGroups extends AbstractConditionableCommand {
ChannelGroupModel channelGroup = ChannelGroupModel
.builder()
.name(group.getGroupName())
.typeKey(group.getChannelGroupType().getGroupTypeKey())
.channels(convertedChannels)
.build();
converted.add(channelGroup);

View File

@@ -8,8 +8,9 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.command.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.service.ChannelGroupService;
import net.dv8tion.jda.api.entities.TextChannel;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -19,22 +20,25 @@ import java.util.List;
@Component
public class RemoveFromChannelGroup extends AbstractConditionableCommand {
@Autowired
private ChannelGroupService channelGroupService;
@Autowired
private ChannelManagementService channelManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
String name = (String) commandContext.getParameters().getParameters().get(0);
TextChannel channel = (TextChannel) commandContext.getParameters().getParameters().get(1);
channelGroupService.removeChannelFromChannelGroup(name, channel);
AChannel fakeChannel = (AChannel) commandContext.getParameters().getParameters().get(1);
AChannel actualChannel = channelManagementService.loadChannel(fakeChannel.getId());
channelGroupService.removeChannelFromChannelGroup(name, actualChannel);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter channelGroupName = Parameter.builder().name("name").type(String.class).build();
Parameter channelToAdd = Parameter.builder().name("channel").type(TextChannel.class).build();
Parameter channelToAdd = Parameter.builder().name("channel").type(AChannel.class).build();
List<Parameter> parameters = Arrays.asList(channelGroupName, channelToAdd);
List<String> aliases = Arrays.asList("rmChChgrp", "chGrpCh-");
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();

View File

@@ -41,7 +41,6 @@ public class DisableMode extends AbstractConditionableCommand {
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
String featureName = (String) commandContext.getParameters().getParameters().get(0);
String modeName = (String) commandContext.getParameters().getParameters().get(1);
FeatureEnum featureEnum = featureConfigService.getFeatureEnum(featureName);

View File

@@ -41,7 +41,6 @@ public class EnableMode extends AbstractConditionableCommand {
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
String featureName = (String) commandContext.getParameters().getParameters().get(0);
String modeName = (String) commandContext.getParameters().getParameters().get(1);
FeatureEnum featureEnum = featureConfigService.getFeatureEnum(featureName);

View File

@@ -42,7 +42,6 @@ public class FeatureModes extends AbstractConditionableCommand {
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
checkParameters(commandContext);
List<FeatureModeDisplay> featureModes;
if(commandContext.getParameters().getParameters().isEmpty()) {
featureModes = featureModeService.getEffectiveFeatureModes(commandContext.getUserInitiatedContext().getServer());

View File

@@ -4,6 +4,7 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
import dev.sheldan.abstracto.core.service.BotService;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
@@ -36,4 +37,9 @@ public class CoreConfig {
Executors.newScheduledThreadPool(threadCount);
return new EventWaiter(scheduledExecutorService, true);
}
@Bean
public OkHttpClient client() {
return new OkHttpClient();
}
}

View File

@@ -2,7 +2,7 @@ package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.exception.ChannelNotInGuildException;
import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
import dev.sheldan.abstracto.core.models.GuildChannelMember;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
@@ -82,7 +82,7 @@ public class MessageDeletedListenerBean extends ListenerAdapter {
GuildChannelMember authorMember = GuildChannelMember
.builder()
.guild(botService.getGuildById(cachedMessage.getServerId()))
.textChannel(botService.getTextChannelFromServerOptional(cachedMessage.getServerId(), cachedMessage.getChannelId()).orElseThrow(() -> new ChannelNotFoundException(cachedMessage.getChannelId())))
.textChannel(botService.getTextChannelFromServerOptional(cachedMessage.getServerId(), cachedMessage.getChannelId()).orElseThrow(() -> new ChannelNotInGuildException(cachedMessage.getChannelId())))
.member(botService.getMemberInServer(cachedMessage.getServerId(), cachedMessage.getAuthorId()))
.build();
listener.forEach(messageDeletedListener -> {

View File

@@ -0,0 +1,84 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.command.service.ExceptionService;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.models.listener.GuildMessageEmbedEventModel;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
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.GuildMessageEmbedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.Comparator;
import java.util.List;
@Component
@Slf4j
public class MessageEmbeddedListenerBean extends ListenerAdapter {
@Autowired
private MessageCache messageCache;
@Autowired
private List<MessageEmbeddedListener> listenerList;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private BotService botService;
@Autowired
private ExceptionService exceptionService;
@Autowired
private MessageEmbeddedListenerBean self;
@Override
public void onGuildMessageEmbed(@NotNull GuildMessageEmbedEvent event) {
GuildMessageEmbedEventModel model = buildModel(event);
listenerList.forEach(messageReceivedListener -> {
try {
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(messageReceivedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
self.executeIndividualGuildMessageReceivedListener(model, messageReceivedListener);
} catch (Exception e) {
log.error("Listener {} had exception when executing.", messageReceivedListener, e);
// exceptionService.reportExceptionToGuildMessageReceivedContext(e, event);
}
});
}
private GuildMessageEmbedEventModel buildModel(GuildMessageEmbedEvent event) {
return GuildMessageEmbedEventModel
.builder()
.channel(event.getChannel())
.embeds(event.getMessageEmbeds())
.messageId(event.getMessageIdLong())
.build();
}
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
public void executeIndividualGuildMessageReceivedListener(GuildMessageEmbedEventModel model, MessageEmbeddedListener messageReceivedListener) {
messageReceivedListener.execute(model);
}
@PostConstruct
public void postConstruct() {
listenerList.sort(Comparator.comparing(Prioritized::getPriority).reversed());
}
}

View File

@@ -0,0 +1,23 @@
package dev.sheldan.abstracto.core.listener.entity;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class ChannelGroupCreatedListenerManager {
@Autowired
private List<ChannelGroupCreatedListener> listener;
@Autowired
private ChannelGroupCreatedListenerManager self;
public void executeListener(AChannelGroup createdGroup){
listener.forEach(channelGroupCreatedListener ->
channelGroupCreatedListener.channelGroupCreated(createdGroup)
);
}
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.abstracto.core.listener.entity;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class ChannelGroupDeletedListenerManager {
@Autowired
private List<ChannelGroupDeletedListener> listener;
public void executeListener(AChannelGroup createdGroup){
listener.forEach(channelGroupCreatedListener ->
channelGroupCreatedListener.channelGroupDeleted(createdGroup)
);
}
}

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.repository;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import dev.sheldan.abstracto.core.models.database.AServer;
import org.springframework.data.jpa.repository.JpaRepository;
@@ -7,15 +8,24 @@ import org.springframework.data.jpa.repository.QueryHints;
import javax.persistence.QueryHint;
import java.util.List;
import java.util.Optional;
public interface ChannelGroupRepository extends JpaRepository<AChannelGroup, Long> {
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
AChannelGroup findByGroupNameAndServer(String name, AServer server);
Optional<AChannelGroup> findByGroupNameAndServer(String name, AServer server);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
Optional<AChannelGroup> findByGroupNameAndServerAndChannelGroupType_GroupTypeKey(String name, AServer server, String groupTyeKey);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
List<AChannelGroup> findByServerAndChannelGroupType_GroupTypeKey(AServer server, String groupTyeKey);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
List<AChannelGroup> findByServer(AServer server);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
boolean existsByGroupNameAndServer(String name, AServer server);
List<AChannelGroup> findAllByChannels(AChannel channel);
}

View File

@@ -0,0 +1,12 @@
package dev.sheldan.abstracto.core.repository;
import dev.sheldan.abstracto.core.models.database.ChannelGroupType;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface ChannelGroupTypeRepository extends JpaRepository<ChannelGroupType, Integer> {
Optional<ChannelGroupType> findByGroupTypeKey(String key);
}

View File

@@ -9,12 +9,13 @@ import org.springframework.stereotype.Repository;
import javax.persistence.QueryHint;
import java.util.List;
import java.util.Optional;
@Repository
public interface UserInServerRepository extends JpaRepository<AUserInAServer, Long> {
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
AUserInAServer findByServerReferenceAndUserReference(AServer serverId, AUser userId);
Optional<AUserInAServer> findByServerReferenceAndUserReference(AServer serverId, AUser userId);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
boolean existsByServerReferenceAndUserReference(AServer server, AUser user);

View File

@@ -1,6 +1,6 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.exception.ChannelNotInGuildException;
import dev.sheldan.abstracto.core.exception.GuildNotFoundException;
import dev.sheldan.abstracto.core.models.GuildChannelMember;
import dev.sheldan.abstracto.core.models.database.AEmote;
@@ -57,7 +57,7 @@ public class BotServiceBean implements BotService {
Member member = guild.getMemberById(userId);
return GuildChannelMember.builder().guild(guild).textChannel(textChannel).member(member).build();
} else {
throw new ChannelNotFoundException(channelId);
throw new ChannelNotInGuildException(channelId);
}
}
@@ -182,7 +182,12 @@ public class BotServiceBean implements BotService {
@Override
public TextChannel getTextChannelFromServer(Guild guild, Long textChannelId) {
return getTextChannelFromServerOptional(guild, textChannelId).orElseThrow(() -> new ChannelNotFoundException(textChannelId));
return getTextChannelFromServerOptional(guild, textChannelId).orElseThrow(() -> new ChannelNotInGuildException(textChannelId));
}
@Override
public TextChannel getTextChannelFromServerNullable(Guild guild, Long textChannelId) {
return getTextChannelFromServerOptional(guild, textChannelId).orElse(null);
}
@Override
@@ -197,7 +202,7 @@ public class BotServiceBean implements BotService {
@Override
public TextChannel getTextChannelFromServer(Long serverId, Long textChannelId) {
return getTextChannelFromServerOptional(serverId, textChannelId).orElseThrow(() -> new ChannelNotFoundException(textChannelId));
return getTextChannelFromServerOptional(serverId, textChannelId).orElseThrow(() -> new ChannelNotInGuildException(textChannelId));
}
@Override
@@ -217,6 +222,6 @@ public class BotServiceBean implements BotService {
@Override
public Member getBotInGuild(AServer server) {
Guild guild = getGuildById(server.getId());
return guild.getMemberById(instance.getSelfUser().getId());
return guild.getSelfMember();
}
}

View File

@@ -8,6 +8,7 @@ import dev.sheldan.abstracto.core.command.service.management.CommandManagementSe
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.ChannelGroupType;
import dev.sheldan.abstracto.core.service.management.ChannelGroupManagementService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
@@ -15,6 +16,9 @@ import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class ChannelGroupServiceBean implements ChannelGroupService {
@@ -33,10 +37,11 @@ public class ChannelGroupServiceBean implements ChannelGroupService {
@Autowired
private ServerManagementService serverManagementService;
@Override
public AChannelGroup createChannelGroup(String name, Long serverId) {
public AChannelGroup createChannelGroup(String name, Long serverId, ChannelGroupType channelGroupType) {
AServer server = serverManagementService.loadOrCreate(serverId);
return channelGroupManagementService.createChannelGroup(name, server);
return channelGroupManagementService.createChannelGroup(name, server, channelGroupType);
}
@Override
@@ -120,4 +125,13 @@ public class ChannelGroupServiceBean implements ChannelGroupService {
AServer server = serverManagementService.loadOrCreate(serverId);
return channelGroupManagementService.findByNameAndServer(groupName, server) != null;
}
@Override
public List<AChannelGroup> getChannelGroupsOfChannelWithType(AChannel channel, String groupTypeKey) {
List<AChannelGroup> channelGroups = channelGroupManagementService.getAllChannelGroupsOfChannel(channel);
return channelGroups
.stream()
.filter(aChannelGroup -> aChannelGroup.getChannelGroupType().getGroupTypeKey().equals(groupTypeKey))
.collect(Collectors.toList());
}
}

View File

@@ -1,7 +1,7 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.CategoryNotFoundException;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.exception.ChannelNotInGuildException;
import dev.sheldan.abstracto.core.exception.GuildNotFoundException;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.templating.model.MessageToSend;
@@ -50,7 +50,7 @@ public class ChannelServiceBean implements ChannelService {
return sendTextToChannel(text, textChannel);
} else {
log.error("Channel {} to post towards was not found in server {}", channel.getId(), channel.getServer().getId());
throw new ChannelNotFoundException(channel.getId());
throw new ChannelNotInGuildException(channel.getId());
}
} else {
log.error("Guild {} was not found when trying to post a message", channel.getServer().getId());
@@ -65,7 +65,7 @@ public class ChannelServiceBean implements ChannelService {
TextChannel textChannel = textChannelOpt.get();
return sendMessageToChannel(message, textChannel);
}
throw new ChannelNotFoundException(channel.getId());
throw new ChannelNotInGuildException(channel.getId());
}
@Override
@@ -90,7 +90,7 @@ public class ChannelServiceBean implements ChannelService {
return sendEmbedToChannel(embed, textChannel);
} else {
log.error("Channel {} to post towards was not found in server {}", channel.getId(), channel.getServer().getId());
throw new ChannelNotFoundException(channel.getId());
throw new ChannelNotInGuildException(channel.getId());
}
} else {
log.error("Guild {} was not found when trying to post a message", channel.getServer().getId());
@@ -115,7 +115,7 @@ public class ChannelServiceBean implements ChannelService {
if(textChannelFromServer.isPresent()) {
return sendMessageToSendToChannel(messageToSend, textChannelFromServer.get());
}
throw new ChannelNotFoundException(channel.getId());
throw new ChannelNotInGuildException(channel.getId());
}
@Override
@@ -166,7 +166,7 @@ public class ChannelServiceBean implements ChannelService {
TextChannel textChannel = textChannelFromServer.get();
editMessageInAChannel(messageToSend, textChannel, messageId);
} else {
throw new ChannelNotFoundException(channel.getId());
throw new ChannelNotInGuildException(channel.getId());
}
}
@@ -244,7 +244,7 @@ public class ChannelServiceBean implements ChannelService {
log.info("Deleting channel {} on server {}.", channelId, serverId);
return textChannelById.delete().submit();
}
throw new ChannelNotFoundException(channelId);
throw new ChannelNotInGuildException(channelId);
}
@Override

View File

@@ -1,7 +1,7 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.command.service.ExceptionService;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.exception.ChannelNotInGuildException;
import dev.sheldan.abstracto.core.interactive.DelayedActionConfig;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.interactive.*;
@@ -106,7 +106,7 @@ public class FeatureSetupServiceBean implements FeatureSetupService {
channelService.sendTextToChannel(text, textChannel);
return executeFeatureSetup(featureConfig, steps, user, new ArrayList<>());
}
throw new ChannelNotFoundException(user.getChannelId());
throw new ChannelNotInGuildException(user.getChannelId());
}
@Override

View File

@@ -0,0 +1,22 @@
package dev.sheldan.abstracto.core.service;
import com.google.common.hash.Hashing;
import com.google.common.io.Files;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@Component
public class HashServiceBean implements HashService {
@Override
public String sha256HashFileContent(File file) throws IOException {
return Files.asByteSource(file).hash(Hashing.sha256()).toString();
}
@Override
public String sha256HashString(String text) {
return Hashing.sha256().hashString(text, StandardCharsets.UTF_8).toString();
}
}

View File

@@ -0,0 +1,31 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.utils.FileUtils;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
@Component
public class HttpServiceBean implements HttpService {
@Autowired
private OkHttpClient client;
@Autowired
private FileUtils fileUtils;
@Override
public File downloadFileToTempFile(String url) throws IOException {
Request request = new Request.Builder().url(url).get().build();
File tempFile = fileUtils.createTempFile(Math.random() + "");
Response execute = client.newCall(request).execute();
fileUtils.writeBytesToFile(tempFile, execute.body().bytes());
return tempFile;
}
}

View File

@@ -1,6 +1,6 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.exception.ChannelNotInGuildException;
import dev.sheldan.abstracto.core.exception.GuildNotFoundException;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReaction;
@@ -94,7 +94,7 @@ public class MessageCacheBean implements MessageCache {
);
} else {
log.error("Not able to load message {} in channel {} in guild {}. Text channel not found.", messageId, textChannelId, guildId);
future.completeExceptionally(new ChannelNotFoundException(textChannelId));
future.completeExceptionally(new ChannelNotInGuildException(textChannelId));
}
} else {
log.error("Not able to load message {} in channel {} in guild {}. Guild not found.", messageId, textChannelId, guildId);

View File

@@ -46,6 +46,16 @@ public class MessageServiceBean implements MessageService {
addReactionToMessageWithFuture(emoteKey, serverId, message);
}
@Override
public void addDefaultReactionToMessage(String unicode, Message message) {
addDefaultReactionToMessageAsync(unicode, message);
}
@Override
public CompletableFuture<Void> addDefaultReactionToMessageAsync(String unicode, Message message) {
return message.addReaction(unicode).submit();
}
@Override
public CompletableFuture<Void> addReactionToMessageWithFuture(String emoteKey, Long serverId, Message message) {
Guild guild = botService.getGuildById(serverId);

View File

@@ -2,10 +2,7 @@ package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.PostTargetEnum;
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
import dev.sheldan.abstracto.core.exception.GuildNotFoundException;
import dev.sheldan.abstracto.core.exception.PostTargetNotFoundException;
import dev.sheldan.abstracto.core.exception.PostTargetNotValidException;
import dev.sheldan.abstracto.core.exception.*;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.DefaultPostTargetManagementService;
import dev.sheldan.abstracto.core.service.management.PostTargetManagement;
@@ -70,7 +67,7 @@ public class PostTargetServiceBean implements PostTargetService {
} else {
log.error("Incorrect post target configuration: {} points to {} on server {}", target.getName(),
target.getChannelReference().getId(), target.getServerReference().getId());
throw new ChannelNotFoundException(target.getChannelReference().getId());
throw new ChannelNotInGuildException(target.getChannelReference().getId());
}
} else {
throw new GuildNotFoundException(target.getServerReference().getId());

View File

@@ -4,15 +4,19 @@ import dev.sheldan.abstracto.core.command.exception.ChannelAlreadyInChannelGroup
import dev.sheldan.abstracto.core.command.exception.ChannelGroupExistsException;
import dev.sheldan.abstracto.core.command.exception.ChannelGroupNotFoundException;
import dev.sheldan.abstracto.core.command.exception.ChannelNotInChannelGroupException;
import dev.sheldan.abstracto.core.listener.entity.ChannelGroupCreatedListenerManager;
import dev.sheldan.abstracto.core.listener.entity.ChannelGroupDeletedListenerManager;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AChannelGroup;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.ChannelGroupType;
import dev.sheldan.abstracto.core.repository.ChannelGroupRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -26,8 +30,14 @@ public class ChannelGroupManagementServiceBean implements ChannelGroupManagement
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private ChannelGroupDeletedListenerManager deletedListenerManager;
@Autowired
private ChannelGroupCreatedListenerManager createdListenerManager;
@Override
public AChannelGroup createChannelGroup(String name, AServer server) {
public AChannelGroup createChannelGroup(String name, AServer server, ChannelGroupType channelGroupType) {
name = name.toLowerCase();
if(doesChannelGroupExist(name, server)) {
throw new ChannelGroupExistsException(name);
@@ -35,10 +45,12 @@ public class ChannelGroupManagementServiceBean implements ChannelGroupManagement
AChannelGroup channelGroup = AChannelGroup
.builder()
.groupName(name)
.channelGroupType(channelGroupType)
.server(server)
.build();
log.info("Creating new channel group in server {}.", server.getId());
channelGroupRepository.save(channelGroup);
createdListenerManager.executeListener(channelGroup);
return channelGroup;
}
@@ -55,6 +67,7 @@ public class ChannelGroupManagementServiceBean implements ChannelGroupManagement
throw new ChannelGroupNotFoundException(name, getAllAvailableAsString(server));
}
log.info("Deleting channel group {} in server {}.", existing.getId(), server.getId());
deletedListenerManager.executeListener(existing);
channelGroupRepository.delete(existing);
}
@@ -83,10 +96,26 @@ public class ChannelGroupManagementServiceBean implements ChannelGroupManagement
@Override
public AChannelGroup findByNameAndServer(String name, AServer server) {
name = name.toLowerCase();
String lowerCaseName = name.toLowerCase();
return channelGroupRepository.findByGroupNameAndServer(lowerCaseName, server)
.orElseThrow(() -> new ChannelGroupNotFoundException(name, getAllAvailableAsString(server)));
}
@Override
public Optional<AChannelGroup> findByNameAndServerOptional(String name, AServer server) {
return channelGroupRepository.findByGroupNameAndServer(name, server);
}
@Override
public AChannelGroup findByNameAndServerAndType(String name, AServer server, String expectedType) {
String lowerName = name.toLowerCase();
Optional<AChannelGroup> channelOptional = channelGroupRepository.findByGroupNameAndServerAndChannelGroupType_GroupTypeKey(lowerName, server, expectedType);
return channelOptional.orElseThrow( () -> {
List<String> channelGroupNames = extractChannelGroupNames(findAllInServerWithType(server.getId(), expectedType));
return new ChannelGroupNotFoundException(name.toLowerCase(), channelGroupNames);
});
}
@Override
public List<AChannelGroup> findAllInServer(AServer server) {
return channelGroupRepository.findByServer(server);
@@ -94,7 +123,12 @@ public class ChannelGroupManagementServiceBean implements ChannelGroupManagement
@Override
public List<String> getAllAvailableAsString(AServer server) {
return findAllInServer(server).stream().map(AChannelGroup::getGroupName).collect(Collectors.toList());
List<AChannelGroup> allInServer = findAllInServer(server);
return extractChannelGroupNames(allInServer);
}
private List<String> extractChannelGroupNames(List<AChannelGroup> allInServer) {
return allInServer.stream().map(AChannelGroup::getGroupName).collect(Collectors.toList());
}
@Override
@@ -102,4 +136,15 @@ public class ChannelGroupManagementServiceBean implements ChannelGroupManagement
AServer server = serverManagementService.loadOrCreate(serverId);
return findAllInServer(server);
}
@Override
public List<AChannelGroup> getAllChannelGroupsOfChannel(AChannel channel) {
return channelGroupRepository.findAllByChannels(channel);
}
@Override
public List<AChannelGroup> findAllInServerWithType(Long serverId, String type) {
AServer server = serverManagementService.loadServer(serverId);
return channelGroupRepository.findByServerAndChannelGroupType_GroupTypeKey(server, type);
}
}

View File

@@ -0,0 +1,43 @@
package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.exception.ChannelGroupTypeNotFound;
import dev.sheldan.abstracto.core.models.database.ChannelGroupType;
import dev.sheldan.abstracto.core.repository.ChannelGroupTypeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Component
public class ChannelGroupTypeManagementServiceBean implements ChannelGroupTypeManagementService {
@Autowired
private ChannelGroupTypeRepository repository;
@Override
public Optional<ChannelGroupType> findChannelGroupTypeByKeyOptional(String key) {
return repository.findByGroupTypeKey(key);
}
@Override
public ChannelGroupType findChannelGroupTypeByKey(String key) {
return findChannelGroupTypeByKeyOptional(key).orElseThrow(() -> new ChannelGroupTypeNotFound(getAllChannelGroupTypesAsString()));
}
@Override
public boolean doesChannelGroupTypeExist(String key) {
return findChannelGroupTypeByKeyOptional(key).isPresent();
}
@Override
public List<ChannelGroupType> getAllChannelGroupTypes() {
return repository.findAll();
}
@Override
public List<String> getAllChannelGroupTypesAsString() {
return getAllChannelGroupTypes().stream().map(ChannelGroupType::getGroupTypeKey).collect(Collectors.toList());
}
}

View File

@@ -37,7 +37,7 @@ public class UserInServerManagementServiceBean implements UserInServerManagement
@Override
public AUserInAServer loadUser(AServer server, AUser user) {
if(userInServerRepository.existsByServerReferenceAndUserReference(server, user)) {
return userInServerRepository.findByServerReferenceAndUserReference(server, user);
return userInServerRepository.findByServerReferenceAndUserReference(server, user).orElseThrow(() -> new UserInServerNotFoundException(0L));
} else {
return this.createUserInServer(server.getId(), user.getId());
}
@@ -76,4 +76,11 @@ public class UserInServerManagementServiceBean implements UserInServerManagement
AUser user = userManagementService.loadUser(userId);
return userInServerRepository.findByUserReference(user);
}
@Override
public Optional<AUserInAServer> loadAUserInAServerOptional(Long serverId, Long userId) {
AUser user = userManagementService.loadUser(userId);
AServer server = serverManagementService.loadServer(serverId);
return userInServerRepository.findByServerReferenceAndUserReference(server, user);
}
}

View File

@@ -31,7 +31,12 @@ public class UserManagementServiceBean implements UserManagementService {
@Override
public AUser loadUser(Long userId) {
Optional<AUser> optional = userRepository.findById(userId);
Optional<AUser> optional = loadUserOptional(userId);
return optional.orElseGet(() -> this.createUser(userId));
}
@Override
public Optional<AUser> loadUserOptional(Long userId) {
return userRepository.findById(userId);
}
}

View File

@@ -13,11 +13,19 @@
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="group_name" type="VARCHAR(255)"/>
<column name="group_server" type="BIGINT">
<column name="group_type_id" type="BIGINT"/>
<column name="server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<changeSet author="Sheldan" id="channel_group-fk_channel_group_channel_group_type">
<addForeignKeyConstraint baseColumnNames="group_type_id" baseTableName="channel_group" constraintName="fk_channel_group_channel_group_type" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="channel_group_type" validate="true"/>
</changeSet>
<changeSet author="Sheldan" id="channel_group-fk_channel_group_server">
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="channel_group" constraintName="fk_channel_group_server" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
</changeSet>
<changeSet author="Sheldan" id="channel_in_group-table">
<createTable tableName="channel_in_group">
<column name="group_id" type="BIGINT">
@@ -51,9 +59,6 @@
<changeSet author="Sheldan" id="channel_in_group-fk_channel_in_group_group">
<addForeignKeyConstraint baseColumnNames="group_id" baseTableName="channel_in_group" constraintName="fk_channel_in_group_group" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="channel_group" validate="true"/>
</changeSet>
<changeSet author="Sheldan" id="channel_group-fk_channel_group_server">
<addForeignKeyConstraint baseColumnNames="group_server" baseTableName="channel_group" constraintName="fk_channel_group_server" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
</changeSet>
<changeSet author="Sheldan" id="channel_group_command-fk_channel_group_command_command">
<addForeignKeyConstraint baseColumnNames="command_id" baseTableName="channel_group_command" constraintName="fk_channel_group_command_command" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="command" validate="true"/>
</changeSet>

View File

@@ -0,0 +1,17 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="channel_group_type-table">
<createTable tableName="channel_group_type">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="channel_group_type_pkey"/>
</column>
<column name="group_type_key" type="VARCHAR(255)"/>
</createTable>
</changeSet>
</databaseChangeLog>

View File

@@ -17,6 +17,7 @@
<include file="role.xml" relativeToChangelogFile="true"/>
<include file="module.xml" relativeToChangelogFile="true"/>
<include file="command.xml" relativeToChangelogFile="true"/>
<include file="channel_group_type.xml" relativeToChangelogFile="true"/>
<include file="channel_group.xml" relativeToChangelogFile="true"/>
<include file="default_feature_flag.xml" relativeToChangelogFile="true"/>
<include file="default_feature_mode.xml" relativeToChangelogFile="true" />