[AB-80] added tracking of emotes used by server members and configuration

updated sonar scanner version
changed some commands to be silent instead of adding a check reaction
This commit is contained in:
Sheldan
2020-11-11 17:11:30 +01:00
parent c60cdb9d98
commit 04f1db2408
202 changed files with 7989 additions and 131 deletions

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,21 @@
package dev.sheldan.abstracto.statistic.config;
import dev.sheldan.abstracto.core.command.config.ModuleInfo;
import dev.sheldan.abstracto.core.command.config.ModuleInterface;
import org.springframework.stereotype.Component;
@Component
public class StatisticModule implements ModuleInterface {
public static final String STATISTIC = "statistic";
@Override
public ModuleInfo getInfo() {
return ModuleInfo.builder().name(STATISTIC).description("Module containing commands related to statistic.").build();
}
@Override
public String getParentModule() {
return "default";
}
}

View File

@@ -0,0 +1,60 @@
package dev.sheldan.abstracto.statistic.emotes.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.FeatureEnum;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.config.StatisticModule;
import dev.sheldan.abstracto.statistic.emotes.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emotes.service.TrackedEmoteService;
import dev.sheldan.abstracto.statistic.emotes.service.management.TrackedEmoteManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class DeleteTrackedEmote extends AbstractConditionableCommand {
@Autowired
private TrackedEmoteManagementService trackedEmoteManagementService;
@Autowired
private TrackedEmoteService trackedEmoteService;
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
TrackedEmote fakeTrackedEmote = (TrackedEmote) parameters.get(0);
TrackedEmote trackedEmote = trackedEmoteManagementService.loadByTrackedEmoteServer(fakeTrackedEmote.getTrackedEmoteId());
trackedEmoteService.deleteTrackedEmote(trackedEmote);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("trackedEmote").templated(true).type(TrackedEmote.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("deleteTrackedEmote")
.module(StatisticModule.STATISTIC)
.templated(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return StatisticFeatures.EMOTE_TRACKING;
}
}

View File

@@ -0,0 +1,89 @@
package dev.sheldan.abstracto.statistic.emotes.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.FeatureEnum;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.config.StatisticModule;
import dev.sheldan.abstracto.statistic.emotes.model.EmoteStatsModel;
import dev.sheldan.abstracto.statistic.emotes.service.UsedEmoteService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class DeletedEmoteStats extends AbstractConditionableCommand {
@Autowired
private UsedEmoteService usedEmoteService;
@Autowired
private ChannelService channelService;
public static final String EMOTE_STATS_STATIC_DELETED_RESPONSE = "deletedEmoteStats_static_response";
public static final String EMOTE_STATS_ANIMATED_DELETED_RESPONSE = "deletedEmoteStats_animated_response";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
Instant statsSince = Instant.EPOCH;
if(!parameters.isEmpty()) {
Duration duration = (Duration) parameters.get(0);
statsSince = Instant.now().minus(duration);
}
EmoteStatsModel emoteStatsModel = usedEmoteService.getDeletedEmoteStatsForServerSince(commandContext.getUserInitiatedContext().getServer(), statsSince);
List<CompletableFuture<Message>> messagePromises = new ArrayList<>();
if(!emoteStatsModel.getStaticEmotes().isEmpty()) {
log.trace("Deleted emote stats has {} static emotes since {}.", emoteStatsModel.getStaticEmotes().size(), statsSince);
messagePromises.addAll(channelService.sendEmbedTemplateInChannel(EMOTE_STATS_STATIC_DELETED_RESPONSE, emoteStatsModel, commandContext.getChannel()));
}
if(!emoteStatsModel.getAnimatedEmotes().isEmpty()) {
log.trace("Deleted emote stats has {} animated emotes since {}.", emoteStatsModel.getAnimatedEmotes(), statsSince);
messagePromises.addAll(channelService.sendEmbedTemplateInChannel(EMOTE_STATS_ANIMATED_DELETED_RESPONSE, emoteStatsModel, commandContext.getChannel()));
}
if(!emoteStatsModel.areStatsAvailable()) {
log.info("No delete emote stats available for guild {} since {}.", commandContext.getGuild().getIdLong(), statsSince);
messagePromises.addAll(channelService.sendEmbedTemplateInChannel(EmoteStats.EMOTE_STATS_NO_STATS_AVAILABLE, new Object(), commandContext.getChannel()));
}
return FutureUtils.toSingleFutureGeneric(messagePromises)
.thenApply(unused -> CommandResult.fromIgnored());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("period").templated(true).optional(true).type(Duration.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("deletedEmoteStats")
.module(StatisticModule.STATISTIC)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return StatisticFeatures.EMOTE_TRACKING;
}
}

View File

@@ -0,0 +1,64 @@
package dev.sheldan.abstracto.statistic.emotes.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.FeatureEnum;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.config.StatisticModule;
import dev.sheldan.abstracto.statistic.emotes.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emotes.service.TrackedEmoteService;
import dev.sheldan.abstracto.statistic.emotes.service.management.TrackedEmoteManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class DisableEmoteTracking extends AbstractConditionableCommand {
@Autowired
private TrackedEmoteService trackedEmoteService;
@Autowired
private TrackedEmoteManagementService trackedEmoteManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
if(!parameters.isEmpty()) {
TrackedEmote fakeTrackedEmote = (TrackedEmote) parameters.get(0);
TrackedEmote trackedEmote = trackedEmoteManagementService.loadByTrackedEmoteServer(fakeTrackedEmote.getTrackedEmoteId());
trackedEmoteManagementService.disableTrackedEmote(trackedEmote);
} else {
trackedEmoteService.disableEmoteTracking(commandContext.getGuild());
}
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("trackedEmote").templated(true).optional(true).type(TrackedEmote.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("disableEmoteTracking")
.module(StatisticModule.STATISTIC)
.templated(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return StatisticFeatures.EMOTE_TRACKING;
}
}

View File

@@ -0,0 +1,94 @@
package dev.sheldan.abstracto.statistic.emotes.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.FeatureEnum;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.config.StatisticModule;
import dev.sheldan.abstracto.statistic.emotes.model.EmoteStatsModel;
import dev.sheldan.abstracto.statistic.emotes.service.UsedEmoteService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class EmoteStats extends AbstractConditionableCommand {
@Autowired
private UsedEmoteService usedEmoteService;
@Autowired
private ChannelService channelService;
@Autowired
private FeatureModeService featureModeService;
public static final String EMOTE_STATS_STATIC_RESPONSE = "emoteStats_static_response";
public static final String EMOTE_STATS_ANIMATED_RESPONSE = "emoteStats_animated_response";
public static final String EMOTE_STATS_NO_STATS_AVAILABLE = "emoteStats_no_stats_available";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
Instant statsSince = Instant.EPOCH;
if(!parameters.isEmpty()) {
Duration duration = (Duration) parameters.get(0);
statsSince = Instant.now().minus(duration);
}
EmoteStatsModel emoteStatsModel = usedEmoteService.getActiveEmoteStatsForServerSince(commandContext.getUserInitiatedContext().getServer(), statsSince);
List<CompletableFuture<Message>> messagePromises = new ArrayList<>();
if(!emoteStatsModel.getStaticEmotes().isEmpty()) {
log.trace("Emote stats has {} static emotes since {}.", emoteStatsModel.getStaticEmotes().size(), statsSince);
messagePromises.addAll(channelService.sendEmbedTemplateInChannel(EMOTE_STATS_STATIC_RESPONSE, emoteStatsModel, commandContext.getChannel()));
}
if(!emoteStatsModel.getAnimatedEmotes().isEmpty()) {
log.trace("Emote stats has {} animated emotes since {}.", emoteStatsModel.getAnimatedEmotes(), statsSince);
messagePromises.addAll(channelService.sendEmbedTemplateInChannel(EMOTE_STATS_ANIMATED_RESPONSE, emoteStatsModel, commandContext.getChannel()));
}
if(!emoteStatsModel.areStatsAvailable()) {
log.info("No emote stats available for guild {} since {}.", commandContext.getGuild().getIdLong(), statsSince);
messagePromises.addAll(channelService.sendEmbedTemplateInChannel(EMOTE_STATS_NO_STATS_AVAILABLE, new Object(), commandContext.getChannel()));
}
return FutureUtils.toSingleFutureGeneric(messagePromises)
.thenApply(unused -> CommandResult.fromIgnored());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("period").templated(true).optional(true).type(Duration.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("emoteStats")
.module(StatisticModule.STATISTIC)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return StatisticFeatures.EMOTE_TRACKING;
}
}

View File

@@ -0,0 +1,130 @@
package dev.sheldan.abstracto.statistic.emotes.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.FeatureEnum;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
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.utils.FileUtils;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.config.StatisticModule;
import dev.sheldan.abstracto.statistic.emotes.exception.DownloadEmoteStatsFileTooBigException;
import dev.sheldan.abstracto.statistic.emotes.model.DownloadEmoteStatsModel;
import dev.sheldan.abstracto.statistic.emotes.model.database.UsedEmote;
import dev.sheldan.abstracto.statistic.emotes.service.management.UsedEmoteManagementService;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class ExportEmoteStats extends AbstractConditionableCommand {
public static final String DOWNLOAD_EMOTE_STATS_NO_STATS_AVAILABLE_RESPONSE_TEMPLATE_KEY = "downloadEmoteStats_no_stats_available_response";
public static final String DOWNLOAD_EMOTE_STATS_FILE_NAME_TEMPLATE_KEY = "downloadEmoteStats_file_name";
public static final String DOWNLOAD_EMOTE_STATS_FILE_CONTENT_TEMPLATE_KEY = "downloadEmoteStats_file_content";
public static final String DOWNLOAD_EMOTE_STATS_RESPONSE_TEMPLATE_KEY = "downloadEmoteStats_response";
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private TemplateService templateService;
@Autowired
private ChannelService channelService;
@Autowired
private UsedEmoteManagementService usedEmoteManagementService;
@Autowired
private FileUtils fileUtils;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
Instant statsSince = Instant.EPOCH;
if(!parameters.isEmpty()) {
Duration duration = (Duration) parameters.get(0);
statsSince = Instant.now().minus(duration);
}
AServer actualServer = serverManagementService.loadServer(commandContext.getGuild().getIdLong());
List<UsedEmote> usedEmotes = usedEmoteManagementService.loadEmoteUsagesForServerSince(actualServer, statsSince);
if(usedEmotes.isEmpty()) {
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(DOWNLOAD_EMOTE_STATS_NO_STATS_AVAILABLE_RESPONSE_TEMPLATE_KEY, new Object(), commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
}
// info might not be nice to handle in the template, and 1970 would look weird to users
// effectively this means if its null its EPOCH, so it would be possible to render it still
Instant toUseForModel = statsSince != Instant.EPOCH ? statsSince : null;
DownloadEmoteStatsModel model = DownloadEmoteStatsModel
.builder()
.emotes(usedEmotes)
.guild(commandContext.getGuild())
.downloadDate(Instant.now())
.requester(commandContext.getAuthor())
.statsSince(toUseForModel)
.build();
String fileName = templateService.renderTemplate(DOWNLOAD_EMOTE_STATS_FILE_NAME_TEMPLATE_KEY, model);
String fileContent = templateService.renderTemplate(DOWNLOAD_EMOTE_STATS_FILE_CONTENT_TEMPLATE_KEY, model);
File tempFile = fileUtils.createTempFile(fileName);
try {
fileUtils.writeContentToFile(tempFile, fileContent);
long maxFileSize = commandContext.getGuild().getMaxFileSize();
if(maxFileSize < tempFile.length()) {
throw new DownloadEmoteStatsFileTooBigException(tempFile.length(), maxFileSize);
}
MessageToSend messageToSend = templateService.renderEmbedTemplate(DOWNLOAD_EMOTE_STATS_RESPONSE_TEMPLATE_KEY, model);
messageToSend.setFileToSend(tempFile);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
} catch (IOException e) {
throw new AbstractoRunTimeException(e);
} finally {
try {
fileUtils.safeDelete(tempFile);
} catch (IOException e) {
log.error("Failed to delete temporary export emote statistics file {}.", tempFile.getAbsoluteFile(), e);
}
}
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("period").templated(true).optional(true).type(Duration.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("exportEmoteStats")
.module(StatisticModule.STATISTIC)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return StatisticFeatures.EMOTE_TRACKING;
}
}

View File

@@ -0,0 +1,102 @@
package dev.sheldan.abstracto.statistic.emotes.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.FeatureEnum;
import dev.sheldan.abstracto.core.config.FeatureMode;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.config.StatisticModule;
import dev.sheldan.abstracto.statistic.emotes.config.EmoteTrackingMode;
import dev.sheldan.abstracto.statistic.emotes.model.EmoteStatsModel;
import dev.sheldan.abstracto.statistic.emotes.service.UsedEmoteService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class ExternalEmoteStats extends AbstractConditionableCommand {
@Autowired
private UsedEmoteService usedEmoteService;
@Autowired
private ChannelService channelService;
@Autowired
private FeatureModeService featureModeService;
public static final String EMOTE_STATS_STATIC_EXTERNAL_RESPONSE = "externalEmoteStats_static_response";
public static final String EMOTE_STATS_ANIMATED_EXTERNAL_RESPONSE = "externalEmoteStats_animated_response";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
Instant statsSince = Instant.EPOCH;
if(!parameters.isEmpty()) {
Duration duration = (Duration) parameters.get(0);
statsSince = Instant.now().minus(duration);
}
EmoteStatsModel emoteStatsModel = usedEmoteService.getExternalEmoteStatsForServerSince(commandContext.getUserInitiatedContext().getServer(), statsSince);
List<CompletableFuture<Message>> messagePromises = new ArrayList<>();
if(!emoteStatsModel.getStaticEmotes().isEmpty()) {
log.trace("External emote stats has {} static emotes since {}.", emoteStatsModel.getStaticEmotes().size(), statsSince);
messagePromises.addAll(channelService.sendEmbedTemplateInChannel(EMOTE_STATS_STATIC_EXTERNAL_RESPONSE, emoteStatsModel, commandContext.getChannel()));
}
if(!emoteStatsModel.getAnimatedEmotes().isEmpty()) {
log.trace("External emote stats has {} animated emotes since {}.", emoteStatsModel.getAnimatedEmotes(), statsSince);
messagePromises.addAll(channelService.sendEmbedTemplateInChannel(EMOTE_STATS_ANIMATED_EXTERNAL_RESPONSE, emoteStatsModel, commandContext.getChannel()));
}
if(!emoteStatsModel.areStatsAvailable()) {
log.info("No external emote stats available for guild {} since {}.", commandContext.getGuild().getIdLong(), statsSince);
messagePromises.addAll(channelService.sendEmbedTemplateInChannel(EmoteStats.EMOTE_STATS_NO_STATS_AVAILABLE, new Object(), commandContext.getChannel()));
}
return FutureUtils.toSingleFutureGeneric(messagePromises)
.thenApply(unused -> CommandResult.fromIgnored());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("period").templated(true).optional(true).type(Duration.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("externalEmoteStats")
.module(StatisticModule.STATISTIC)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return StatisticFeatures.EMOTE_TRACKING;
}
@Override
public List<FeatureMode> getFeatureModeLimitations() {
return Arrays.asList(EmoteTrackingMode.EXTERNAL_EMOTES);
}
}

View File

@@ -0,0 +1,68 @@
package dev.sheldan.abstracto.statistic.emotes.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.FeatureEnum;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.config.StatisticModule;
import dev.sheldan.abstracto.statistic.emotes.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emotes.service.UsedEmoteService;
import dev.sheldan.abstracto.statistic.emotes.service.management.TrackedEmoteManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@Component
public class PurgeEmoteStats extends AbstractConditionableCommand {
@Autowired
private TrackedEmoteManagementService trackedEmoteManagementService;
@Autowired
private UsedEmoteService usedEmoteService;
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
TrackedEmote fakeTrackedEmote = (TrackedEmote) parameters.get(0);
TrackedEmote trackedEmote = trackedEmoteManagementService.loadByTrackedEmoteServer(fakeTrackedEmote.getTrackedEmoteId());
Instant since = Instant.EPOCH;
if(parameters.size() > 1) {
Duration parameter = (Duration) parameters.get(1);
since = Instant.now().minus(parameter);
}
usedEmoteService.purgeEmoteUsagesSince(trackedEmote, since);
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("trackedEmote").templated(true).type(TrackedEmote.class).build());
parameters.add(Parameter.builder().name("period").templated(true).optional(true).type(Duration.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("purgeEmoteStats")
.module(StatisticModule.STATISTIC)
.templated(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return StatisticFeatures.EMOTE_TRACKING;
}
}

View File

@@ -0,0 +1,50 @@
package dev.sheldan.abstracto.statistic.emotes.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.FeatureEnum;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.config.StatisticModule;
import dev.sheldan.abstracto.statistic.emotes.service.TrackedEmoteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
public class ResetEmoteStats extends AbstractConditionableCommand {
@Autowired
private TrackedEmoteService trackedEmoteService;
@Override
public CommandResult execute(CommandContext commandContext) {
trackedEmoteService.resetEmoteStats(commandContext.getGuild());
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("resetEmoteStats")
.module(StatisticModule.STATISTIC)
.templated(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return StatisticFeatures.EMOTE_TRACKING;
}
}

View File

@@ -0,0 +1,76 @@
package dev.sheldan.abstracto.statistic.emotes.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.exception.AbstractoTemplatedException;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.FeatureMode;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.config.StatisticModule;
import dev.sheldan.abstracto.statistic.emotes.config.EmoteTrackingMode;
import dev.sheldan.abstracto.statistic.emotes.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emotes.service.management.TrackedEmoteManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class ShowExternalTrackedEmote extends AbstractConditionableCommand {
public static final String SHOW_EXTERNAL_TRACKED_EMOTE_RESPONSE_TEMPLATE_KEY = "showExternalTrackedEmote_response";
@Autowired
private ChannelService channelService;
@Autowired
private TrackedEmoteManagementService trackedEmoteManagementService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
checkParameters(commandContext);
List<Object> parameters = commandContext.getParameters().getParameters();
TrackedEmote fakeTrackedEmote = (TrackedEmote) parameters.get(0);
TrackedEmote trackedEmote = trackedEmoteManagementService.loadByTrackedEmoteServer(fakeTrackedEmote.getTrackedEmoteId());
if(!trackedEmote.getExternal()) {
throw new AbstractoTemplatedException("Emote is not external", "showExternalTrackedEmote_emote_is_not_external");
}
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(SHOW_EXTERNAL_TRACKED_EMOTE_RESPONSE_TEMPLATE_KEY, trackedEmote, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("trackedEmote").templated(true).type(TrackedEmote.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("showExternalTrackedEmote")
.module(StatisticModule.STATISTIC)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return StatisticFeatures.EMOTE_TRACKING;
}
@Override
public List<FeatureMode> getFeatureModeLimitations() {
return Arrays.asList(EmoteTrackingMode.EXTERNAL_EMOTES);
}
}

View File

@@ -0,0 +1,114 @@
package dev.sheldan.abstracto.statistic.emotes.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.FeatureEnum;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.config.StatisticModule;
import dev.sheldan.abstracto.statistic.emotes.config.EmoteTrackingMode;
import dev.sheldan.abstracto.statistic.emotes.model.TrackedEmoteOverview;
import dev.sheldan.abstracto.statistic.emotes.service.TrackedEmoteService;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class ShowTrackedEmotes extends AbstractConditionableCommand {
@Autowired
private TrackedEmoteService trackedEmoteService;
@Autowired
private ChannelService channelService;
@Autowired
private FeatureModeService featureModeService;
public static final String EMOTE_STATS_STATIC_RESPONSE = "showTrackedEmotes_static_response";
public static final String EMOTE_STATS_ANIMATED_RESPONSE = "showTrackedEmotes_animated_response";
public static final String EMOTE_STATS_EXTERNAL_ANIMATED_RESPONSE = "showTrackedEmotes_external_animated_response";
public static final String EMOTE_STATS_EXTERNAL_STATIC_RESPONSE = "showTrackedEmotes_external_static_response";
public static final String EMOTE_STATS_DELETED_STATIC_RESPONSE = "showTrackedEmotes_deleted_static_response";
public static final String EMOTE_STATS_DELETED_ANIMATED_RESPONSE = "showTrackedEmotes_deleted_animated_response";
public static final String EMOTE_STATS_NO_STATS_AVAILABLE = "showTrackedEmotes_no_emotes_available";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
checkParameters(commandContext);
Boolean showTrackingDisabled = false;
if(!commandContext.getParameters().getParameters().isEmpty()) {
showTrackingDisabled = (Boolean) commandContext.getParameters().getParameters().get(0);
}
boolean externalTrackingEnabled = featureModeService.featureModeActive(StatisticFeatures.EMOTE_TRACKING, commandContext.getGuild().getIdLong(), EmoteTrackingMode.EXTERNAL_EMOTES);
TrackedEmoteOverview trackedEmoteOverview = trackedEmoteService.loadTrackedEmoteOverview(commandContext.getGuild(), showTrackingDisabled);
boolean noStatsAvailable = true;
List<CompletableFuture<Message>> messagePromises = new ArrayList<>();
if(!trackedEmoteOverview.getStaticEmotes().isEmpty()) {
noStatsAvailable = false;
messagePromises.addAll(channelService.sendEmbedTemplateInChannel(EMOTE_STATS_STATIC_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
}
if(!trackedEmoteOverview.getAnimatedEmotes().isEmpty()) {
noStatsAvailable = false;
messagePromises.addAll(channelService.sendEmbedTemplateInChannel(EMOTE_STATS_ANIMATED_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
}
if(!trackedEmoteOverview.getDeletedStaticEmotes().isEmpty()) {
noStatsAvailable = false;
messagePromises.addAll(channelService.sendEmbedTemplateInChannel(EMOTE_STATS_DELETED_STATIC_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
}
if(!trackedEmoteOverview.getDeletedAnimatedEmotes().isEmpty()) {
noStatsAvailable = false;
messagePromises.addAll(channelService.sendEmbedTemplateInChannel(EMOTE_STATS_DELETED_ANIMATED_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
}
if(externalTrackingEnabled) {
if(!trackedEmoteOverview.getExternalStaticEmotes().isEmpty()) {
noStatsAvailable = false;
messagePromises.addAll(channelService.sendEmbedTemplateInChannel(EMOTE_STATS_EXTERNAL_STATIC_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
}
if(!trackedEmoteOverview.getExternalAnimatedEmotes().isEmpty()) {
noStatsAvailable = false;
messagePromises.addAll(channelService.sendEmbedTemplateInChannel(EMOTE_STATS_EXTERNAL_ANIMATED_RESPONSE, trackedEmoteOverview, commandContext.getChannel()));
}
}
if(noStatsAvailable) {
messagePromises.addAll(channelService.sendEmbedTemplateInChannel(EMOTE_STATS_NO_STATS_AVAILABLE, new Object(), commandContext.getChannel()));
}
return FutureUtils.toSingleFutureGeneric(messagePromises)
.thenApply(unused -> CommandResult.fromIgnored());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
parameters.add(Parameter.builder().name("showAll").templated(true).optional(true).type(Boolean.class).build());
return CommandConfiguration.builder()
.name("showTrackedEmotes")
.module(StatisticModule.STATISTIC)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return StatisticFeatures.EMOTE_TRACKING;
}
}

View File

@@ -0,0 +1,60 @@
package dev.sheldan.abstracto.statistic.emotes.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.FeatureEnum;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.config.StatisticModule;
import dev.sheldan.abstracto.statistic.emotes.model.TrackedEmoteSynchronizationResult;
import dev.sheldan.abstracto.statistic.emotes.service.TrackedEmoteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class SyncTrackedEmotes extends AbstractConditionableCommand {
public static final String SYNC_TRACKED_EMOTES_RESULT_RESPONSE = "syncTrackedEmotes_result_response";
@Autowired
private TrackedEmoteService trackedEmoteService;
@Autowired
private ChannelService channelService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
TrackedEmoteSynchronizationResult syncResult = trackedEmoteService.synchronizeTrackedEmotes(commandContext.getGuild());
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInChannel(SYNC_TRACKED_EMOTES_RESULT_RESPONSE, syncResult, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("syncTrackedEmotes")
.module(StatisticModule.STATISTIC)
.templated(true)
.async(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return StatisticFeatures.EMOTE_TRACKING;
}
}

View File

@@ -0,0 +1,87 @@
package dev.sheldan.abstracto.statistic.emotes.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.exception.IncorrectParameterException;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.exception.IncorrectFeatureModeException;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.config.StatisticModule;
import dev.sheldan.abstracto.statistic.emotes.command.parameter.TrackEmoteParameter;
import dev.sheldan.abstracto.statistic.emotes.config.EmoteTrackingMode;
import dev.sheldan.abstracto.statistic.emotes.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emotes.service.TrackedEmoteService;
import dev.sheldan.abstracto.statistic.emotes.service.management.TrackedEmoteManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Component
public class TrackEmote extends AbstractConditionableCommand {
@Autowired
private TrackedEmoteService trackedEmoteService;
@Autowired
private TrackedEmoteManagementService trackedEmoteManagementService;
@Autowired
private EmoteService emoteService;
@Autowired
private FeatureModeService featureModeService;
@Override
public CommandResult execute(CommandContext commandContext) {
checkParameters(commandContext);
TrackEmoteParameter emoteToTrack = (TrackEmoteParameter) commandContext.getParameters().getParameters().get(0);
Long emoteId = emoteToTrack.getTrackedEmote().getTrackedEmoteId().getEmoteId();
long serverId = commandContext.getGuild().getIdLong();
if(trackedEmoteManagementService.trackedEmoteExists(emoteId, serverId)) {
TrackedEmote trackedemote = trackedEmoteManagementService.loadByEmoteId(emoteId, serverId);
trackedEmoteManagementService.enableTrackedEmote(trackedemote);
} else if(emoteToTrack.getEmote() != null) {
boolean external = !emoteService.emoteIsFromGuild(emoteToTrack.getEmote(), commandContext.getGuild());
if(external) {
boolean trackExternalEmotes = featureModeService.featureModeActive(StatisticFeatures.EMOTE_TRACKING, serverId, EmoteTrackingMode.EXTERNAL_EMOTES);
if(!trackExternalEmotes) {
throw new IncorrectFeatureModeException(StatisticFeatures.EMOTE_TRACKING, Arrays.asList(EmoteTrackingMode.EXTERNAL_EMOTES));
}
}
trackedEmoteService.createFakeTrackedEmote(emoteToTrack.getEmote(), commandContext.getGuild());
} else {
throw new IncorrectParameterException(this, getConfiguration().getParameters().get(0).getName());
}
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("emote").templated(true).type(TrackEmoteParameter.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("trackEmote")
.module(StatisticModule.STATISTIC)
.templated(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureEnum getFeature() {
return StatisticFeatures.EMOTE_TRACKING;
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.statistic.emotes.command.parameter;
import dev.sheldan.abstracto.statistic.emotes.model.database.TrackedEmote;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Emote;
@Getter
@Setter
@Builder
public class TrackEmoteParameter {
private Emote emote;
private TrackedEmote trackedEmote;
}

View File

@@ -0,0 +1,42 @@
package dev.sheldan.abstracto.statistic.emotes.command.parameter.handler;
import dev.sheldan.abstracto.core.command.handler.CommandParameterHandler;
import dev.sheldan.abstracto.core.command.handler.CommandParameterIterators;
import dev.sheldan.abstracto.core.command.handler.provided.EmoteParameterHandler;
import dev.sheldan.abstracto.statistic.emotes.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emotes.service.TrackedEmoteService;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class TrackedEmoteParameterHandler implements CommandParameterHandler {
@Autowired
private EmoteParameterHandler emoteParameterHandler;
@Autowired
private TrackedEmoteService trackedEmoteService;
@Override
public boolean handles(Class clazz) {
return clazz.equals(TrackedEmote.class);
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
Emote emote = (Emote) emoteParameterHandler.handle(input, iterators, Emote.class, context);
if(emote != null) {
return trackedEmoteService.getFakeTrackedEmote(emote, context.getGuild());
} else {
long trackedEmoteId = Long.parseLong(input);
return trackedEmoteService.getFakeTrackedEmote(trackedEmoteId, context.getGuild());
}
}
@Override
public Integer getPriority() {
return 51;
}
}

View File

@@ -0,0 +1,45 @@
package dev.sheldan.abstracto.statistic.emotes.command.parameter.handler;
import dev.sheldan.abstracto.core.command.handler.CommandParameterHandler;
import dev.sheldan.abstracto.core.command.handler.CommandParameterIterators;
import dev.sheldan.abstracto.core.command.handler.provided.EmoteParameterHandler;
import dev.sheldan.abstracto.statistic.emotes.command.parameter.TrackEmoteParameter;
import dev.sheldan.abstracto.statistic.emotes.service.TrackedEmoteService;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class TrackedEmoteParameterParameterHandler implements CommandParameterHandler {
@Autowired
private EmoteParameterHandler emoteParameterHandler;
@Autowired
private TrackedEmoteService trackedEmoteService;
@Override
public boolean handles(Class clazz) {
return clazz.equals(TrackEmoteParameter.class);
}
@Override
public Object handle(String input, CommandParameterIterators iterators, Class clazz, Message context) {
TrackEmoteParameter parameter = TrackEmoteParameter.builder().build();
Emote emote = (Emote) emoteParameterHandler.handle(input, iterators, Emote.class, context);
if(emote != null) {
parameter.setEmote(emote);
parameter.setTrackedEmote(trackedEmoteService.getFakeTrackedEmote(emote, context.getGuild()));
} else {
long trackedEmoteId = Long.parseLong(input);
parameter.setTrackedEmote(trackedEmoteService.getFakeTrackedEmote(trackedEmoteId, context.getGuild()));
}
return parameter;
}
@Override
public Integer getPriority() {
return 51;
}
}

View File

@@ -0,0 +1,51 @@
package dev.sheldan.abstracto.statistic.emotes.converter;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.statistic.emotes.model.EmoteStatsModel;
import dev.sheldan.abstracto.statistic.emotes.model.EmoteStatsResult;
import dev.sheldan.abstracto.statistic.emotes.model.EmoteStatsResultDisplay;
import dev.sheldan.abstracto.statistic.emotes.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emotes.service.management.TrackedEmoteManagementService;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.Guild;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class EmoteStatsConverter {
@Autowired
private BotService botService;
@Autowired
private TrackedEmoteManagementService trackedEmoteManagementService;
public EmoteStatsModel fromEmoteStatsResults(List<EmoteStatsResult> resultList) {
if(resultList.isEmpty()) {
return EmoteStatsModel.builder().build();
}
Guild relevantGuild = botService.getGuildById(resultList.get(0).getServerId());
EmoteStatsModel resultingModel = EmoteStatsModel.builder().build();
resultList.forEach(emoteStatsResult -> {
TrackedEmote trackedEmote = trackedEmoteManagementService.loadByEmoteId(emoteStatsResult.getEmoteId(), emoteStatsResult.getServerId());
Emote loadedEmote = null;
if(!trackedEmote.getExternal() && !trackedEmote.getDeleted()) {
loadedEmote = relevantGuild.getEmoteById(trackedEmote.getTrackedEmoteId().getEmoteId());
}
EmoteStatsResultDisplay display = EmoteStatsResultDisplay
.builder()
.emote(loadedEmote)
.result(emoteStatsResult)
.trackedEmote(trackedEmote)
.build();
if(display.getTrackedEmote().getAnimated()) {
resultingModel.getAnimatedEmotes().add(display);
} else {
resultingModel.getStaticEmotes().add(display);
}
});
return resultingModel;
}
}

View File

@@ -0,0 +1,60 @@
package dev.sheldan.abstracto.statistic.emotes.job;
import dev.sheldan.abstracto.statistic.emotes.model.PersistingEmote;
import dev.sheldan.abstracto.statistic.emotes.service.TrackedEmoteRuntimeService;
import dev.sheldan.abstracto.statistic.emotes.service.TrackedEmoteService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
@DisallowConcurrentExecution
@Component
@PersistJobDataAfterExecution
public class EmotePersistingJob extends QuartzJobBean {
@Autowired
private TrackedEmoteRuntimeService trackedEmoteRuntimeService;
@Autowired
private TrackedEmoteService trackedEmoteService;
@Override
@Transactional
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Map<Long, Map<Long, List<PersistingEmote>>> runtimeConfig = trackedEmoteRuntimeService.getRuntimeConfig();
log.info("Running statistic persisting job.");
Long pastMinute = getPastMinute();
if(runtimeConfig.containsKey(pastMinute)) {
Map<Long, List<PersistingEmote>> foundStatistics = runtimeConfig.get(pastMinute);
log.info("Found emote statistics from {} servers to persist.", foundStatistics.size());
trackedEmoteService.storeEmoteStatistics(foundStatistics);
runtimeConfig.remove(pastMinute);
checkForPastEmoteStats(pastMinute, runtimeConfig);
}
}
private void checkForPastEmoteStats(Long minuteToCheck, Map<Long, Map<Long, List<PersistingEmote>>> runtimeConfig) {
List<Long> missedMinutes = runtimeConfig.keySet().stream().filter(aLong -> aLong < minuteToCheck).collect(Collectors.toList());
missedMinutes.forEach(pastMinute -> {
log.info("Persisting emotes for a minute in the past, it should have been previously, but was not. Minute {}.", pastMinute);
trackedEmoteService.storeEmoteStatistics(runtimeConfig.get(pastMinute));
runtimeConfig.remove(pastMinute);
});
}
public long getPastMinute() {
return (Instant.now().getEpochSecond() / 60) - 1;
}
}

View File

@@ -0,0 +1,36 @@
package dev.sheldan.abstracto.statistic.emotes.listener;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.listener.EmoteCreatedListener;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.emotes.service.management.TrackedEmoteManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Emote;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class CreateTrackedEmoteListener implements EmoteCreatedListener {
@Autowired
private TrackedEmoteManagementService trackedEmoteManagementService;
@Override
public void emoteCreated(Emote createdEmote) {
// guild should be available, because we are in the emote created event, and the emote object should come from there
log.info("Creating tracked emote {} in server {}.", createdEmote.getGuild().getIdLong(), createdEmote.getIdLong());
trackedEmoteManagementService.createTrackedEmote(createdEmote, createdEmote.getGuild());
}
@Override
public FeatureEnum getFeature() {
return StatisticFeatures.EMOTE_TRACKING;
}
@Override
public Integer getPriority() {
return ListenerPriority.MEDIUM;
}
}

View File

@@ -0,0 +1,35 @@
package dev.sheldan.abstracto.statistic.emotes.listener;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.listener.EmoteDeletedListener;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.emotes.service.management.TrackedEmoteManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Emote;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class DeleteTrackedEmoteListener implements EmoteDeletedListener {
@Autowired
private TrackedEmoteManagementService trackedEmoteManagementService;
@Override
public void emoteDeleted(Emote deletedEmote) {
log.info("Marking tracked emote {} in gild {} as deleted.", deletedEmote.getId(), deletedEmote.getGuild().getIdLong());
trackedEmoteManagementService.markAsDeleted(deletedEmote.getGuild().getIdLong(), deletedEmote.getIdLong());
}
@Override
public FeatureEnum getFeature() {
return StatisticFeatures.EMOTE_TRACKING;
}
@Override
public Integer getPriority() {
return ListenerPriority.MEDIUM;
}
}

View File

@@ -0,0 +1,43 @@
package dev.sheldan.abstracto.statistic.emotes.listener;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.listener.MessageReceivedListener;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.emotes.service.TrackedEmoteService;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.ISnowflake;
import net.dv8tion.jda.api.entities.Message;
import org.apache.commons.collections4.Bag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Component
public class EmoteTrackingListener implements MessageReceivedListener {
@Autowired
private TrackedEmoteService trackedEmoteService;
@Override
public void execute(Message message) {
Bag<Emote> emotesBag = message.getEmotesBag();
Map<Long, List<Emote>> collect = emotesBag.stream().collect(Collectors.groupingBy(ISnowflake::getIdLong));
collect.values().forEach(groupedEmotes ->
trackedEmoteService.addEmoteToRuntimeStorage(groupedEmotes.get(0), message.getGuild(), (long) groupedEmotes.size())
);
}
@Override
public FeatureEnum getFeature() {
return StatisticFeatures.EMOTE_TRACKING;
}
@Override
public Integer getPriority() {
return ListenerPriority.LOW;
}
}

View File

@@ -0,0 +1,34 @@
package dev.sheldan.abstracto.statistic.emotes.listener;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.listener.EmoteUpdatedListener;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.emotes.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emotes.service.management.TrackedEmoteManagementService;
import net.dv8tion.jda.api.entities.Emote;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class UpdateTrackedEmoteListener implements EmoteUpdatedListener {
@Autowired
private TrackedEmoteManagementService trackedEmoteManagementService;
@Override
public void emoteUpdated(Emote updatedEmote, String oldValue, String newValue) {
TrackedEmote trackedEmote = trackedEmoteManagementService.loadByEmote(updatedEmote);
trackedEmoteManagementService.changeName(trackedEmote, newValue);
}
@Override
public FeatureEnum getFeature() {
return StatisticFeatures.EMOTE_TRACKING;
}
@Override
public Integer getPriority() {
return ListenerPriority.MEDIUM;
}
}

View File

@@ -0,0 +1,22 @@
package dev.sheldan.abstracto.statistic.emotes.model;
import dev.sheldan.abstracto.statistic.emotes.model.database.UsedEmote;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import java.time.Instant;
import java.util.List;
@Getter
@Setter
@Builder
public class DownloadEmoteStatsModel {
private Guild guild;
private Instant downloadDate;
private Instant statsSince;
private Member requester;
private List<UsedEmote> emotes;
}

View File

@@ -0,0 +1,22 @@
package dev.sheldan.abstracto.statistic.emotes.repository;
import dev.sheldan.abstracto.statistic.emotes.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emotes.model.database.embed.TrackedEmoteServer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.QueryHints;
import org.springframework.stereotype.Repository;
import javax.persistence.QueryHint;
import java.util.List;
@Repository
public interface TrackedEmoteRepository extends JpaRepository<TrackedEmote, TrackedEmoteServer> {
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
List<TrackedEmote> findByTrackedEmoteId_ServerIdAndDeletedFalseAndExternalFalse(Long serverId);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
List<TrackedEmote> findByTrackedEmoteId_ServerIdAndTrackingEnabledTrue(Long serverId);
@QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
List<TrackedEmote> findByTrackedEmoteId_ServerId(Long serverId);
}

View File

@@ -0,0 +1,58 @@
package dev.sheldan.abstracto.statistic.emotes.repository;
import dev.sheldan.abstracto.statistic.emotes.model.EmoteStatsResult;
import dev.sheldan.abstracto.statistic.emotes.model.database.UsedEmote;
import dev.sheldan.abstracto.statistic.emotes.model.database.embed.UsedEmoteDay;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
@Repository
public interface UsedEmoteRepository extends JpaRepository<UsedEmote, UsedEmoteDay> {
@Query(value="select * from used_emote " +
"where emote_id = :emote_id and server_id = :server_id " +
"and use_date = date_trunc('day', now())", nativeQuery = true)
Optional<UsedEmote> findEmoteFromServerToday(@Param("emote_id") Long emoteId, @Param("server_id") Long server_id);
@Query(value = "select us.emote_id as emoteId, us.server_id as serverId, sum(us.amount) as amount from used_emote us " +
"inner join tracked_emote te " +
"on us.emote_id = te.id and us.server_id = te.server_id " +
"where us.use_date >= date_trunc('day', cast(:start_date AS timestamp)) and us.server_id = :server_id " +
"group by us.emote_id, us.server_id " +
"order by amount desc", nativeQuery = true)
List<EmoteStatsResult> getEmoteStatsForServerSince(@Param("server_id") Long serverId, @Param("start_date") Instant since);
@Query(value = "select us.emote_id as emoteId, us.server_id as serverId, sum(us.amount) as amount from used_emote us " +
"inner join tracked_emote te " +
"on us.emote_id = te.id and us.server_id = te.server_id " +
"where us.use_date >= date_trunc('day', cast(:start_date AS timestamp)) and us.server_id = :server_id and te.external = true " +
"group by us.emote_id, us.server_id " +
"order by amount desc", nativeQuery = true)
List<EmoteStatsResult> getExternalEmoteStatsForServerSince(@Param("server_id") Long serverId, @Param("start_date") Instant since);
@Query(value = "select us.emote_id as emoteId, us.server_id as serverId, sum(us.amount) as amount from used_emote us " +
"inner join tracked_emote te " +
"on us.emote_id = te.id and us.server_id = te.server_id " +
"where us.use_date >= date_trunc('day', cast(:start_date AS timestamp)) and us.server_id = :server_id and te.deleted = true " +
"group by us.emote_id, us.server_id " +
"order by amount desc", nativeQuery = true)
List<EmoteStatsResult> getDeletedEmoteStatsForServerSince(@Param("server_id") Long serverId, @Param("start_date") Instant since);
@Query(value = "select us.emote_id as emoteId, us.server_id as serverId, sum(us.amount) as amount from used_emote us " +
"inner join tracked_emote te " +
"on us.emote_id = te.id and us.server_id = te.server_id " +
"where us.use_date >= date_trunc('day', cast(:start_date AS timestamp)) and us.server_id = :server_id and te.external = false and te.deleted = false " +
"group by us.emote_id, us.server_id " +
"order by amount desc", nativeQuery = true)
List<EmoteStatsResult> getCurrentlyExistingEmoteStatsForServerSince(@Param("server_id") Long serverId, @Param("start_date") Instant since);
void deleteByEmoteId_EmoteIdAndEmoteId_ServerIdAndEmoteId_UseDateGreaterThan(Long emoteId, Long serverId, Instant timestamp);
List<UsedEmote> getByEmoteId_ServerIdAndEmoteId_UseDateGreaterThan(Long emoteId, Instant timestamp);
}

View File

@@ -0,0 +1,29 @@
package dev.sheldan.abstracto.statistic.emotes.service;
import dev.sheldan.abstracto.statistic.emotes.model.PersistingEmote;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class TrackedEmoteRunTimeStorage {
private HashMap<Long, Map<Long, List<PersistingEmote>>> trackedEmotes = new HashMap<>();
public Map<Long, Map<Long, List<PersistingEmote>>> getRuntimeConfig() {
return trackedEmotes;
}
public boolean contains(Long key) {
return trackedEmotes.containsKey(key);
}
public void put(Long key, Map<Long, List<PersistingEmote>> objectToPut) {
trackedEmotes.put(key, objectToPut);
}
public Map<Long, List<PersistingEmote>> get(Long key) {
return trackedEmotes.get(key);
}
}

View File

@@ -0,0 +1,82 @@
package dev.sheldan.abstracto.statistic.emotes.service;
import dev.sheldan.abstracto.statistic.emotes.model.PersistingEmote;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.Guild;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.*;
@Component
@Slf4j
public class TrackedEmoteRuntimeServiceBean implements TrackedEmoteRuntimeService {
@Autowired
private TrackedEmoteRunTimeStorage trackedEmoteRunTimeStorage;
@Override
public Map<Long, Map<Long, List<PersistingEmote>>> getRuntimeConfig() {
return trackedEmoteRunTimeStorage.getRuntimeConfig();
}
@Override
public void addEmoteForServer(Emote emote, Guild guild, boolean external) {
addEmoteForServer(emote, guild, 1L, external);
}
@Override
public void addEmoteForServer(Emote emote, Guild guild, Long count, boolean external) {
Long key = getKey();
PersistingEmote newPersistentEmote = createFromEmote(guild, emote, count, external);
if(trackedEmoteRunTimeStorage.contains(key)) {
Map<Long, List<PersistingEmote>> elementsForKey = trackedEmoteRunTimeStorage.get(key);
if(elementsForKey.containsKey(guild.getIdLong())) {
List<PersistingEmote> persistingEmotes = elementsForKey.get(guild.getIdLong());
Optional<PersistingEmote> existingEmote = persistingEmotes
.stream()
.filter(persistingEmote -> persistingEmote.getEmoteId().equals(emote.getIdLong()))
.findFirst();
existingEmote.ifPresent(persistingEmote -> persistingEmote.setCount(persistingEmote.getCount() + count));
if(!existingEmote.isPresent()) {
persistingEmotes.add(newPersistentEmote);
}
} else {
log.trace("Adding emote {} to list of server {}.", newPersistentEmote.getEmoteId(), guild.getIdLong());
elementsForKey.put(guild.getIdLong(), new ArrayList<>(Arrays.asList(newPersistentEmote)));
}
} else {
HashMap<Long, List<PersistingEmote>> serverEmotes = new HashMap<>();
serverEmotes.put(guild.getIdLong(), new ArrayList<>(Arrays.asList(newPersistentEmote)));
log.trace("Adding emote map entry for server {}.", guild.getIdLong());
trackedEmoteRunTimeStorage.put(key, serverEmotes);
}
}
@Override
public Long getKey() {
return Instant.now().getEpochSecond() / 60;
}
@Override
public PersistingEmote createFromEmote(Guild guild, Emote emote, boolean external) {
return createFromEmote(guild, emote, 1L, external);
}
@Override
public PersistingEmote createFromEmote(Guild guild, Emote emote, Long count, boolean external) {
String url = external ? emote.getImageUrl() : null;
return PersistingEmote
.builder()
.animated(emote.isAnimated())
.emoteId(emote.getIdLong())
.external(external)
.externalUrl(url)
.emoteName(emote.getName())
.count(count)
.serverId(guild.getIdLong())
.build();
}
}

View File

@@ -0,0 +1,197 @@
package dev.sheldan.abstracto.statistic.emotes.service;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.FeatureModeService;
import dev.sheldan.abstracto.statistic.config.StatisticFeatures;
import dev.sheldan.abstracto.statistic.emotes.config.EmoteTrackingMode;
import dev.sheldan.abstracto.statistic.emotes.model.PersistingEmote;
import dev.sheldan.abstracto.statistic.emotes.model.TrackedEmoteOverview;
import dev.sheldan.abstracto.statistic.emotes.model.TrackedEmoteSynchronizationResult;
import dev.sheldan.abstracto.statistic.emotes.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emotes.model.database.UsedEmote;
import dev.sheldan.abstracto.statistic.emotes.model.database.embed.TrackedEmoteServer;
import dev.sheldan.abstracto.statistic.emotes.service.management.TrackedEmoteManagementService;
import dev.sheldan.abstracto.statistic.emotes.service.management.UsedEmoteManagementService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.Guild;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.util.Map;
import java.util.List;
import java.util.Optional;
@Component
@Slf4j
public class TrackedEmoteServiceBean implements TrackedEmoteService {
@Autowired
private TrackedEmoteRuntimeService trackedEmoteRuntimeService;
@Autowired
private FeatureModeService featureModeService;
@Autowired
private EmoteService emoteService;
@Autowired
private TrackedEmoteManagementService trackedEmoteManagementService;
@Autowired
private UsedEmoteManagementService usedEmoteManagementService;
@Autowired
private BotService botService;
@Override
public void addEmoteToRuntimeStorage(List<Emote> emotes, Guild guild) {
boolean externalTrackingEnabled = featureModeService.featureModeActive(StatisticFeatures.EMOTE_TRACKING, guild.getIdLong(), EmoteTrackingMode.EXTERNAL_EMOTES);
emotes.forEach(emote -> {
boolean emoteIsFromGuild = emoteService.emoteIsFromGuild(emote, guild);
if(externalTrackingEnabled || emoteIsFromGuild) {
trackedEmoteRuntimeService.addEmoteForServer(emote, guild, !emoteIsFromGuild);
}
});
}
@Override
public void addEmoteToRuntimeStorage(Emote emote, Guild guild, Long count) {
boolean externalTrackingEnabled = featureModeService.featureModeActive(StatisticFeatures.EMOTE_TRACKING, guild.getIdLong(), EmoteTrackingMode.EXTERNAL_EMOTES);
boolean emoteIsFromGuild = emoteService.emoteIsFromGuild(emote, guild);
if(externalTrackingEnabled || emoteIsFromGuild) {
trackedEmoteRuntimeService.addEmoteForServer(emote, guild, count, !emoteIsFromGuild);
}
}
@Override
@Transactional
public void storeEmoteStatistics(Map<Long, List<PersistingEmote>> usagesToStore) {
usagesToStore.forEach((serverId, persistingEmotes) -> {
log.info("Storing {} emotes for server {}.", persistingEmotes.size(), serverId);
boolean autoTrackExternalEmotes = featureModeService.featureModeActive(StatisticFeatures.EMOTE_TRACKING, serverId, EmoteTrackingMode.AUTO_TRACK_EXTERNAL);
boolean trackExternalEmotes = featureModeService.featureModeActive(StatisticFeatures.EMOTE_TRACKING, serverId, EmoteTrackingMode.EXTERNAL_EMOTES);
persistingEmotes.forEach(persistingEmote -> {
Optional<TrackedEmote> emoteOptional = trackedEmoteManagementService.loadByEmoteIdOptional(persistingEmote.getEmoteId(), serverId);
emoteOptional.ifPresent(trackedEmote -> {
if(trackedEmote.getTrackingEnabled()) {
Optional<UsedEmote> existingUsedEmote = usedEmoteManagementService.loadUsedEmoteForTrackedEmoteToday(trackedEmote);
existingUsedEmote.ifPresent(usedEmote ->
usedEmote.setAmount(usedEmote.getAmount() + persistingEmote.getCount())
);
if(!existingUsedEmote.isPresent()) {
usedEmoteManagementService.createEmoteUsageForToday(trackedEmote, persistingEmote.getCount());
}
} else {
log.trace("Tracking disabled for emote {} in server {}.", trackedEmote.getTrackedEmoteId().getEmoteId(), trackedEmote.getTrackedEmoteId().getServerId());
}
});
if(!emoteOptional.isPresent()) {
createNewTrackedEmote(serverId, autoTrackExternalEmotes, trackExternalEmotes, persistingEmote);
}
});
});
}
private void createNewTrackedEmote(Long serverId, boolean autoTrackExternalEmotes, boolean trackExternalEmotes, PersistingEmote persistingEmote) {
Optional<Guild> guildOptional = botService.getGuildByIdOptional(serverId);
guildOptional.ifPresent(guild -> {
Emote emoteFromGuild = guild.getEmoteById(persistingEmote.getEmoteId());
if(emoteFromGuild != null) {
TrackedEmote newCreatedTrackedEmote = trackedEmoteManagementService.createTrackedEmote(emoteFromGuild, guild);
usedEmoteManagementService.createEmoteUsageForToday(newCreatedTrackedEmote, persistingEmote.getCount());
} else if(autoTrackExternalEmotes && trackExternalEmotes){
TrackedEmote newCreatedTrackedEmote = trackedEmoteManagementService.createExternalEmote(persistingEmote);
usedEmoteManagementService.createEmoteUsageForToday(newCreatedTrackedEmote, persistingEmote.getCount());
}
});
}
@Override
public TrackedEmote getFakeTrackedEmote(Emote emote, Guild guild) {
return getFakeTrackedEmote(emote.getIdLong(), guild);
}
@Override
public TrackedEmote getFakeTrackedEmote(Long id, Guild guild) {
return TrackedEmote
.builder()
.trackedEmoteId(new TrackedEmoteServer(id, guild.getIdLong()))
.fake(true)
.build();
}
@Override
public TrackedEmoteSynchronizationResult synchronizeTrackedEmotes(Guild guild) {
List<TrackedEmote> activeTrackedEmotes = trackedEmoteManagementService.getAllActiveTrackedEmoteForServer(guild.getIdLong());
Long addedEmotes = 0L;
List<Emote> allExistingEmotes = guild.getEmotes();
log.info("Synchronizing emotes for server {}, currently tracked emotes {}, available emotes for server {}.", guild.getIdLong(), activeTrackedEmotes.size(), allExistingEmotes.size());
for (Emote emote : allExistingEmotes) {
Optional<TrackedEmote> trackedEmoteOptional = activeTrackedEmotes
.stream()
.filter(trackedEmote ->
trackedEmote.getTrackedEmoteId().getEmoteId().equals(emote.getIdLong())
&& trackedEmote.getTrackedEmoteId().getServerId().equals(guild.getIdLong()))
.findFirst();
if (!trackedEmoteOptional.isPresent()) {
trackedEmoteManagementService.createTrackedEmote(emote, guild);
addedEmotes++;
} else {
activeTrackedEmotes.remove(trackedEmoteOptional.get());
}
}
activeTrackedEmotes.forEach(trackedEmote ->
trackedEmoteManagementService.markAsDeleted(trackedEmote)
);
return TrackedEmoteSynchronizationResult
.builder()
.emotesAdded(addedEmotes)
.emotesMarkedDeleted((long) activeTrackedEmotes.size())
.build();
}
@Override
public TrackedEmoteOverview loadTrackedEmoteOverview(Guild guild) {
return loadTrackedEmoteOverview(guild, false);
}
@Override
public TrackedEmoteOverview loadTrackedEmoteOverview(Guild guild, Boolean showTrackingDisabled) {
List<TrackedEmote> trackedEmotes = trackedEmoteManagementService.getTrackedEmoteForServer(guild.getIdLong(), showTrackingDisabled);
TrackedEmoteOverview emoteOverView = TrackedEmoteOverview.builder().build();
trackedEmotes.forEach(trackedEmote ->
emoteOverView.addTrackedEmote(trackedEmote, guild)
);
return emoteOverView;
}
@Override
public TrackedEmote createFakeTrackedEmote(Emote emote, Guild guild) {
boolean external = !emoteService.emoteIsFromGuild(emote, guild);
return trackedEmoteManagementService.createTrackedEmote(emote, guild, external);
}
@Override
public void deleteTrackedEmote(TrackedEmote trackedEmote) {
usedEmoteManagementService.purgeEmoteUsagesSince(trackedEmote, Instant.EPOCH);
trackedEmoteManagementService.deleteTrackedEmote(trackedEmote);
}
@Override
public void resetEmoteStats(Guild guild) {
List<TrackedEmote> trackedEmotes = trackedEmoteManagementService.getTrackedEmoteForServer(guild.getIdLong(), true);
trackedEmotes.forEach(this::deleteTrackedEmote);
}
@Override
public void disableEmoteTracking(Guild guild) {
List<TrackedEmote> trackedEmotes = trackedEmoteManagementService.getTrackedEmoteForServer(guild.getIdLong(), true);
trackedEmotes.forEach(trackedEmote -> trackedEmote.setTrackingEnabled(false));
}
}

View File

@@ -0,0 +1,61 @@
package dev.sheldan.abstracto.statistic.emotes.service;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.statistic.emotes.converter.EmoteStatsConverter;
import dev.sheldan.abstracto.statistic.emotes.model.EmoteStatsModel;
import dev.sheldan.abstracto.statistic.emotes.model.EmoteStatsResult;
import dev.sheldan.abstracto.statistic.emotes.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emotes.service.management.UsedEmoteManagementService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
@Component
@Slf4j
public class UsedEmoteServiceBean implements UsedEmoteService {
@Autowired
private EmoteStatsConverter converter;
@Autowired
private UsedEmoteManagementService usedEmoteManagementService;
@Override
public EmoteStatsModel getEmoteStatsForServerSince(AServer server, Instant since) {
List<EmoteStatsResult> emoteStatsResults = usedEmoteManagementService.loadAllEmoteStatsForServerSince(server, since);
return converter.fromEmoteStatsResults(emoteStatsResults);
}
@Override
public EmoteStatsModel getDeletedEmoteStatsForServerSince(AServer server, Instant since) {
List<EmoteStatsResult> emoteStatsResults = usedEmoteManagementService.loadDeletedEmoteStatsForServerSince(server, since);
return converter.fromEmoteStatsResults(emoteStatsResults);
}
@Override
public EmoteStatsModel getExternalEmoteStatsForServerSince(AServer server, Instant since) {
List<EmoteStatsResult> emoteStatsResults = usedEmoteManagementService.loadExternalEmoteStatsForServerSince(server, since);
return converter.fromEmoteStatsResults(emoteStatsResults);
}
@Override
public EmoteStatsModel getActiveEmoteStatsForServerSince(AServer server, Instant since) {
List<EmoteStatsResult> emoteStatsResults = usedEmoteManagementService.loadActiveEmoteStatsForServerSince(server, since);
return converter.fromEmoteStatsResults(emoteStatsResults);
}
@Override
public void purgeEmoteUsagesSince(TrackedEmote emote, Instant since) {
log.info("Purging emote {} in server {} since {}.", emote.getTrackedEmoteId().getEmoteId(), emote.getTrackedEmoteId().getServerId(), since);
usedEmoteManagementService.purgeEmoteUsagesSince(emote, since.truncatedTo(ChronoUnit.DAYS));
}
@Override
public void purgeEmoteUsages(TrackedEmote emote) {
purgeEmoteUsagesSince(emote, Instant.EPOCH);
}
}

View File

@@ -0,0 +1,179 @@
package dev.sheldan.abstracto.statistic.emotes.service.management;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.statistic.emotes.exception.TrackedEmoteNotFoundException;
import dev.sheldan.abstracto.statistic.emotes.model.PersistingEmote;
import dev.sheldan.abstracto.statistic.emotes.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emotes.model.database.embed.TrackedEmoteServer;
import dev.sheldan.abstracto.statistic.emotes.repository.TrackedEmoteRepository;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.Guild;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
@Component
@Slf4j
public class TrackedEmoteManagementServiceBean implements TrackedEmoteManagementService {
@Autowired
private TrackedEmoteRepository repository;
@Autowired
private ServerManagementService serverManagementService;
@Override
public TrackedEmote createTrackedEmote(Long emoteId, String emoteName, Boolean animated, AServer server) {
return createTrackedEmote(emoteId, emoteName, animated, true, server);
}
@Override
public TrackedEmote createTrackedEmote(Emote emote, Guild guild) {
AServer server = serverManagementService.loadServer(guild.getIdLong());
return createTrackedEmote(emote.getIdLong(), emote.getName(), emote.isAnimated(), true, server);
}
@Override
public TrackedEmote createTrackedEmote(Emote emote, Guild guild, boolean external) {
if(external) {
return createExternalEmote(emote, guild);
} else {
return createTrackedEmote(emote, guild);
}
}
@Override
public TrackedEmote createTrackedEmote(Long emoteId, String emoteName, Boolean animated, Boolean tracked, AServer server) {
TrackedEmote emote = TrackedEmote
.builder()
.animated(animated)
.trackedEmoteId(new TrackedEmoteServer(emoteId, server.getId()))
.trackingEnabled(tracked)
.emoteName(emoteName)
.server(server)
.external(false)
.deleted(false)
.build();
log.info("Creating tracking emote with id {} and server {}.", emoteId, server.getId());
return repository.save(emote);
}
@Override
public TrackedEmote createExternalEmote(Long emoteId, String emoteName, String externalUrl, Boolean animated, AServer server) {
TrackedEmote emote = TrackedEmote
.builder()
.animated(animated)
.trackedEmoteId(new TrackedEmoteServer(emoteId, server.getId()))
.trackingEnabled(true)
.deleted(false)
.emoteName(emoteName)
.server(server)
.external(true)
.externalUrl(externalUrl)
.build();
log.info("Creating external emote with id {} for server {}.", emoteId, server.getId());
return repository.save(emote);
}
@Override
public TrackedEmote createNotTrackedEmote(Long emoteId, String emoteName, Boolean animated, AServer server) {
return createTrackedEmote(emoteId, emoteName, animated, false, server);
}
@Override
public TrackedEmote createExternalEmote(PersistingEmote persistingEmote) {
AServer server = serverManagementService.loadServer(persistingEmote.getServerId());
return createExternalEmote(persistingEmote.getEmoteId(), persistingEmote.getEmoteName(), persistingEmote.getExternalUrl(), persistingEmote.getAnimated(), server);
}
@Override
public TrackedEmote createExternalEmote(Emote emote, Guild guild) {
AServer server = serverManagementService.loadServer(guild.getIdLong());
return createExternalEmote(emote.getIdLong(), emote.getName(), emote.getImageUrl(), emote.isAnimated(), server);
}
@Override
public void markAsDeleted(Long serverId, Long emoteId) {
TrackedEmote emote = loadByEmoteId(emoteId, serverId);
markAsDeleted(emote);
}
@Override
public void markAsDeleted(TrackedEmote trackedemote) {
log.info("Marking tracked emote {} in server {} as deleted.", trackedemote.getTrackedEmoteId().getEmoteId(), trackedemote.getTrackedEmoteId().getServerId());
trackedemote.setDeleted(true);
}
@Override
public TrackedEmote loadByEmoteId(Long emoteId, Long serverId) {
return loadByEmoteIdOptional(emoteId, serverId).orElseThrow(() -> new TrackedEmoteNotFoundException(String.format("Tracked emote %s in server %s not found.", emoteId, serverId)));
}
@Override
public TrackedEmote loadByEmote(Emote emote) {
return loadByEmoteId(emote.getIdLong(), emote.getGuild().getIdLong());
}
@Override
public boolean trackedEmoteExists(Long emoteId, Long serverId) {
return loadByEmoteIdOptional(emoteId, serverId).isPresent();
}
@Override
public TrackedEmote loadByTrackedEmoteServer(TrackedEmoteServer trackedEmoteServer) {
return loadByEmoteId(trackedEmoteServer.getEmoteId(), trackedEmoteServer.getServerId());
}
@Override
public Optional<TrackedEmote> loadByEmoteIdOptional(Long emoteId, Long serverId) {
return repository.findById(new TrackedEmoteServer(emoteId, serverId));
}
@Override
public List<TrackedEmote> getAllActiveTrackedEmoteForServer(AServer server) {
return getAllActiveTrackedEmoteForServer(server.getId());
}
@Override
public List<TrackedEmote> getAllActiveTrackedEmoteForServer(Long serverId) {
return repository.findByTrackedEmoteId_ServerIdAndDeletedFalseAndExternalFalse(serverId);
}
@Override
public List<TrackedEmote> getTrackedEmoteForServer(Long serverId, Boolean showTrackingDisabledEmotes) {
if(showTrackingDisabledEmotes) {
return repository.findByTrackedEmoteId_ServerId(serverId);
} else {
return repository.findByTrackedEmoteId_ServerIdAndTrackingEnabledTrue(serverId);
}
}
@Override
public void changeName(TrackedEmote emote, String newName) {
log.info("Changing name of emote {} in server {}.", emote.getTrackedEmoteId().getEmoteId(), emote.getTrackedEmoteId().getServerId());
emote.setEmoteName(newName);
}
@Override
public void disableTrackedEmote(TrackedEmote emote) {
log.info("Disabling tracking for tracked emote {} in server {}.", emote.getTrackedEmoteId().getEmoteId(), emote.getTrackedEmoteId().getServerId());
emote.setTrackingEnabled(false);
}
@Override
public void enableTrackedEmote(TrackedEmote emote) {
log.info("Enabling tracking for tracked emote {} in server {}.", emote.getTrackedEmoteId().getEmoteId(), emote.getTrackedEmoteId().getServerId());
emote.setTrackingEnabled(true);
}
@Override
public void deleteTrackedEmote(TrackedEmote emote) {
log.info("Deleting tracked emote {} in server {}.", emote.getTrackedEmoteId().getEmoteId(), emote.getTrackedEmoteId().getServerId());
repository.delete(emote);
}
}

View File

@@ -0,0 +1,69 @@
package dev.sheldan.abstracto.statistic.emotes.service.management;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.statistic.emotes.model.EmoteStatsResult;
import dev.sheldan.abstracto.statistic.emotes.model.database.TrackedEmote;
import dev.sheldan.abstracto.statistic.emotes.model.database.UsedEmote;
import dev.sheldan.abstracto.statistic.emotes.model.database.embed.UsedEmoteDay;
import dev.sheldan.abstracto.statistic.emotes.repository.UsedEmoteRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
@Component
@Slf4j
public class UsedEmoteManagementServiceBean implements UsedEmoteManagementService {
@Autowired
private UsedEmoteRepository usedEmoteRepository;
@Override
public Optional<UsedEmote> loadUsedEmoteForTrackedEmoteToday(TrackedEmote trackedEmote) {
return usedEmoteRepository.findEmoteFromServerToday(trackedEmote.getTrackedEmoteId().getEmoteId() , trackedEmote.getTrackedEmoteId().getServerId());
}
@Override
public UsedEmote createEmoteUsageForToday(TrackedEmote trackedEmote, Long count) {
UsedEmote usedEmote = UsedEmote
.builder()
.emoteId(new UsedEmoteDay(trackedEmote.getTrackedEmoteId().getEmoteId(), trackedEmote.getTrackedEmoteId().getServerId(), Instant.now()))
.amount(count)
.build();
log.trace("Creating emote usage for emote {} in server {} with count {}.", trackedEmote.getTrackedEmoteId().getEmoteId(), trackedEmote.getTrackedEmoteId().getServerId(), count);
return usedEmoteRepository.save(usedEmote);
}
@Override
public List<UsedEmote> loadEmoteUsagesForServerSince(AServer server, Instant since) {
return usedEmoteRepository.getByEmoteId_ServerIdAndEmoteId_UseDateGreaterThan(server.getId(), since);
}
@Override
public List<EmoteStatsResult> loadAllEmoteStatsForServerSince(AServer server, Instant since) {
return usedEmoteRepository.getEmoteStatsForServerSince(server.getId(), since);
}
@Override
public List<EmoteStatsResult> loadDeletedEmoteStatsForServerSince(AServer server, Instant since) {
return usedEmoteRepository.getDeletedEmoteStatsForServerSince(server.getId(), since);
}
@Override
public List<EmoteStatsResult> loadExternalEmoteStatsForServerSince(AServer server, Instant since) {
return usedEmoteRepository.getExternalEmoteStatsForServerSince(server.getId(), since);
}
@Override
public List<EmoteStatsResult> loadActiveEmoteStatsForServerSince(AServer server, Instant since) {
return usedEmoteRepository.getCurrentlyExistingEmoteStatsForServerSince(server.getId(), since);
}
@Override
public void purgeEmoteUsagesSince(TrackedEmote emote, Instant since) {
usedEmoteRepository.deleteByEmoteId_EmoteIdAndEmoteId_ServerIdAndEmoteId_UseDateGreaterThan(emote.getTrackedEmoteId().getEmoteId(), emote.getTrackedEmoteId().getServerId(), since);
}
}

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-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" >
<include file="statistic-tables/tables.xml" relativeToChangelogFile="true"/>
<include file="statistic-seedData/data.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,88 @@
<?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" >
<property name="statisticModule" value="(SELECT id FROM module WHERE name = 'statistic')"/>
<property name="emoteTrackingFeature" value="(SELECT id FROM feature WHERE key = 'emote_tracking')"/>
<property name="today" value="(SELECT NOW())"/>
<changeSet author="Sheldan" id="statistic-commands">
<insert tableName="command">
<column name="name" value="emoteStats"/>
<column name="module_id" valueComputed="${statisticModule}"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="deletedEmoteStats"/>
<column name="module_id" valueComputed="${statisticModule}"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="externalEmoteStats"/>
<column name="module_id" valueComputed="${statisticModule}"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="purgeEmoteStats"/>
<column name="module_id" valueComputed="${statisticModule}"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="syncTrackedEmotes"/>
<column name="module_id" valueComputed="${statisticModule}"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="trackEmote"/>
<column name="module_id" valueComputed="${statisticModule}"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="disableEmoteTracking"/>
<column name="module_id" valueComputed="${statisticModule}"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="deleteTrackedEmote"/>
<column name="module_id" valueComputed="${statisticModule}"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="resetEmoteStats"/>
<column name="module_id" valueComputed="${statisticModule}"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="exportEmoteStats"/>
<column name="module_id" valueComputed="${statisticModule}"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="showExternalTrackedEmote"/>
<column name="module_id" valueComputed="${statisticModule}"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="command">
<column name="name" value="showTrackedEmotes"/>
<column name="module_id" valueComputed="${statisticModule}"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}"/>
<column name="created" valueComputed="${today}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,15 @@
<?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" >
<include file="feature.xml" relativeToChangelogFile="true"/>
<include file="module.xml" relativeToChangelogFile="true"/>
<include file="default_feature_flag.xml" relativeToChangelogFile="true"/>
<include file="emote_statistic_job.xml" relativeToChangelogFile="true"/>
<include file="command.xml" relativeToChangelogFile="true"/>
<include file="default_feature_mode.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,18 @@
<?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" >
<property name="emoteTrackingFeature" value="(SELECT id FROM feature WHERE key = 'emote_tracking')"/>
<property name="today" value="(SELECT NOW())"/>
<changeSet author="Sheldan" id="emote_tracking_default_feature_flag-insertion">
<insert tableName="default_feature_flag">
<column name="enabled" value="false"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}" />
<column name="created" valueComputed="${today}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,31 @@
<?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" >
<property name="emoteTrackingFeature" value="(SELECT id FROM feature WHERE key = 'emote_tracking')"/>
<property name="today" value="(SELECT NOW())"/>
<changeSet author="Sheldan" id="emote_tracking_default_feature_mode-insertion">
<insert tableName="default_feature_mode">
<column name="enabled" value="true"/>
<column name="mode" value="emoteAutoTrack"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}" />
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="default_feature_mode">
<column name="enabled" value="false"/>
<column name="mode" value="externalEmotes"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}" />
<column name="created" valueComputed="${today}"/>
</insert>
<insert tableName="default_feature_mode">
<column name="enabled" value="false"/>
<column name="mode" value="autoTrackExternal"/>
<column name="feature_id" valueComputed="${emoteTrackingFeature}" />
<column name="created" valueComputed="${today}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,19 @@
<?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="emote-statistic-job-insert">
<insert tableName="scheduler_job">
<column name="name" value="emoteStatisticJob"/>
<column name="group_name" value="statistic"/>
<column name="clazz" value="dev.sheldan.abstracto.statistic.emotes.job.EmotePersistingJob"/>
<column name="active" value="true"/>
<column name="cron_expression" value="40 * * * * ?"/>
<column name="recovery" value="false"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,16 @@
<?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" >
<property name="today" value="(SELECT NOW())"/>
<changeSet author="Sheldan" id="emote_tracking_feature-insertion">
<insert tableName="feature">
<column name="key" value="emote_tracking"/>
<column name="created" valueComputed="${today}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,16 @@
<?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" >
<property name="today" value="(SELECT NOW())"/>
<changeSet author="Sheldan" id="statistic-module-insertion">
<insert tableName="module">
<column name="name" value="statistic"/>
<column name="created" valueComputed="${today}"/>
</insert>
</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-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" >
<include file="tracked_emote.xml" relativeToChangelogFile="true"/>
<include file="used_emote.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,30 @@
<?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="tracked_emote-table">
<createTable tableName="tracked_emote">
<column name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="tracked_emote_pkey"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="name" type="VARCHAR(32)"/>
<column name="animated" type="BOOLEAN"/>
<column name="tracking_enabled" type="BOOLEAN"/>
<column name="deleted" type="BOOLEAN"/>
<column name="external" type="BOOLEAN"/>
<column name="external_url" type="VARCHAR(255)"/>
<column name="server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<changeSet author="Sheldan" id="tracked_emote-fk_tracked_emote_server">
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="tracked_emote" constraintName="fk_tracked_emote_server" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,29 @@
<?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="used_emote-table">
<createTable tableName="used_emote">
<column name="emote_id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="used_emote_pkey"/>
</column>
<column name="server_id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="used_emote_pkey"/>
</column>
<column name="use_date" type="DATE">
<constraints nullable="false" primaryKey="true" primaryKeyName="used_emote_pkey"/>
</column>
<column name="amount" type="BIGINT"/>
</createTable>
</changeSet>
<changeSet author="Sheldan" id="used_emote-fk_used_emote_server">
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="used_emote" constraintName="fk_used_emote_server" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
</changeSet>
<changeSet author="Sheldan" id="used_emote-fk_used_emote_tracked_emote">
<addForeignKeyConstraint baseColumnNames="emote_id" baseTableName="used_emote" constraintName="fk_used_emote_tracked_emote" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="tracked_emote" validate="true"/>
</changeSet>
</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-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" >
<include file="1.0-statistic/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>