added module responsible for accumulating the dependencies and resulting in a jar

added channels module
added help info object to command configuration
added description field to parameter
added modules for commands (and packed modules), they are mapped by name
added post command execution interface
added support for optional parameters
added support for using guildchannel as parameter
added printing of modules to help command
added service beans to wrap over the operations on the repository
added synchronizing of channels/roles on startup (controlled by flag)
added builder annotations to model classes
added more model classes
This commit is contained in:
Sheldan
2019-12-12 16:47:54 +01:00
parent 42cfe33b3a
commit 5c6b7b9a78
61 changed files with 1136 additions and 138 deletions

View File

@@ -1,29 +0,0 @@
package dev.sheldan.abstracto;
import dev.sheldan.abstracto.service.StartupManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = "dev.sheldan.abstracto")
@EnableCaching
public class Application implements CommandLineRunner {
@Autowired
private StartupManager startup;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run(String... args) throws Exception {
startup.startBot();
}
}

View File

@@ -0,0 +1,19 @@
package dev.sheldan.abstracto;
import dev.sheldan.abstracto.core.models.SnowFlake;
import net.dv8tion.jda.api.entities.ISnowflake;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class SnowflakeUtils {
public static Set<Long> getOwnItemsIds(List<? extends SnowFlake> elements){
return elements.stream().map(SnowFlake::getId).collect(Collectors.toSet());
}
public static Set<Long> getSnowflakeIds(List<? extends ISnowflake> elements){
return elements.stream().map(ISnowflake::getIdLong).collect(Collectors.toSet());
}
}

View File

@@ -1,4 +1,4 @@
package dev.sheldan.abstracto.service;
package dev.sheldan.abstracto.core.service;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;

View File

@@ -0,0 +1,24 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.models.AChannel;
import dev.sheldan.abstracto.core.models.AChannelType;
import dev.sheldan.abstracto.repository.ChannelRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ChannelServiceBean implements ChannelService {
@Autowired
private ChannelRepository repository;
@Override
public AChannel loadChannel(Long id) {
return repository.getOne(id);
}
@Override
public AChannel createChannel(Long id, AChannelType type) {
return repository.save(AChannel.builder().id(id).type(type).build());
}
}

View File

@@ -0,0 +1,47 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.commands.management.PostTargetException;
import dev.sheldan.abstracto.core.models.AChannel;
import dev.sheldan.abstracto.core.models.PostTarget;
import dev.sheldan.abstracto.repository.ChannelRepository;
import dev.sheldan.abstracto.repository.PostTargetRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
@Service
public class PostTargetServiceServiceBean implements PostTargetService {
@Autowired
private PostTargetRepository postTargetRepository;
@Autowired
private ChannelRepository channelRepository;
@Override
@Transactional
public void createPostTarget(String name, AChannel targetChannel) {
if(!PostTarget.AVAILABLE_POST_TARGETS.contains(name)) {
throw new PostTargetException("PostTarget not found");
}
postTargetRepository.save(PostTarget.builder().name(name).AChannel(targetChannel).build());
}
@Override
@Transactional
public void createOrUpdate(String name, AChannel targetChannel) {
PostTarget existing = postTargetRepository.findPostTargetByName(name);
if(existing == null){
this.createPostTarget(name, targetChannel);
} else {
this.updatePostTarget(existing, targetChannel);
}
}
@Override
@Transactional
public void updatePostTarget(PostTarget target, AChannel newTargetChannel) {
postTargetRepository.getOne(target.getId()).setAChannel(newTargetChannel);
}
}

View File

@@ -0,0 +1,18 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.models.ARole;
import dev.sheldan.abstracto.repository.RoleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class RoleServiceBean implements RoleService {
@Autowired
private RoleRepository repository;
@Override
public ARole createRole(Long id) {
return repository.save(ARole.builder().id(id).build());
}
}

View File

@@ -0,0 +1,28 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.models.AChannel;
import dev.sheldan.abstracto.core.models.AServer;
import dev.sheldan.abstracto.repository.ServerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
@Service
public class ServerServiceBean implements ServerService {
@Autowired
private ServerRepository repository;
@Override
public AServer createServer(Long id) {
return repository.save(AServer.builder().id(id).build());
}
@Override
public void addChannelToServer(AServer server, AChannel channel) {
server.getChannels().add(channel);
}
}

View File

@@ -0,0 +1,93 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.SnowflakeUtils;
import dev.sheldan.abstracto.core.models.AChannel;
import dev.sheldan.abstracto.core.models.AChannelType;
import dev.sheldan.abstracto.core.models.ARole;
import dev.sheldan.abstracto.core.models.AServer;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.GuildChannel;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.apache.commons.collections4.SetUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.security.auth.login.LoginException;
import javax.transaction.Transactional;
import java.util.List;
import java.util.Set;
@Service
public class StartupManager implements Startup {
@Autowired
private BotService service;
@Autowired
private List<? extends ListenerAdapter> listeners;
@Autowired
private ServerService serverService;
@Autowired
private ChannelService channelService;
@Autowired
private RoleService roleService;
@Override
public void startBot() throws LoginException {
service.login();
listeners.forEach(o -> service.getInstance().addEventListener(o));
}
@Override
@Transactional
public void synchronize() {
synchronizeServers();
}
private void synchronizeServers(){
JDA instance = service.getInstance();
List<Guild> onlineGuilds = instance.getGuilds();
Set<Long> availableServers = SnowflakeUtils.getSnowflakeIds(onlineGuilds);
availableServers.forEach(aLong -> {
AServer newAServer = serverService.createServer(aLong);
Guild newGuild = instance.getGuildById(aLong);
if(newGuild != null){
synchronizeRolesOf(newGuild, newAServer);
synchronizeChannelsOf(newGuild, newAServer);
}
});
}
private void synchronizeRolesOf(Guild guild, AServer existingAServer){
List<Role> existingRoles = guild.getRoles();
List<ARole> knownARoles = existingAServer.getRoles();
Set<Long> knownRolesId = SnowflakeUtils.getOwnItemsIds(knownARoles);
Set<Long> availableRoles = SnowflakeUtils.getSnowflakeIds(existingRoles);
Set<Long> newRoles = SetUtils.disjunction(availableRoles, knownRolesId);
newRoles.forEach(aLong -> {
ARole newRole = roleService.createRole(aLong);
existingAServer.getRoles().add(newRole);
});
}
private void synchronizeChannelsOf(Guild guild, AServer existingServer){
List<GuildChannel> available = guild.getChannels();
List<AChannel> knownChannels = existingServer.getChannels();
Set<Long> knownChannelsIds = SnowflakeUtils.getOwnItemsIds(knownChannels);
Set<Long> existingChannelsIds = SnowflakeUtils.getSnowflakeIds(available);
Set<Long> newChannels = SetUtils.disjunction(existingChannelsIds, knownChannelsIds);
newChannels.forEach(aLong -> {
GuildChannel channel1 = available.stream().filter(channel -> channel.getIdLong() == aLong).findFirst().get();
AChannelType type = AChannel.getAChannelType(channel1.getType());
AChannel newChannel = channelService.createChannel(channel1.getIdLong(), type);
serverService.addChannelToServer(existingServer, newChannel);
});
}
}

View File

@@ -0,0 +1,44 @@
package dev.sheldan.abstracto.listener;
import dev.sheldan.abstracto.core.models.AChannel;
import dev.sheldan.abstracto.core.models.AServer;
import dev.sheldan.abstracto.repository.ServerRepository;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.events.channel.text.TextChannelCreateEvent;
import net.dv8tion.jda.api.events.channel.text.TextChannelDeleteEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Nonnull;
import java.util.Optional;
@Service
public class ChannelListener extends ListenerAdapter {
@Autowired
private ServerRepository serverRepository;
private static Logger logger = LoggerFactory.getLogger(ChannelListener.class);
@Override
public void onTextChannelDelete(@Nonnull TextChannelDeleteEvent event) {
AServer serverObject = serverRepository.getOne(event.getGuild().getIdLong());
serverObject.getChannels().add(AChannel.builder().id(event.getChannel().getIdLong()).build());
}
@Override
public void onTextChannelCreate(@Nonnull TextChannelCreateEvent event) {
AServer serverObject = serverRepository.getOne(event.getGuild().getIdLong());
TextChannel createdChannel = event.getChannel();
Optional<AChannel> possibleChannel = serverObject.getChannels().stream().filter(aChannel -> aChannel.id == createdChannel.getIdLong()).findAny();
if(possibleChannel.isPresent()){
serverObject.getChannels().remove(possibleChannel.get());
logger.info("Adding channel {} with id {}", createdChannel.getName(), createdChannel.getIdLong());
} else {
logger.warn("Channel removed event for channel which was not in present");
}
}
}

View File

@@ -0,0 +1,27 @@
package dev.sheldan.abstracto.listener;
import dev.sheldan.abstracto.core.service.StartupManager;
import net.dv8tion.jda.api.events.ReadyEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Nonnull;
@Service
public class ReadyListener extends ListenerAdapter {
@Autowired
private StartupManager startup;
@Value("${abstracto.startup.synchronize}")
private boolean synchronize;
@Override
public void onReady(@Nonnull ReadyEvent event) {
if(synchronize){
startup.synchronize();
}
}
}

View File

@@ -1,24 +0,0 @@
package dev.sheldan.abstracto.models;
import lombok.Builder;
import lombok.Getter;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import java.util.Set;
@Entity
@Table
@Builder
public class Channel {
@Id
@Getter
public Long id;
@Getter
@ManyToMany(mappedBy = "channels")
private Set<ChannelGroup> groups;
}

View File

@@ -1,31 +0,0 @@
package dev.sheldan.abstracto.models;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.Set;
@Entity
@Table
public class ChannelGroup {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Getter
private Long id;
@Column
@Getter @Setter
private String groupName;
@ManyToMany
@JoinTable(
name = "channel_in_group",
joinColumns = @JoinColumn(name = "group_id"),
inverseJoinColumns = @JoinColumn(name = "channel_id"))
@Getter
private Set<Channel> channels;
}

View File

@@ -1,26 +0,0 @@
package dev.sheldan.abstracto.models;
import lombok.Getter;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import javax.persistence.*;
@Entity
@Table
public class PostTarget {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Getter
private Long id;
@Column(unique = true)
@Getter
private String name;
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "id", nullable = false)
@Getter
private Channel channel;
}

View File

@@ -1,23 +0,0 @@
package dev.sheldan.abstracto.models;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table
public class Role {
@Id
@Getter
private Long id;
@Column(unique = true)
@Getter @Setter
private String name;
}

View File

@@ -1,9 +1,12 @@
package dev.sheldan.abstracto.repository;
import dev.sheldan.abstracto.models.Channel;
import dev.sheldan.abstracto.core.models.AChannel;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ChannelRepository extends JpaRepository<Channel, Long> {
public interface ChannelRepository extends JpaRepository<AChannel, Long> {
List<AChannel> findAll();
}

View File

@@ -0,0 +1,12 @@
package dev.sheldan.abstracto.repository;
import dev.sheldan.abstracto.core.models.PostTarget;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface PostTargetRepository extends JpaRepository<PostTarget, Long> {
PostTarget findPostTargetByName(String name);
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.abstracto.repository;
import dev.sheldan.abstracto.core.models.ARole;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface RoleRepository extends JpaRepository<ARole, Long> {
}

View File

@@ -0,0 +1,12 @@
package dev.sheldan.abstracto.repository;
import dev.sheldan.abstracto.core.models.AServer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ServerRepository extends JpaRepository<AServer, Long> {
List<AServer> findAll();
}

View File

@@ -1,25 +0,0 @@
package dev.sheldan.abstracto.service;
import dev.sheldan.abstracto.commands.management.CommandReceivedHandler;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.security.auth.login.LoginException;
import java.util.List;
@Service
public class StartupManager implements Startup {
@Autowired
private BotService service;
@Autowired
private List<? extends ListenerAdapter> listeners;
@Override
public void startBot() throws LoginException {
service.login();
listeners.forEach(o -> service.getInstance().addEventListener(o));
}
}

View File

@@ -1,6 +0,0 @@
spring.datasource.url=jdbc:postgresql://localhost:5432/abstracto
spring.datasource.username= abstracto
spring.datasource.password= abstracto
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto = update