Compare commits

...

7 Commits

Author SHA1 Message Date
Sheldan
a5ead2b6d7 [maven-release-plugin] prepare release r-austria-bot-1.0.2 2022-05-15 18:08:27 +02:00
Sheldan
b4fbe368d7 [RAB-xxx] preparing for release 2022-05-15 18:05:18 +02:00
Sheldan
199ca18cac [RAB-2] adding code to retrieve legacy quotes
adding migration script for legacy quotes
changing docker-compose file to use different container names
2022-05-15 16:13:48 +02:00
Sheldan
da6c4a5b23 [maven-release-plugin] prepare for next development iteration 2022-02-12 23:33:29 +01:00
Sheldan
7f4d02c9e6 [maven-release-plugin] prepare release r-austria-bot-1.0.1 2022-02-12 23:33:26 +01:00
Sheldan
cdfc06a63d [RAB-1] fixing prometheus configuration 2022-02-12 16:26:07 +01:00
Sheldan
ce2050f790 [maven-release-plugin] prepare for next development iteration 2022-02-12 13:15:07 +01:00
85 changed files with 3034 additions and 39 deletions

View File

@@ -34,5 +34,5 @@ jobs:
env:
REGISTRY_PREFIX: docker.pkg.github.com/sheldan/r-austria-bot/
VERSION: ${{ env.version }}
ABSTRACTO_VERSION: 1.3.11
ABSTRACTO_VERSION: 1.3.13
ABSTRACTO_REGISTRY_PREFIX: docker.pkg.github.com/sheldan/abstracto/

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.raustria.bot.application</groupId>
<artifactId>application</artifactId>
<version>1.0.0</version>
<version>1.0.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>executable</artifactId>
@@ -75,6 +75,12 @@
<artifactId>starboard-impl</artifactId>
</dependency>
<dependency>
<groupId>dev.sheldan.raustria.bot.application.module</groupId>
<artifactId>quotes</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.raustria.bot</groupId>
<artifactId>r-austria-bot</artifactId>
<version>1.0.0</version>
<version>1.0.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -27,6 +27,7 @@
<modules>
<module>executable</module>
<module>r-austria-modules</module>
</modules>
<dependencies>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>application</artifactId>
<groupId>dev.sheldan.raustria.bot.application</groupId>
<version>1.0.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>r-austria-modules</artifactId>
<packaging>pom</packaging>
<modules>
<module>quotes</module>
</modules>
</project>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>r-austria-modules</artifactId>
<groupId>dev.sheldan.raustria.bot.application</groupId>
<version>1.0.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>dev.sheldan.raustria.bot.application.module</groupId>
<artifactId>quotes</artifactId>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/assembly/liquibase.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,18 @@
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<id>liquibase</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<outputDirectory>.</outputDirectory>
<directory>${project.basedir}/src/main/resources/migrations</directory>
<includes>
<include>**/*</include>
</includes>
</fileSet>
</fileSets>
</assembly>

View File

@@ -0,0 +1,100 @@
package dev.sheldan.raustria.bot.module.quotes.command;
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.FeatureDefinition;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.raustria.bot.module.quotes.config.QuotesFeatureDefinition;
import dev.sheldan.raustria.bot.module.quotes.config.QuotesModuleDefinition;
import dev.sheldan.raustria.bot.module.quotes.exception.QuoteNotFoundException;
import dev.sheldan.raustria.bot.module.quotes.model.database.Quote;
import dev.sheldan.raustria.bot.module.quotes.service.QuoteServiceBean;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.MessageChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
public class QuoteCommand extends AbstractConditionableCommand {
@Autowired
private QuoteServiceBean quoteServiceBean;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private ChannelService channelService;
@Autowired
private QuoteCommand self;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
Optional<Quote> foundQuote;
if(parameters.isEmpty()) {
AServer server = serverManagementService.loadServer(commandContext.getGuild().getIdLong());
foundQuote = quoteServiceBean.getRandomQuote(server);
} else {
Member targetMember = (Member) parameters.get(0);
AUserInAServer user = userInServerManagementService.loadOrCreateUser(targetMember);
foundQuote = quoteServiceBean.getRandomQuoteForMember(user);
}
if(foundQuote.isPresent()) {
Quote quoteToDisplay = foundQuote.get();
return quoteServiceBean.renderQuoteToMessageToSend(quoteToDisplay)
.thenCompose(messageToSend -> self.sendMessageToChannel(messageToSend, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
} else {
throw new QuoteNotFoundException();
}
}
@Transactional
public CompletableFuture<Void> sendMessageToChannel(MessageToSend messageToSend, MessageChannel messageChannel) {
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, messageChannel));
}
@Override
public CommandConfiguration getConfiguration() {
Parameter memberParameter = Parameter.builder().templated(true).name("member").type(Member.class).optional(true).build();
List<Parameter> parameters = Collections.singletonList(memberParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("quote")
.module(QuotesModuleDefinition.QUOTES)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(false)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return QuotesFeatureDefinition.QUOTES;
}
}

View File

@@ -0,0 +1,61 @@
package dev.sheldan.raustria.bot.module.quotes.command;
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.FeatureDefinition;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.raustria.bot.module.quotes.config.QuotesFeatureDefinition;
import dev.sheldan.raustria.bot.module.quotes.config.QuotesModuleDefinition;
import dev.sheldan.raustria.bot.module.quotes.service.QuoteServiceBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
@Component
public class QuoteDelete extends AbstractConditionableCommand {
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private QuoteServiceBean quoteServiceBean;
@Override
public CommandResult execute(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
Long quoteId = (Long) parameters.get(0);
AServer server = serverManagementService.loadServer(commandContext.getGuild().getIdLong());
quoteServiceBean.deleteQuote(quoteId, server);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter quoteIdParameter = Parameter.builder().templated(true).name("quoteId").type(Long.class).build();
List<Parameter> parameters = Collections.singletonList(quoteIdParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("quoteDelete")
.module(QuotesModuleDefinition.QUOTES)
.templated(true)
.async(false)
.requiresConfirmation(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return QuotesFeatureDefinition.QUOTES;
}
}

View File

@@ -0,0 +1,89 @@
package dev.sheldan.raustria.bot.module.quotes.command;
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.FeatureDefinition;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.raustria.bot.module.quotes.config.QuotesFeatureDefinition;
import dev.sheldan.raustria.bot.module.quotes.config.QuotesModuleDefinition;
import dev.sheldan.raustria.bot.module.quotes.exception.QuoteNotFoundException;
import dev.sheldan.raustria.bot.module.quotes.model.database.Quote;
import dev.sheldan.raustria.bot.module.quotes.service.QuoteServiceBean;
import net.dv8tion.jda.api.entities.MessageChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
public class QuoteGet extends AbstractConditionableCommand {
@Autowired
private QuoteServiceBean quoteServiceBean;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private ChannelService channelService;
@Autowired
private QuoteGet self;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
Long quoteId = (Long) parameters.get(0);
AServer server = serverManagementService.loadServer(commandContext.getGuild().getIdLong());
Optional<Quote> possibleQuote = quoteServiceBean.getQuote(quoteId, server);
if(possibleQuote.isPresent()) {
Quote quoteToDisplay = possibleQuote.get();
return quoteServiceBean.renderQuoteToMessageToSend(quoteToDisplay)
.thenCompose(messageToSend -> self.sendMessageToChannel(messageToSend, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
} else {
throw new QuoteNotFoundException();
}
}
@Transactional
public CompletableFuture<Void> sendMessageToChannel(MessageToSend messageToSend, MessageChannel messageChannel) {
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, messageChannel));
}
@Override
public CommandConfiguration getConfiguration() {
Parameter quoteIdParameter = Parameter.builder().templated(true).name("quoteId").type(Long.class).build();
List<Parameter> parameters = Collections.singletonList(quoteIdParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("quoteGet")
.module(QuotesModuleDefinition.QUOTES)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(false)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return QuotesFeatureDefinition.QUOTES;
}
}

View File

@@ -0,0 +1,88 @@
package dev.sheldan.raustria.bot.module.quotes.command;
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.FeatureDefinition;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.raustria.bot.module.quotes.config.QuotesFeatureDefinition;
import dev.sheldan.raustria.bot.module.quotes.config.QuotesModuleDefinition;
import dev.sheldan.raustria.bot.module.quotes.exception.QuoteNotFoundException;
import dev.sheldan.raustria.bot.module.quotes.model.database.Quote;
import dev.sheldan.raustria.bot.module.quotes.service.QuoteServiceBean;
import net.dv8tion.jda.api.entities.MessageChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
public class QuoteSearch extends AbstractConditionableCommand {
@Autowired
private QuoteServiceBean quoteServiceBean;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private ChannelService channelService;
@Autowired
private QuoteSearch self;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
String query = (String) parameters.get(0);
AServer server = serverManagementService.loadServer(commandContext.getGuild().getIdLong());
Optional<Quote> possibleQuote = quoteServiceBean.searchQuote(query, server);
if(possibleQuote.isPresent()) {
Quote quoteToDisplay = possibleQuote.get();
return quoteServiceBean.renderQuoteToMessageToSend(quoteToDisplay)
.thenCompose(messageToSend -> self.sendMessageToChannel(messageToSend, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
} else {
throw new QuoteNotFoundException();
}
}
@Transactional
public CompletableFuture<Void> sendMessageToChannel(MessageToSend messageToSend, MessageChannel messageChannel) {
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, messageChannel));
}
@Override
public CommandConfiguration getConfiguration() {
Parameter searchParameter = Parameter.builder().templated(true).name("query").type(String.class).build();
List<Parameter> parameters = Collections.singletonList(searchParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("quoteSearch")
.module(QuotesModuleDefinition.QUOTES)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(false)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return QuotesFeatureDefinition.QUOTES;
}
}

View File

@@ -0,0 +1,70 @@
package dev.sheldan.raustria.bot.module.quotes.command;
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.FeatureDefinition;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.raustria.bot.module.quotes.config.QuotesFeatureDefinition;
import dev.sheldan.raustria.bot.module.quotes.config.QuotesModuleDefinition;
import dev.sheldan.raustria.bot.module.quotes.model.command.QuoteStatsModel;
import dev.sheldan.raustria.bot.module.quotes.service.QuoteServiceBean;
import net.dv8tion.jda.api.entities.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class QuoteStats extends AbstractConditionableCommand {
@Autowired
private QuoteServiceBean quoteServiceBean;
@Autowired
private ChannelService channelService;
private static final String QUOTE_STATS_RESPONSE_TEMPLATE_KEY = "quoteStats_response";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
Member targetMember;
if(parameters.isEmpty()) {
targetMember = commandContext.getAuthor();
} else {
targetMember = (Member) parameters.get(0);
}
QuoteStatsModel model = quoteServiceBean.getQuoteStats(targetMember);
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannelList(QUOTE_STATS_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter searchParameter = Parameter.builder().templated(true).name("member").type(Member.class).optional(true).build();
List<Parameter> parameters = Collections.singletonList(searchParameter);
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("quoteStats")
.module(QuotesModuleDefinition.QUOTES)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(false)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return QuotesFeatureDefinition.QUOTES;
}
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.raustria.bot.module.quotes.config;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import org.springframework.stereotype.Component;
@Component
public class QuotesFeatureConfig implements FeatureConfig {
@Override
public FeatureDefinition getFeature() {
return QuotesFeatureDefinition.QUOTES;
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.raustria.bot.module.quotes.config;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import lombok.Getter;
@Getter
public enum QuotesFeatureDefinition implements FeatureDefinition {
QUOTES("quotes");
private String key;
QuotesFeatureDefinition(String key) {
this.key = key;
}
}

View File

@@ -0,0 +1,25 @@
package dev.sheldan.raustria.bot.module.quotes.config;
import dev.sheldan.abstracto.core.command.config.ModuleDefinition;
import dev.sheldan.abstracto.core.command.config.ModuleInfo;
import org.springframework.stereotype.Component;
@Component
public class QuotesModuleDefinition implements ModuleDefinition {
public static final String QUOTES = "quotes";
@Override
public ModuleInfo getInfo() {
return ModuleInfo
.builder()
.name(QUOTES)
.templated(true)
.build();
}
@Override
public String getParentModule() {
return "default";
}
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.raustria.bot.module.quotes.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource("classpath:quotes.properties")
public class QuotesProperties {
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.raustria.bot.module.quotes.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class QuoteNotFoundException extends AbstractoTemplatableException {
@Override
public String getTemplateName() {
return "quote_not_found_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,24 @@
package dev.sheldan.raustria.bot.module.quotes.model.command;
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
import lombok.Builder;
import lombok.Getter;
import java.time.Instant;
import java.util.List;
@Getter
@Builder
public class QuoteResponseModel {
private Long quoteId;
private String authorAvatarURL;
private String authorName;
private ServerChannelMessage quotedMessage;
private String quoteContent;
private List<String> imageAttachmentURLs;
private List<String> fileAttachmentURLs;
private String adderAvatarURL;
private String adderName;
private Instant creationDate;
private String sourceChannelName;
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.raustria.bot.module.quotes.model.command;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class QuoteStatsModel {
private Long authorCount;
private String userName;
private Long quoteCount;
private Long userId;
private Long serverId;
}

View File

@@ -0,0 +1,66 @@
package dev.sheldan.raustria.bot.module.quotes.model.database;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
import javax.persistence.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "quote")
@Getter
@Setter
@EqualsAndHashCode
public class Quote {
@EmbeddedId
@Getter
private ServerSpecificId id;
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.LAZY)
@MapsId("serverId")
@JoinColumn(name = "server_id", nullable = false)
private AServer server;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author_user_in_server_id", nullable = false)
private AUserInAServer author;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "adder_user_in_server_id", nullable = false)
private AUserInAServer adder;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "source_channel_id", nullable = false)
private AChannel sourceChannel;
@Getter
@Column(name = "message_id")
private Long messageId;
@OneToMany(
fetch = FetchType.LAZY,
orphanRemoval = true,
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
mappedBy = "quote")
@Builder.Default
private List<QuoteAttachment> attachments = new ArrayList<>();
@Getter
@Column(name = "text")
private String text;
@Column(name = "created")
private Instant created;
@Column(name = "updated")
private Instant updated;
}

View File

@@ -0,0 +1,44 @@
package dev.sheldan.raustria.bot.module.quotes.model.database;
import lombok.*;
import javax.persistence.*;
@Builder
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "quote_attachment")
@Getter
@Setter
@EqualsAndHashCode
public class QuoteAttachment {
@Id
@Getter
@Setter
@Column(name = "id", nullable = false)
private Long id;
@Getter
@Setter
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns(
{
@JoinColumn(updatable = false, insertable = false, name = "quote_id", referencedColumnName = "id"),
@JoinColumn(updatable = false, insertable = false, name = "server_id", referencedColumnName = "server_id")
})
private Quote quote;
@Getter
@Setter
@Column(name = "url", nullable = false)
private String url;
@Getter
@Setter
@Column(name = "is_image", nullable = false)
@Builder.Default
private Boolean isImage = false;
}

View File

@@ -0,0 +1,19 @@
package dev.sheldan.raustria.bot.module.quotes.repository;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.raustria.bot.module.quotes.model.database.Quote;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface QuoteRepository extends JpaRepository<Quote, ServerSpecificId> {
List<Quote> findByTextContainingAndServer(String text, AServer server);
List<Quote> findByServer(AServer server);
List<Quote> findByAuthor(AUserInAServer author);
Long countByAuthor(AUserInAServer author);
Long countByAdder(AUserInAServer adder);
}

View File

@@ -0,0 +1,239 @@
package dev.sheldan.raustria.bot.module.quotes.service;
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.UserService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.CompletableFutureList;
import dev.sheldan.raustria.bot.module.quotes.exception.QuoteNotFoundException;
import dev.sheldan.raustria.bot.module.quotes.model.command.QuoteResponseModel;
import dev.sheldan.raustria.bot.module.quotes.model.command.QuoteStatsModel;
import dev.sheldan.raustria.bot.module.quotes.model.database.Quote;
import dev.sheldan.raustria.bot.module.quotes.model.database.QuoteAttachment;
import dev.sheldan.raustria.bot.module.quotes.repository.QuoteRepository;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.AbstractChannel;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Component
@Slf4j
public class QuoteServiceBean {
@Autowired
private QuoteRepository quoteRepository;
@Autowired
private MemberService memberService;
@Autowired
private UserService userService;
@Autowired
private TemplateService templateService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Autowired
private SecureRandom secureRandom;
@Autowired
private ChannelService channelService;
private static final String QUOTE_RESPONSE_TEMPLATE_KEY = "quote_response";
public Optional<Quote> getRandomQuoteForMember(AUserInAServer aUserInAServer) {
// not nice, but good enough for now
List<Quote> allQuotes = quoteRepository.findByAuthor(aUserInAServer);
if(allQuotes.isEmpty()) {
return Optional.empty();
}
return Optional.of(allQuotes.get(secureRandom.nextInt(allQuotes.size())));
}
public Optional<Quote> getRandomQuote(AServer server) {
// not nice, but good enough for now
List<Quote> allQuotes = quoteRepository.findByServer(server);
if(allQuotes.isEmpty()) {
return Optional.empty();
}
return Optional.of(allQuotes.get(secureRandom.nextInt(allQuotes.size())));
}
public void deleteQuote(Long quoteId, AServer server) {
Optional<Quote> existingQuote = getQuote(quoteId, server);
if(existingQuote.isPresent()) {
quoteRepository.delete(existingQuote.get());
log.info("Deleting quote with id {} in server {}.", quoteId, server.getId());
} else {
throw new QuoteNotFoundException();
}
}
public Optional<Quote> getQuote(Long quoteId, AServer server) {
ServerSpecificId id = new ServerSpecificId(server.getId(), quoteId);
log.info("Loading quote with id {} in server {}.", quoteId, server.getId());
return quoteRepository.findById(id);
}
public CompletableFuture<MessageToSend> renderQuoteToMessageToSend(Quote quote) {
ServerChannelMessage quotedMessage = ServerChannelMessage
.builder()
.messageId(quote.getMessageId())
.channelId(quote.getSourceChannel().getId())
.serverId(quote.getServer().getId())
.build();
List<String> imageAttachments = quote
.getAttachments()
.stream()
.filter(QuoteAttachment::getIsImage)
.map(QuoteAttachment::getUrl)
.collect(Collectors.toList());
List<String> fileAttachments = quote
.getAttachments()
.stream()
.filter(quoteAttachment -> !quoteAttachment.getIsImage())
.map(QuoteAttachment::getUrl)
.collect(Collectors.toList());
QuoteResponseModel.QuoteResponseModelBuilder modelBuilder = QuoteResponseModel
.builder()
.quoteContent(quote.getText())
.imageAttachmentURLs(imageAttachments)
.quoteId(quote.getId().getId())
.fileAttachmentURLs(fileAttachments)
.creationDate(quote.getCreated())
.quotedMessage(quotedMessage);
Long quotedUserId = quote.getAuthor().getUserReference().getId();
Long quoteAdderUserId = quote.getAdder().getUserReference().getId();
Long serverId = quote.getServer().getId();
Long channelId = quote.getSourceChannel().getId();
Optional<TextChannel> sourceChannel = channelService.getTextChannelFromServerOptional(serverId, channelId);
List<Long> userIds = Arrays.asList(quotedUserId, quoteAdderUserId);
CompletableFutureList<User> futureList = userService.retrieveUsers(userIds);
CompletableFuture<MessageToSend> messageFuture = new CompletableFuture<>();
futureList.getMainFuture().whenComplete((unused, throwable) ->
createMessageToSend(futureList, modelBuilder, quotedUserId, quoteAdderUserId, serverId, sourceChannel)
.thenAccept(messageFuture::complete)
.exceptionally(throwable1 -> {
messageFuture.completeExceptionally(throwable1);
return null;
}));
return messageFuture;
}
private CompletableFuture<MessageToSend> createMessageToSend( CompletableFutureList<User> possibleUsers, QuoteResponseModel.QuoteResponseModelBuilder modelBuilder,
Long quotedUserId, Long quoteAdderUserId, Long serverId, Optional<TextChannel> sourceChannel) {
return memberService.getMembersInServerAsync(serverId, Arrays.asList(quotedUserId, quoteAdderUserId))
.thenApply(members -> {
List<User> foundUsers = possibleUsers.getObjects();
Member authorMember = members
.stream()
.filter(member -> member.getIdLong() == quotedUserId)
.findFirst()
.orElse(null);
Member adderMember = members
.stream()
.filter(member -> member.getIdLong() == quoteAdderUserId)
.findFirst()
.orElse(null);
User authorUser = foundUsers
.stream()
.filter(user -> user.getIdLong() == quotedUserId)
.findFirst()
.orElse(null);
User adderUser = foundUsers
.stream()
.filter(user -> user.getIdLong() == quoteAdderUserId)
.findFirst()
.orElse(null);
String adderAvatar = Optional
.ofNullable(adderMember)
.map(member -> member.getUser().getAvatarUrl())
.orElse(Optional
.ofNullable(adderUser)
.map(User::getAvatarUrl)
.orElse(null));
String authorAvatar = Optional
.ofNullable(authorMember)
.map(member -> member.getUser().getAvatarUrl())
.orElse(Optional
.ofNullable(authorUser)
.map(User::getAvatarUrl)
.orElse(null));
String adderName = Optional
.ofNullable(adderMember)
.map(Member::getEffectiveName)
.orElse(Optional
.ofNullable(adderUser)
.map(User::getName)
.orElse(null));
String authorName = Optional
.ofNullable(authorMember)
.map(Member::getEffectiveName)
.orElse(Optional
.ofNullable(authorUser)
.map(User::getName)
.orElse(null));
String channelName = sourceChannel
.map(AbstractChannel::getName)
.orElse(null);
QuoteResponseModel model = modelBuilder
.adderAvatarURL(adderAvatar)
.authorAvatarURL(authorAvatar)
.adderName(adderName)
.authorName(authorName)
.sourceChannelName(channelName)
.build();
return templateService.renderEmbedTemplate(QUOTE_RESPONSE_TEMPLATE_KEY, model, serverId);
});
}
public Optional<Quote> searchQuote(String query, AServer server) {
List<Quote> foundQuotes = quoteRepository.findByTextContainingAndServer(query, server);
if(foundQuotes.isEmpty()) {
return Optional.empty();
}
if(foundQuotes.size() > 1) {
log.info("Found multiple quotes in server {}, returning first one.", server.getId());
}
return Optional.of(foundQuotes.get(0));
}
public QuoteStatsModel getQuoteStats(Member member) {
AUserInAServer user = userInServerManagementService.loadOrCreateUser(member);
return getQuoteStats(user, member);
}
public QuoteStatsModel getQuoteStats(AUserInAServer user, Member member) {
Long authored = quoteRepository.countByAuthor(user);
Long added = quoteRepository.countByAdder(user);
return QuoteStatsModel
.builder()
.quoteCount(added)
.authorCount(authored)
.userName(member.getEffectiveName())
.userId(user.getUserReference().getId())
.serverId(user.getServerReference().getId())
.build();
}
}

View File

@@ -0,0 +1,11 @@
<?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.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="tables/tables.xml" relativeToChangelogFile="true"/>
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,40 @@
<?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.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<property name="quotesModule" value="(SELECT id FROM module WHERE name = 'quotes')"/>
<property name="quotesFeature" value="(SELECT id FROM feature WHERE key = 'quotes')"/>
<changeSet author="Sheldan" id="quotes-commands">
<insert tableName="command">
<column name="name" value="quote"/>
<column name="module_id" valueComputed="${quotesModule}"/>
<column name="feature_id" valueComputed="${quotesFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="quoteDelete"/>
<column name="module_id" valueComputed="${quotesModule}"/>
<column name="feature_id" valueComputed="${quotesFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="quoteGet"/>
<column name="module_id" valueComputed="${quotesModule}"/>
<column name="feature_id" valueComputed="${quotesFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="quoteSearch"/>
<column name="module_id" valueComputed="${quotesModule}"/>
<column name="feature_id" valueComputed="${quotesFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="quoteStats"/>
<column name="module_id" valueComputed="${quotesModule}"/>
<column name="feature_id" valueComputed="${quotesFeature}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,12 @@
<?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.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="feature.xml" relativeToChangelogFile="true"/>
<include file="module.xml" relativeToChangelogFile="true"/>
<include file="command.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,14 @@
<?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.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="quotes_feature-insertion">
<insert tableName="feature">
<column name="key" value="quotes"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,14 @@
<?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.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="quotes-module-insertion">
<insert tableName="module">
<column name="name" value="quotes"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,54 @@
<?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.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="quote-table">
<createTable tableName="quote">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="quote_pkey"/>
</column>
<column name="author_user_in_server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="adder_user_in_server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="source_channel_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="message_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="text" type="VARCHAR(4096)">
<constraints nullable="true"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
</createTable>
<addForeignKeyConstraint baseColumnNames="author_user_in_server_id" baseTableName="quote" constraintName="fk_quote_author_user_in_server_id"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="user_in_server_id"
referencedTableName="user_in_server" validate="true"/>
<addForeignKeyConstraint baseColumnNames="adder_user_in_server_id" baseTableName="quote" constraintName="fk_quote_adder_user_in_server_id"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="user_in_server_id"
referencedTableName="user_in_server" validate="true"/>
<addForeignKeyConstraint baseColumnNames="source_channel_id" baseTableName="quote" constraintName="fk_quote_source_channel"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
referencedTableName="channel" validate="true"/>
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="quote" constraintName="fk_quote_server"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
referencedTableName="server" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS quote_update_trigger ON quote;
CREATE TRIGGER quote_update_trigger BEFORE UPDATE ON quote FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,33 @@
<?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.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="quote_attachment-table">
<createTable tableName="quote_attachment">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="quote_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="url" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="is_image" type="BOOLEAN">
<constraints nullable="false"/>
</column>
</createTable>
<addPrimaryKey columnNames="server_id, id" tableName="quote_attachment" constraintName="pk_quote" validate="true"/>
<addForeignKeyConstraint baseColumnNames="quote_id" baseTableName="quote_attachment" constraintName="fk_quote_attachment_quote"
deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id"
referencedTableName="quote" validate="true"/>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,11 @@
<?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.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="quote.xml" relativeToChangelogFile="true"/>
<include file="quote_attachment.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
<?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.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="1.0.2/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,2 @@
abstracto.featureFlags.quotes.featureName=quotes
abstracto.featureFlags.quotes.enabled=false

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.raustria.bot</groupId>
<artifactId>deployment</artifactId>
<version>1.0.0</version>
<version>1.0.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -1,3 +1,4 @@
COMPOSE_PROJECT_NAME=r-austria
# database configuration
DATABASE_HOST=database
DATABASE_PORT=5432
@@ -29,4 +30,4 @@ PGADMIN_DEFAULT_EMAIL=sheldan@sheldan.dev
PGADMIN_DEFAULT_PASSWORD=admin
TOKEN=<INSERT TOKEN>
YOUTUBE_API_KEY=<INSERT KEY>
R_AUSTRIA_BOT_VERSION=1.0.0
R_AUSTRIA_BOT_VERSION=1.0.2

View File

@@ -3,7 +3,7 @@ version: '3.7'
services:
db:
image: ${REGISTRY_PREFIX}r_austria_bot_database:${R_AUSTRIA_BOT_VERSION}
container_name: database
container_name: ${COMPOSE_PROJECT_NAME}_database
restart: always
environment:
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
@@ -13,15 +13,15 @@ services:
networks:
- raustriabot
volumes:
- r-austria-bot-db-data:/var/lib/postgresql/data
- db-data:/var/lib/postgresql/data
deployment_container:
container_name: deployment
container_name: ${COMPOSE_PROJECT_NAME}_deployment
image: ${REGISTRY_PREFIX}r_austria_bot_deployment:${R_AUSTRIA_BOT_VERSION}
depends_on:
- db
environment:
DB_PASS: ${DATABASE_PASSWORD}
DB_HOST: ${DATABASE_HOST}
DB_HOST: ${COMPOSE_PROJECT_NAME}_${DATABASE_HOST}
DB_PORT: ${DATABASE_PORT}
DB_USER: ${DATABASE_USER}
DB_NAME: ${DATABASE_NAME}
@@ -32,7 +32,7 @@ services:
EXECUTE_TEMPLATES: ${EXECUTE_TEMPLATES}
LIQUIBASE_PATH: ${LIQUIBASE_PATH:-/liquibase}
POSTGRES_DRIVER_PATH: ${EXECUTE_DEPLOYMENT:-/postgres/driver.jar}
WAIT_HOSTS: ${DATABASE_HOST}:${DATABASE_PORT}
WAIT_HOSTS: ${COMPOSE_PROJECT_NAME}_${DATABASE_HOST}:${DATABASE_PORT}
networks:
- raustriabot
bot:
@@ -41,12 +41,12 @@ services:
- db
- deployment_container
restart: on-failure
container_name: raustriabot
container_name: ${COMPOSE_PROJECT_NAME}_raustriabot
environment:
TOKEN: ${TOKEN}
REMOTE_DEBUG: ${REMOTE_DEBUG}
DB_PASS: ${DATABASE_PASSWORD}
DB_HOST: ${DATABASE_HOST}
DB_HOST: ${COMPOSE_PROJECT_NAME}_${DATABASE_HOST}
DB_PORT: ${DATABASE_PORT}
DB_USER: ${DATABASE_USER}
DB_NAME: ${DATABASE_NAME}
@@ -60,10 +60,10 @@ services:
networks:
- raustriabot
volumes:
- r-austria-bot-bot-logs:/logs
- bot-logs:/logs
- ./config:/config
pgadmin:
container_name: pgadmin
container_name: ${COMPOSE_PROJECT_NAME}_pgadmin
image: ${REGISTRY_PREFIX}r_austria_bot_pg_admin:${R_AUSTRIA_BOT_VERSION}
depends_on:
- db
@@ -76,7 +76,7 @@ services:
networks:
- raustriabot
prometheus:
container_name: prometheus
container_name: ${COMPOSE_PROJECT_NAME}_prometheus
image: ${REGISTRY_PREFIX}r_austria_bot_prometheus:${R_AUSTRIA_BOT_VERSION}
depends_on:
- bot
@@ -87,9 +87,9 @@ services:
- raustriabot
volumes:
- ./res/prometheus-scrapper-password-filled:/etc/prometheus/micrometer_password
- r-austria-bot-prometheus-data:/prometheus
- prometheus-data:/prometheus
grafana:
container_name: grafana
container_name: ${COMPOSE_PROJECT_NAME}_grafana
image: ${REGISTRY_PREFIX}r_austria_bot_grafana:${R_AUSTRIA_BOT_VERSION}
depends_on:
- prometheus
@@ -99,31 +99,31 @@ services:
restart: unless-stopped
environment:
DB_PASS: ${GRAFANA_DATABASE_PASSWORD}
DB_HOST: ${DATABASE_HOST}
DB_HOST: ${COMPOSE_PROJECT_NAME}_${DATABASE_HOST}
DB_PORT: ${DATABASE_PORT}
DB_USER: ${GRAFANA_DATABASE_USER}
DB_NAME: ${DATABASE_NAME}
PROMETHEUS_HOST: 'prometheus'
PROMETHEUS_HOST: '${COMPOSE_PROJECT_NAME}_prometheus'
PROMETHEUS_PORT: 9090
LOKI_HOST: 'loki'
LOKI_HOST: '${COMPOSE_PROJECT_NAME}_loki'
LOKI_PORT: ${LOKI_PORT}
volumes:
- r-austria-bot-grafana-user-data:/var/lib/grafana
- grafana-user-data:/var/lib/grafana
networks:
- raustriabot
promtail:
container_name: promtail
container_name: ${COMPOSE_PROJECT_NAME}_promtail
image: ${REGISTRY_PREFIX}r_austria_bot_promtail:${R_AUSTRIA_BOT_VERSION}
depends_on:
- bot
restart: unless-stopped
command: -config.file=/mnt/config/promtail-config.yaml
volumes:
- r-austria-bot-bot-logs:/logs
- bot-logs:/logs
networks:
- raustriabot
loki:
container_name: loki
container_name: ${COMPOSE_PROJECT_NAME}_loki
image: ${REGISTRY_PREFIX}r_austria_bot_loki:${R_AUSTRIA_BOT_VERSION}
depends_on:
- promtail
@@ -140,7 +140,7 @@ networks:
name: raustriabot-network
volumes:
r-austria-bot-db-data:
r-austria-bot-grafana-user-data:
r-austria-bot-prometheus-data:
r-austria-bot-bot-logs:
db-data:
grafana-user-data:
prometheus-data:
bot-logs:

View File

@@ -0,0 +1 @@
${REST_PASSWORD}

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.raustria.bot</groupId>
<artifactId>deployment</artifactId>
<version>1.0.0</version>
<version>1.0.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -72,6 +72,16 @@
<destFileName>starboard.zip</destFileName>
</artifactItem>
<artifactItem>
<groupId>dev.sheldan.raustria.bot.templates</groupId>
<artifactId>quotes-templates</artifactId>
<version>${project.version}</version>
<type>zip</type>
<overWrite>true</overWrite>
<outputDirectory>${file.basedir}/deployment/template-artifacts/</outputDirectory>
<destFileName>quotes.zip</destFileName>
</artifactItem>
<!-- translation artefacts -->
<artifactItem>
@@ -104,6 +114,16 @@
<destFileName>starboard.zip</destFileName>
</artifactItem>
<artifactItem>
<groupId>dev.sheldan.raustria.bot.templates.translations</groupId>
<artifactId>quote-translations</artifactId>
<version>${project.version}</version>
<type>zip</type>
<overWrite>true</overWrite>
<outputDirectory>${file.basedir}/deployment/translation-artifacts/</outputDirectory>
<destFileName>quotes.zip</destFileName>
</artifactItem>
<!-- custom -->
<!-- liquibase artifacts -->
@@ -151,6 +171,19 @@
<destFileName>starboard.zip</destFileName>
</artifactItem>
<!-- custom modules -->
<artifactItem>
<groupId>dev.sheldan.raustria.bot.application.module</groupId>
<artifactId>quotes</artifactId>
<version>${project.version}</version>
<classifier>liquibase</classifier>
<type>zip</type>
<overWrite>true</overWrite>
<outputDirectory>${file.basedir}/deployment/liquibase-artifacts/</outputDirectory>
<destFileName>quotes.zip</destFileName>
</artifactItem>
<!-- customizations -->
<!-- overrides -->

View File

@@ -1,11 +1,12 @@
{
"template_artifacts": ["core","starboard", "link-embed"],
"translation_artifacts": ["core", "starboard", "link-embed"],
"template_artifacts": ["core","starboard", "link-embed", "quotes"],
"translation_artifacts": ["core", "starboard", "link-embed", "quotes"],
"liquibase_artifacts": [
{ "zip": "scheduling", "file": "scheduling-changeLog.xml" },
{ "zip": "core", "file": "core-changeLog.xml" },
{ "zip": "link-embed", "file": "link-embed-changeLog.xml"},
{ "zip": "starboard", "file": "starboard-changeLog.xml"}
{ "zip": "starboard", "file": "starboard-changeLog.xml"},
{ "zip": "quotes", "file": "quotes-changeLog.xml"}
]
}

View File

@@ -11,7 +11,7 @@ scrape_configs:
scheme: http
static_configs:
- targets:
- r_austra:8080
- raustriabot:8080
basic_auth:
username: "abstracto"
password_file: /etc/prometheus/micrometer_password

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.raustria.bot</groupId>
<artifactId>r-austria-bot</artifactId>
<version>1.0.0</version>
<version>1.0.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -12,15 +12,15 @@
<groupId>dev.sheldan.raustria.bot</groupId>
<artifactId>r-austria-bot</artifactId>
<version>1.0.0</version>
<version>1.0.2</version>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<!-- edit in release.yml as well -->
<!-- when releasing a new bot version, update the .env as well-->
<abstracto.version>1.3.11</abstracto.version>
<abstracto.templates.version>1.3.1</abstracto.templates.version>
<abstracto.version>1.3.13</abstracto.version>
<abstracto.templates.version>1.3.2</abstracto.templates.version>
</properties>
<modules>
@@ -56,7 +56,7 @@
<scm>
<url>https://maven.pkg.github.com/Sheldan/r-austria-bot</url>
<developerConnection>scm:git:git@github.com:Sheldan/r-austria-bot.git</developerConnection>
<tag>r-austria-bot-1.0.0</tag>
<tag>r-austria-bot-1.0.2</tag>
</scm>
</project>

View File

@@ -3,11 +3,17 @@
<parent>
<artifactId>r-austria-bot</artifactId>
<groupId>dev.sheldan.raustria.bot</groupId>
<version>1.0.0</version>
<version>1.0.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>dev.sheldan.raustria.bot.templates</groupId>
<artifactId>templates</artifactId>
<packaging>pom</packaging>
<modules>
<module>r-austria-bot-templates</module>
<module>r-austria-bot-translations</module>
</modules>
</project>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>dev.sheldan.raustria.bot.templates</groupId>
<artifactId>r-austria-bot-templates</artifactId>
<version>1.0.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>module-templates</artifactId>
<packaging>pom</packaging>
<modules>
<module>quotes-templates</module>
</modules>
</project>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>dev.sheldan.raustria.bot.templates</groupId>
<artifactId>module-templates</artifactId>
<version>1.0.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>quotes-templates</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<finalName>quotes-templates-${project.version}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,15 @@
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>zip</id>
<includeBaseDirectory>false</includeBaseDirectory>
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<outputDirectory>.</outputDirectory>
<directory>${project.basedir}/src/main/resources</directory>
</fileSet>
</fileSets>
</assembly>

View File

@@ -0,0 +1,26 @@
{
"embeds": [
{
<#include "abstracto_color">,
"author": {
<#assign authorName><@default_template_if_null authorName "quote_response_default_author_name"/></#assign>
<#assign channelName><@default_template_if_null sourceChannelName "quote_response_default_channel_name"/></#assign>
"name": "<@safe_include "quote_response_header_author_name"/>"
<#if authorAvatarURL??>,"avatar": "${authorAvatarURL}"</#if>
},
<#assign quoteId=quoteId>
<#assign quoteDescription=quoteContent>
<#assign quoteJumpUrl=quotedMessage.jumpUrl>
"description": "<@safe_include "quote_response_description"/>",
"footer": {
<#assign adderUserName><@default_template_if_null adderName "quote_response_default_adder_name"/></#assign>
"text": "<@safe_include "quote_response_footer_adder_name" />"
<#if adderAvatarURL??>,"icon": "${adderAvatarURL}"</#if>
},
<#if imageAttachmentURLs?size = 1>
"imageUrl": "${imageAttachmentURLs[0]}",
</#if>
"timeStamp": "${creationDate}"
}
]
}

View File

@@ -0,0 +1,6 @@
{
<#assign userName=userName>
<#assign adderCount=quoteCount>
<#assign quotedCount=authorCount>
"additionalMessage": "<@safe_include "quoteStats_response_message_text"/>"
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>templates</artifactId>
<groupId>dev.sheldan.raustria.bot.templates</groupId>
<version>1.0.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>r-austria-bot-templates</artifactId>
<packaging>pom</packaging>
<modules>
<module>module-templates</module>
</modules>
</project>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>dev.sheldan.raustria.bot.templates.translations</groupId>
<artifactId>r-austria-bot-translations</artifactId>
<version>1.0.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>module-translations</artifactId>
<packaging>pom</packaging>
<modules>
<module>quote-translations</module>
</modules>
</project>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>module-translations</artifactId>
<groupId>dev.sheldan.raustria.bot.templates.translations</groupId>
<version>1.0.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>quote-translations</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<finalName>quotes-translations-${project.version}</finalName>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,15 @@
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>zip</id>
<includeBaseDirectory>false</includeBaseDirectory>
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<outputDirectory>.</outputDirectory>
<directory>${project.basedir}/src/main/resources</directory>
</fileSet>
</fileSets>
</assembly>

View File

@@ -0,0 +1,2 @@
Shows a random quote from the server or from the given member.
Will display an error if there is no quote found.

View File

@@ -0,0 +1,2 @@
[**Quote #${quoteId?c}**](${quoteJumpUrl})
${quoteDescription}

View File

@@ -0,0 +1 @@
Deletes a quote completely from the database. Will ask for confirmation before doing so.

View File

@@ -0,0 +1 @@
This command can be used to retrieve and show a single quote by the ID

View File

@@ -0,0 +1,3 @@
This command can be used to search through all quotes by a text query.
It will return quotes in which the text of the quote contains the given query.
If there are more quotes found, the first one is returned.

View File

@@ -0,0 +1 @@
The text which should be searched for. A query whose text contains this will match.

View File

@@ -0,0 +1 @@
Can be used to see how often you or any other member has been quoted or how many quotes were added.

View File

@@ -0,0 +1 @@
:pencil: ${userName} been quoted ${quotedCount} times, and has added ${adderCount} quotes.

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>templates</artifactId>
<groupId>dev.sheldan.raustria.bot.templates</groupId>
<version>1.0.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>dev.sheldan.raustria.bot.templates.translations</groupId>
<artifactId>r-austria-bot-translations</artifactId>
<packaging>pom</packaging>
<modules>
<module>module-translations</module>
</modules>
</project>

1
tools/quotes-migration/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.db

View File

@@ -0,0 +1,17 @@
class LegacyQuoteAttachment:
message_id = 0
file_name = ''
url = ''
is_image: bool = False
class LegacyQuote:
id = 0
channel_id = 0
author_id = 0
adder_id = 0
creation_time_stamp = None
content = ''
message_id = 0
attachments = None

View File

@@ -0,0 +1,54 @@
import sqlite3
from sqlite3 import Error
import datetime
from dto import LegacyQuote, LegacyQuoteAttachment
def create_connection(file: str):
conn = None
try:
conn = sqlite3.connect(file)
except Error as e:
print(e)
return conn
def load_all_quotes_with_attachments(conn):
cur = conn.cursor()
cur.execute("SELECT q.id, q.chan_id, q.author_id, "
"q.adder_id, q.jump_url, q.'timestamp', q.content,"
"a.msg_id, a.filename, a.url, a.is_image "
"FROM quotes q left outer join attachments a ON q.jump_url like '%' || a.msg_id")
rows = cur.fetchall()
quotes = {}
for row in rows:
quote_id = row[0]
if quote_id not in quotes:
quotes[quote_id] = LegacyQuote()
current_quote = quotes[quote_id]
current_quote.id = quote_id
current_quote.channel_id = row[1]
current_quote.author_id = row[2]
current_quote.adder_id = row[3]
if len(row[5]) > 25:
current_quote.creation_time_stamp = datetime.datetime.strptime(row[5], '%Y-%m-%d %H:%M:%S.%f')
elif len(row[5]) != 19:
current_quote.creation_time_stamp = datetime.datetime.strptime(row[5], '%Y-%m-%d %H:%M:%S.%f')
else:
current_quote.creation_time_stamp = datetime.datetime.strptime(row[5], '%Y-%m-%d %H:%M:%S')
current_quote.content = row[6]
current_quote.message_id = row[4][row[4].rindex('/')+1:]
if row[7] is not None:
if current_quote.attachments is None:
current_quote.attachments = []
attachment = LegacyQuoteAttachment()
attachment.message_id = row[7]
attachment.file_name = row[8]
attachment.url = row[9]
attachment.is_image = row[10] == 1
current_quote.attachments.append(attachment)
return quotes

View File

@@ -0,0 +1,22 @@
from legacy_loader import create_connection, load_all_quotes_with_attachments
from quote_importer import insert_quotes, create_users, create_channels
import sqlalchemy as db
import os
server_id = os.getenv('SERVER_ID')
conn = create_connection('new_quotes.db')
all_quotes = load_all_quotes_with_attachments(conn)
db_host = os.getenv('DB_HOST')
db_port = os.getenv('DB_PORT')
db_database = os.getenv('DB_NAME')
db_user = os.getenv('DB_USER')
db_password = os.getenv('DB_PASS')
engine = db.create_engine('postgresql://%s:%s@%s:%s/%s' % (db_user, db_password, db_host, db_port, db_database))
with engine.connect() as con:
with con.begin():
create_users(server_id, all_quotes, con)
create_channels(server_id, all_quotes, con)
with con.begin():
insert_quotes(server_id, all_quotes, con)

View File

@@ -0,0 +1,70 @@
from sqlalchemy.sql import text
def insert_quotes(server_id: int, quotes, con):
for quote_id in quotes:
quote = quotes[quote_id]
statement = text("""INSERT INTO quote(id, server_id, author_user_in_server_id, adder_user_in_server_id, source_channel_id, message_id, text, created)
VALUES(:id, :server_id,
(select user_in_server_id from user_in_server where user_id = :author_id and server_id = :server_id),
(select user_in_server_id from user_in_server where user_id = :adder_id and server_id = :server_id),
:channel_id, :message_id, :text, :created)""")
con.execute(statement, {'id': quote.id, 'server_id': server_id, 'author_id': quote.author_id, 'adder_id': quote.adder_id,
'channel_id': quote.channel_id, 'message_id': quote.message_id,
'text': quote.content, 'created': quote.creation_time_stamp})
if quote.attachments:
attachment_statement = text("""INSERT INTO quote_attachment(quote_id, server_id, url, is_image)
VALUES(:quote_id, :server_id, :url, :is_image)""")
for attachment in quote.attachments:
con.execute(attachment_statement, {'quote_id': quote_id, 'server_id': server_id, 'url': attachment.url, 'is_image': attachment.is_image})
def create_channels(server_id: int, quotes, con):
channel_ids = {}
for quote_id in quotes:
quote = quotes[quote_id]
if not does_channel_exist(quote.channel_id, con) and quote.channel_id not in channel_ids:
channel_ids[quote.channel_id] = 1
for channel_id in channel_ids:
create_channel(channel_id, server_id, con)
def create_users(server_id: int, quotes, con):
created_users = {}
for quote_id in quotes:
quote = quotes[quote_id]
if not does_user_exist(quote.adder_id, con) and quote.adder_id not in created_users:
create_user(quote.adder_id, con)
create_user_in_server(quote.adder_id, server_id, con)
created_users[quote.adder_id] = 1
if not does_user_exist(quote.author_id, con) and quote.author_id not in created_users:
create_user(quote.author_id, con)
create_user_in_server(quote.author_id, server_id, con)
created_users[quote.author_id] = 1
def does_user_exist(user_id, con):
statement = text("""SELECT count(1) FROM auser where id = :id""")
return con.execute(statement, {'id': user_id}).fetchone()[0] == 1
def does_channel_exist(channel_id, con):
statement = text("""SELECT count(1) FROM channel where id = :id""")
return con.execute(statement, {'id': channel_id}).fetchone()[0] == 1
def create_user(user_id, con):
statement = text("""INSERT INTO auser(id) VALUES(:id)""")
print(f'Creating user {user_id}')
con.execute(statement, {'id': user_id})
def create_channel(channel_id, server_id, con):
statement = text("""INSERT INTO channel(id, server_id, type, deleted) VALUES(:id, :server_id, 'TEXT', false)""")
print(f'Creating channel {channel_id}')
con.execute(statement, {'id': channel_id, 'server_id': server_id})
def create_user_in_server(user_id, server_id, con):
statement = text("""INSERT INTO user_in_server(server_id, user_id) VALUES(:server_id, :user_id) returning user_in_server_id""")
return con.execute(statement, {'user_id': user_id, 'server_id': server_id}).fetchone()[0]