diff --git a/build.gradle b/build.gradle index 154b30c..677cb9f 100644 --- a/build.gradle +++ b/build.gradle @@ -9,15 +9,6 @@ buildscript { } } -repositories { - mavenCentral() - jcenter() - maven { - name = "Minecraft Libraries" - url = "https://libraries.minecraft.net" - } -} - apply plugin: 'com.github.johnrengelman.shadow' apply plugin: 'org.ajoberstar.grgit' apply plugin: 'idea' @@ -25,8 +16,6 @@ apply plugin: 'java' apply plugin: 'application' apply plugin: 'maven-publish' -group = 'sciwhiz12.janitor' -archivesBaseName = 'janitor_bot' version = getVersion() println("Version: ${version}") @@ -40,34 +29,117 @@ tasks.withType(JavaCompile) { } dependencies { - implementation group: 'net.dv8tion', name: 'JDA', version: jda_version - implementation group: 'com.electronwill.night-config', name: 'toml', version: nightconfig_version - implementation group: 'net.sf.jopt-simple', name: 'jopt-simple', version: jopt_version - implementation group: 'com.google.guava', name: 'guava', version: guava_version - implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jackson_version - implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: jackson_version - implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jackson_version - implementation group: 'org.apache.logging.log4j', name: 'log4j-to-slf4j', version: log4j_bridge_version - implementation group: 'ch.qos.logback', name: 'logback-classic', version: logback_version - implementation group: 'com.mojang', name: 'brigadier', version: brigadier_version - - testImplementation group: 'junit', name: 'junit', version: junit_version + implementation project(':core') + implementation project(path: ':core', configuration: 'api') + runtimeOnly project(':moderation') + runtimeOnly project(path: ':moderation', configuration: 'api') } application { mainClassName = 'sciwhiz12.janitor.BotStartup' } - -task sourcesJar(type: Jar, dependsOn: classes) { - from sourceSets.main.allSource - classifier "sources" +jar.finalizedBy 'shadowJar' +shadowJar { + exclude 'META-INF/NOTICE**' + exclude 'META-INF/LICENSE**' + exclude 'META-INF/DEPENDENCIES' + exclude 'META-INF/proguard/**' + exclude 'META-INF/maven/**' + classifier '' + includeEmptyDirs false +} + +allprojects { + apply plugin: 'java' + group = 'sciwhiz12.janitor' + version = rootProject.version + + repositories { + mavenCentral() + jcenter() + maven { + name = "Minecraft Libraries" + url = "https://libraries.minecraft.net" + } + } +} + +subprojects { + sourceSets { api } + + configurations { + apiImplementation.extendsFrom apiDependency + api { + canBeConsumed = true + canBeResolved = true + extendsFrom apiDependency + } + implementation.extendsFrom apiImplementation, api + 'default' { + extendsFrom api, apiDependency + } + } + + dependencies { + api sourceSets.api.output + testImplementation group: 'junit', name: 'junit', version: junit_version + } + + jar { + from sourceSets.api.output + finalizedBy 'apiJar', 'sourcesJar' + } + + task apiJar(type: Jar, dependsOn: apiClasses) { + from sourceSets.api.allJava + classifier "api" + } + + task sourcesJar(type: Jar, dependsOn: classes) { + from sourceSets.main.allSource + from sourceSets.api.allSource + classifier "sources" + } + + java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } +} + +project(':core') { + apply plugin: 'com.github.johnrengelman.shadow' + archivesBaseName = 'janitor-core' + + dependencies { + apiDependency group: 'net.dv8tion', name: 'JDA', version: jda_version + apiDependency group: 'com.electronwill.night-config', name: 'toml', version: nightconfig_version + apiDependency group: 'net.sf.jopt-simple', name: 'jopt-simple', version: jopt_version + apiDependency group: 'com.google.guava', name: 'guava', version: guava_version + apiDependency group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jackson_version + apiDependency group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: jackson_version + apiDependency group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jackson_version + apiDependency group: 'org.apache.logging.log4j', name: 'log4j-to-slf4j', version: log4j_bridge_version + apiDependency group: 'ch.qos.logback', name: 'logback-classic', version: logback_version + apiDependency group: 'com.mojang', name: 'brigadier', version: brigadier_version + } + + shadowJar.configurations = [project.configurations.api] + + jar.finalizedBy 'shadowJar' +} + +project(':moderation') { + archivesBaseName = 'janitor-moderation' + + dependencies { + apiDependency project(path: ':core', configuration: 'api') + } } -jar.finalizedBy('sourcesJar') -jar.finalizedBy('shadowJar') def getVersion() { try { - def raw_version = grgit.describe(longDescr: true, tags: true) + String raw_version = grgit.describe(longDescr: true, tags: true) def versionSep = raw_version.split "-" def startVer = versionSep[0].substring(1) return startVer + "." + versionSep[1] diff --git a/core/src/api/java/sciwhiz12/janitor/api/JanitorBot.java b/core/src/api/java/sciwhiz12/janitor/api/JanitorBot.java new file mode 100644 index 0000000..ce2c893 --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/JanitorBot.java @@ -0,0 +1,33 @@ +package sciwhiz12.janitor.api; + +import net.dv8tion.jda.api.JDA; +import sciwhiz12.janitor.api.command.CommandRegistry; +import sciwhiz12.janitor.api.config.BotConfig; +import sciwhiz12.janitor.api.config.ConfigManager; +import sciwhiz12.janitor.api.messages.Messages; +import sciwhiz12.janitor.api.messages.emote.ReactionManager; +import sciwhiz12.janitor.api.messages.substitution.SubstitutionsMap; +import sciwhiz12.janitor.api.module.ModuleManager; +import sciwhiz12.janitor.api.storage.GuildStorageManager; + +public interface JanitorBot { + BotConfig getBotConfig(); + + CommandRegistry getCommands(); + + GuildStorageManager getGuildStorage(); + + ConfigManager getConfigs(); + + SubstitutionsMap getSubstitutions(); + + ReactionManager getReactions(); + + Messages getMessages(); + + ModuleManager getModuleManager(); + + void shutdown(); + + JDA getDiscord(); +} diff --git a/src/main/java/sciwhiz12/janitor/Logging.java b/core/src/api/java/sciwhiz12/janitor/api/Logging.java similarity index 75% rename from src/main/java/sciwhiz12/janitor/Logging.java rename to core/src/api/java/sciwhiz12/janitor/api/Logging.java index 90a570e..f021ccf 100644 --- a/src/main/java/sciwhiz12/janitor/Logging.java +++ b/core/src/api/java/sciwhiz12/janitor/api/Logging.java @@ -1,4 +1,4 @@ -package sciwhiz12.janitor; +package sciwhiz12.janitor.api; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -8,11 +8,10 @@ import org.slf4j.MarkerFactory; public class Logging { public static final Marker STATUS = MarkerFactory.getMarker("STATUS"); public static final Marker COMMANDS = MarkerFactory.getMarker("COMMANDS"); - public static final Marker TRANSLATIONS = MarkerFactory.getMarker("TRANSLATIONS"); public static final Marker MESSAGES = MarkerFactory.getMarker("MESSAGES"); public static final Marker STORAGE = MarkerFactory.getMarker("STORAGE"); + public static final Marker MODULE = MarkerFactory.getMarker("MODULE"); public static final Logger JANITOR = LoggerFactory.getLogger("janitor"); - public static final Logger CONSOLE = LoggerFactory.getLogger("janitor.console"); public static final Logger CONFIG = LoggerFactory.getLogger("janitor.config"); } diff --git a/core/src/api/java/sciwhiz12/janitor/api/command/BaseCommand.java b/core/src/api/java/sciwhiz12/janitor/api/command/BaseCommand.java new file mode 100644 index 0000000..a8dc994 --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/command/BaseCommand.java @@ -0,0 +1,23 @@ +package sciwhiz12.janitor.api.command; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import sciwhiz12.janitor.api.JanitorBot; + +public abstract class BaseCommand implements Command { + private final CommandRegistry registry; + + public BaseCommand(CommandRegistry registry) { + this.registry = registry; + } + + public CommandRegistry getRegistry() { + return registry; + } + + public JanitorBot getBot() { + return registry.getBot(); + } + + public abstract LiteralArgumentBuilder getNode(); +} diff --git a/core/src/api/java/sciwhiz12/janitor/api/command/Command.java b/core/src/api/java/sciwhiz12/janitor/api/command/Command.java new file mode 100644 index 0000000..a5d9ce2 --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/command/Command.java @@ -0,0 +1,30 @@ +package sciwhiz12.janitor.api.command; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import sciwhiz12.janitor.api.JanitorBot; +import sciwhiz12.janitor.api.config.GuildConfig; +import sciwhiz12.janitor.api.messages.Messages; + +public interface Command { + LiteralArgumentBuilder getNode(); + + JanitorBot getBot(); + + default Messages messages() { + return getBot().getMessages(); + } + + default GuildConfig config(MessageReceivedEvent event) { + return config(event.getGuild().getIdLong()); + } + + default GuildConfig config(Guild guild) { + return config(guild.getIdLong()); + } + + default GuildConfig config(long guildID) { + return getBot().getConfigs().get(guildID); + } +} diff --git a/core/src/api/java/sciwhiz12/janitor/api/command/CommandRegistry.java b/core/src/api/java/sciwhiz12/janitor/api/command/CommandRegistry.java new file mode 100644 index 0000000..d48e14a --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/command/CommandRegistry.java @@ -0,0 +1,15 @@ +package sciwhiz12.janitor.api.command; + +import com.mojang.brigadier.CommandDispatcher; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import sciwhiz12.janitor.api.JanitorBot; + +import java.util.function.Function; + +public interface CommandRegistry { + CommandDispatcher getDispatcher(); + + void addCommand(Function command); + + JanitorBot getBot(); +} diff --git a/src/main/java/sciwhiz12/janitor/commands/arguments/CustomStringArgumentType.java b/core/src/api/java/sciwhiz12/janitor/api/command/arguments/CustomStringArgumentType.java similarity index 85% rename from src/main/java/sciwhiz12/janitor/commands/arguments/CustomStringArgumentType.java rename to core/src/api/java/sciwhiz12/janitor/api/command/arguments/CustomStringArgumentType.java index 8a18e7e..dccd74d 100644 --- a/src/main/java/sciwhiz12/janitor/commands/arguments/CustomStringArgumentType.java +++ b/core/src/api/java/sciwhiz12/janitor/api/command/arguments/CustomStringArgumentType.java @@ -1,11 +1,11 @@ -package sciwhiz12.janitor.commands.arguments; +package sciwhiz12.janitor.api.command.arguments; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import sciwhiz12.janitor.utils.StringReaderUtil; +import sciwhiz12.janitor.api.utils.StringReaderUtil; import java.util.Collection; @@ -17,18 +17,15 @@ public class CustomStringArgumentType implements ArgumentType { } public static CustomStringArgumentType word() { - return new CustomStringArgumentType( - StringArgumentType.StringType.SINGLE_WORD); + return new CustomStringArgumentType(StringArgumentType.StringType.SINGLE_WORD); } public static CustomStringArgumentType string() { - return new CustomStringArgumentType( - StringArgumentType.StringType.QUOTABLE_PHRASE); + return new CustomStringArgumentType(StringArgumentType.StringType.QUOTABLE_PHRASE); } public static CustomStringArgumentType greedyString() { - return new CustomStringArgumentType( - StringArgumentType.StringType.GREEDY_PHRASE); + return new CustomStringArgumentType(StringArgumentType.StringType.GREEDY_PHRASE); } public static String getString(final CommandContext context, final String name) { diff --git a/src/main/java/sciwhiz12/janitor/commands/arguments/GuildMemberArgument.java b/core/src/api/java/sciwhiz12/janitor/api/command/arguments/GuildMemberArgument.java similarity index 96% rename from src/main/java/sciwhiz12/janitor/commands/arguments/GuildMemberArgument.java rename to core/src/api/java/sciwhiz12/janitor/api/command/arguments/GuildMemberArgument.java index 63f3c31..0cc26a3 100644 --- a/src/main/java/sciwhiz12/janitor/commands/arguments/GuildMemberArgument.java +++ b/core/src/api/java/sciwhiz12/janitor/api/command/arguments/GuildMemberArgument.java @@ -1,4 +1,4 @@ -package sciwhiz12.janitor.commands.arguments; +package sciwhiz12.janitor.api.command.arguments; import com.google.common.collect.ImmutableList; import com.mojang.brigadier.LiteralMessage; @@ -9,7 +9,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; -import sciwhiz12.janitor.utils.StringReaderUtil; +import sciwhiz12.janitor.api.utils.StringReaderUtil; import java.util.Collection; import java.util.Collections; @@ -71,6 +71,11 @@ public class GuildMemberArgument implements ArgumentType getExamples() { return ImmutableList.of("<@!607058472709652501>", "<@750291676764962816>"); diff --git a/core/src/api/java/sciwhiz12/janitor/api/config/BotConfig.java b/core/src/api/java/sciwhiz12/janitor/api/config/BotConfig.java new file mode 100644 index 0000000..0e6fddc --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/config/BotConfig.java @@ -0,0 +1,18 @@ +package sciwhiz12.janitor.api.config; + +import java.nio.file.Path; +import java.util.Optional; +import javax.annotation.Nullable; + +public interface BotConfig { + @Nullable + Path getMessagesFolder(); + + Path getConfigsFolder(); + + String getToken(); + + String getCommandPrefix(); + + Optional getOwnerID(); +} diff --git a/core/src/api/java/sciwhiz12/janitor/api/config/ConfigManager.java b/core/src/api/java/sciwhiz12/janitor/api/config/ConfigManager.java new file mode 100644 index 0000000..2729752 --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/config/ConfigManager.java @@ -0,0 +1,21 @@ +package sciwhiz12.janitor.api.config; + +import sciwhiz12.janitor.api.JanitorBot; + +public interface ConfigManager { + GuildConfig get(long guildID); + + void save(); + + void close(); + + void registerNode(ConfigNode node); + + default void registerNodes(ConfigNode... nodes) { + for (ConfigNode node : nodes) { + registerNode(node); + } + } + + JanitorBot getBot(); +} diff --git a/core/src/api/java/sciwhiz12/janitor/api/config/ConfigNode.java b/core/src/api/java/sciwhiz12/janitor/api/config/ConfigNode.java new file mode 100644 index 0000000..10051cd --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/config/ConfigNode.java @@ -0,0 +1,50 @@ +package sciwhiz12.janitor.api.config; + +import com.google.common.base.Joiner; + +import java.util.Objects; +import java.util.function.Supplier; + +public class ConfigNode { + private static final Joiner NEWLINE = Joiner.on('\n'); + + private final String path; + private final String comment; + private final Supplier defaultValue; + + public ConfigNode(String path, Supplier defaultValue, String... comment) { + Objects.requireNonNull(path, "Config node path must not be null"); + Objects.requireNonNull(defaultValue, "Default value supplier must not be null"); + Objects.requireNonNull(comment, "Config node comments must not be null"); + this.path = path; + this.defaultValue = defaultValue; + this.comment = NEWLINE.join(comment); + } + + public String path() { + return path; + } + + public String comment() { + return comment; + } + + public Supplier defaultValue() { + return defaultValue; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ConfigNode that = (ConfigNode) o; + return path.equals(that.path) && + comment.equals(that.comment) && + defaultValue.equals(that.defaultValue); + } + + @Override + public int hashCode() { + return Objects.hash(path, comment, defaultValue); + } +} diff --git a/core/src/api/java/sciwhiz12/janitor/api/config/CoreConfigs.java b/core/src/api/java/sciwhiz12/janitor/api/config/CoreConfigs.java new file mode 100644 index 0000000..cc15c8c --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/config/CoreConfigs.java @@ -0,0 +1,14 @@ +package sciwhiz12.janitor.api.config; + +public final class CoreConfigs { + public static final ConfigNode ENABLE = new ConfigNode<>( + "enable", () -> true, + "Whether the bot is enabled for this guild.", + "Can be used to temporarily disable the bot in emergency situations."); + + public static final ConfigNode COMMAND_PREFIX = new ConfigNode<>( + "commands.prefix", () -> "!", + "The prefix for all commands."); + + private CoreConfigs() {} +} diff --git a/core/src/api/java/sciwhiz12/janitor/api/config/GuildConfig.java b/core/src/api/java/sciwhiz12/janitor/api/config/GuildConfig.java new file mode 100644 index 0000000..8772068 --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/config/GuildConfig.java @@ -0,0 +1,26 @@ +package sciwhiz12.janitor.api.config; + +import com.electronwill.nightconfig.core.CommentedConfig; +import net.dv8tion.jda.api.entities.GuildChannel; + +public interface GuildConfig { + CommentedConfig getChannelOverrides(); + + CommentedConfig getChannelConfig(GuildChannel channel); + + T forGuild(ConfigNode node); + + void forGuild(ConfigNode node, T newValue); + + T forChannel(GuildChannel channel, ConfigNode node); + + void forChannel(GuildChannel channel, ConfigNode node, T newValue); + + long getGuildID(); + + CommentedConfig getRawConfig(); + + void save(); + + void close(); +} diff --git a/src/main/java/sciwhiz12/janitor/msg/json/ListingMessage.java b/core/src/api/java/sciwhiz12/janitor/api/messages/ListingMessage.java similarity index 57% rename from src/main/java/sciwhiz12/janitor/msg/json/ListingMessage.java rename to core/src/api/java/sciwhiz12/janitor/api/messages/ListingMessage.java index a7b6ff7..a343c95 100644 --- a/src/main/java/sciwhiz12/janitor/msg/json/ListingMessage.java +++ b/core/src/api/java/sciwhiz12/janitor/api/messages/ListingMessage.java @@ -1,22 +1,19 @@ -package sciwhiz12.janitor.msg.json; +package sciwhiz12.janitor.api.messages; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.google.common.primitives.Ints; -import joptsimple.internal.Strings; -import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.entities.Role; -import sciwhiz12.janitor.msg.substitution.CustomSubstitutions; -import sciwhiz12.janitor.msg.substitution.ISubstitutor; +import sciwhiz12.janitor.api.JanitorBot; +import sciwhiz12.janitor.api.messages.substitution.ModifiableSubstitutions; +import sciwhiz12.janitor.api.messages.substitution.ModifiableSubstitutor; +import sciwhiz12.janitor.api.messages.substitution.SubstitutionsMap; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; import java.util.List; import java.util.function.BiConsumer; -import java.util.function.Function; +import java.util.function.Consumer; +import java.util.function.Supplier; import javax.annotation.Nullable; -@JsonDeserialize(using = ListingMessageDeserializer.class) public class ListingMessage { @Nullable protected final String title; @Nullable protected final String url; @@ -140,70 +137,6 @@ public class ListingMessage { return afterFields; } - public EmbedBuilder create( - ISubstitutor global, - Iterable iterable, - BiConsumer entryApplier - ) { - final EmbedBuilder builder = new EmbedBuilder(); - builder.setTitle(global.substitute(title), global.substitute(url)); - builder.setColor(parseColor(global.substitute(color))); - builder.setAuthor(global.substitute(authorName), global.substitute(authorUrl), global.substitute(authorIconUrl)); - builder.setDescription(global.substitute(description)); - builder.setImage(global.substitute(imageUrl)); - builder.setThumbnail(global.substitute(thumbnailUrl)); - builder.setTimestamp(OffsetDateTime.now(ZoneOffset.UTC)); - builder.setFooter(global.substitute(footerText), global.substitute(footerIconUrl)); - for (MessageEmbed.Field field : beforeFields) { - builder.addField(global.substitute(field.getName()), global.substitute(field.getValue()), field.isInline()); - } - - final CustomSubstitutions entrySubs = new CustomSubstitutions(); - final Function entryFunc = str -> str != null ? entrySubs.substitute(global.substitute(str)) : null; - int count = 0; - for (T listEntry : iterable) { - entryApplier.accept(listEntry, entrySubs); - if (entry instanceof FieldEntry) { - FieldEntry fieldEntry = (FieldEntry) entry; - builder.addField( - entryFunc.apply(fieldEntry.getFieldName()), - entryFunc.apply(fieldEntry.getFieldValue()), - fieldEntry.isInline() - ); - } else if (entry instanceof DescriptionEntry) { - DescriptionEntry descEntry = (DescriptionEntry) entry; - builder.getDescriptionBuilder().append(entryFunc.apply(descEntry.getDescription())); - builder.getDescriptionBuilder().append(descEntry.getJoiner()); - } - count++; - } - if (count < 1) { - builder.getDescriptionBuilder().append(global.substitute(emptyText)); - } - - for (MessageEmbed.Field field : afterFields) { - builder.addField(global.substitute(field.getName()), global.substitute(field.getValue()), field.isInline()); - } - return builder; - } - - private static int parseColor(String str) { - if (Strings.isNullOrEmpty(str)) return Role.DEFAULT_COLOR_RAW; - if (str.startsWith("0x")) { - // noinspection UnstableApiUsage - final Integer res = Ints.tryParse(str.substring(2), 16); - if (res != null) { - return res; - } - } - // noinspection UnstableApiUsage - final Integer res = Ints.tryParse(str, 10); - if (res != null) { - return res; - } - return Role.DEFAULT_COLOR_RAW; - } - public interface Entry {} public static class DescriptionEntry implements Entry { @@ -248,4 +181,23 @@ public class ListingMessage { return inline; } } + + public interface Builder extends ModifiableSubstitutions> { + Builder amountPerPage(int amountPerPage); + + Builder setEntryApplier(BiConsumer> entryApplier); + + Builder apply(Consumer> consumer); + + Builder with(final String argument, final Supplier value); + + void build(MessageChannel channel, + SubstitutionsMap globalSubstitutions, + Message triggerMessage, + List entries); + + default void build(MessageChannel channel, JanitorBot bot, Message triggerMessage, List entries) { + build(channel, bot.getSubstitutions(), triggerMessage, entries); + } + } } diff --git a/core/src/api/java/sciwhiz12/janitor/api/messages/Messages.java b/core/src/api/java/sciwhiz12/janitor/api/messages/Messages.java new file mode 100644 index 0000000..5acf686 --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/messages/Messages.java @@ -0,0 +1,51 @@ +package sciwhiz12.janitor.api.messages; + +import net.dv8tion.jda.api.entities.MessageEmbed; +import sciwhiz12.janitor.api.JanitorBot; + +import java.util.Collections; + +public interface Messages { + JanitorBot getBot(); + + void loadMessages(); + + RegularMessage.Builder getRegularMessage(String messageKey); + + ListingMessage.Builder getListingMessage(String messageKey); + + RegularMessage UNKNOWN_REGULAR_MESSAGE = new RegularMessage( + "UNKNOWN MESSAGE!", + null, + "A regular message was tried to be looked up, but was not found. " + + "Please report this to your bot maintainer/administrator.", + String.valueOf(0xFF0000), + null, + null, + null, + null, + null, + null, + null, + Collections.singletonList(new MessageEmbed.Field("Message Key", "${key}", false)) + ); + + ListingMessage UNKNOWN_LISTING_MESSAGE = new ListingMessage( + "UNKNOWN MESSAGE!", + null, + "A listing message was tried to be looked up, but was not found. " + + "Please report this to your bot maintainer/administrator.", + String.valueOf(0xFF0000), + null, + null, + null, + null, + null, + null, + null, + null, + new ListingMessage.DescriptionEntry(null, ""), + Collections.singletonList(new MessageEmbed.Field("Message Key", "${key}", false)), + Collections.emptyList() + ); +} diff --git a/src/main/java/sciwhiz12/janitor/msg/json/RegularMessage.java b/core/src/api/java/sciwhiz12/janitor/api/messages/RegularMessage.java similarity index 62% rename from src/main/java/sciwhiz12/janitor/msg/json/RegularMessage.java rename to core/src/api/java/sciwhiz12/janitor/api/messages/RegularMessage.java index ff96769..2eb15fc 100644 --- a/src/main/java/sciwhiz12/janitor/msg/json/RegularMessage.java +++ b/core/src/api/java/sciwhiz12/janitor/api/messages/RegularMessage.java @@ -1,15 +1,12 @@ -package sciwhiz12.janitor.msg.json; +package sciwhiz12.janitor.api.messages; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.google.common.primitives.Ints; -import joptsimple.internal.Strings; -import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.entities.Role; -import sciwhiz12.janitor.msg.substitution.ISubstitutor; +import net.dv8tion.jda.api.requests.restaction.MessageAction; +import sciwhiz12.janitor.api.JanitorBot; +import sciwhiz12.janitor.api.messages.substitution.ModifiableSubstitutions; +import sciwhiz12.janitor.api.messages.substitution.SubstitutionsMap; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -17,30 +14,18 @@ import java.util.Objects; import java.util.StringJoiner; import javax.annotation.Nullable; -@JsonDeserialize(using = RegularMessageDeserializer.class) public class RegularMessage { - @Nullable - protected final String title; - @Nullable - protected final String url; - @Nullable - protected final String description; - @Nullable - protected final String color; - @Nullable - protected final String authorName; - @Nullable - protected final String authorUrl; - @Nullable - protected final String authorIconUrl; - @Nullable - protected final String footerText; - @Nullable - protected final String footerIconUrl; - @Nullable - protected final String imageUrl; - @Nullable - protected final String thumbnailUrl; + @Nullable protected final String title; + @Nullable protected final String url; + @Nullable protected final String description; + @Nullable protected final String color; + @Nullable protected final String authorName; + @Nullable protected final String authorUrl; + @Nullable protected final String authorIconUrl; + @Nullable protected final String footerText; + @Nullable protected final String footerIconUrl; + @Nullable protected final String imageUrl; + @Nullable protected final String thumbnailUrl; protected final List fields; public RegularMessage( @@ -174,36 +159,14 @@ public class RegularMessage { thumbnailUrl, fields); } - public EmbedBuilder create(ISubstitutor subs) { - final EmbedBuilder builder = new EmbedBuilder(); - builder.setTitle(subs.substitute(title), subs.substitute(url)); - builder.setColor(parseColor(subs.substitute(color))); - builder.setAuthor(subs.substitute(authorName), subs.substitute(authorUrl), subs.substitute(authorIconUrl)); - builder.setDescription(subs.substitute(description)); - builder.setImage(subs.substitute(imageUrl)); - builder.setThumbnail(subs.substitute(thumbnailUrl)); - builder.setTimestamp(OffsetDateTime.now(ZoneOffset.UTC)); - builder.setFooter(subs.substitute(footerText), subs.substitute(footerIconUrl)); - for (MessageEmbed.Field field : fields) { - builder.addField(subs.substitute(field.getName()), subs.substitute(field.getValue()), field.isInline()); - } - return builder; - } + public interface Builder> extends ModifiableSubstitutions { + MessageEmbed build(SubstitutionsMap substitutions); - private static int parseColor(String str) { - if (Strings.isNullOrEmpty(str)) return Role.DEFAULT_COLOR_RAW; - if (str.startsWith("0x")) { - // noinspection UnstableApiUsage - final Integer res = Ints.tryParse(str.substring(2), 16); - if (res != null) { - return res; - } + MessageEmbed build(JanitorBot bot); + + default MessageAction send(JanitorBot bot, MessageChannel channel) { + return channel.sendMessage(build(bot)); } - // noinspection UnstableApiUsage - final Integer res = Ints.tryParse(str, 10); - if (res != null) { - return res; - } - return Role.DEFAULT_COLOR_RAW; + } } diff --git a/core/src/api/java/sciwhiz12/janitor/api/messages/emote/ReactionManager.java b/core/src/api/java/sciwhiz12/janitor/api/messages/emote/ReactionManager.java new file mode 100644 index 0000000..df31d47 --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/messages/emote/ReactionManager.java @@ -0,0 +1,16 @@ +package sciwhiz12.janitor.api.messages.emote; + +import net.dv8tion.jda.api.entities.Message; +import sciwhiz12.janitor.api.JanitorBot; + +import java.util.Map; + +public interface ReactionManager { + ReactionMessage newMessage(Message message); + + void removeMessage(long messageID); + + Map getRegisteredMessages(); + + JanitorBot getBot(); +} diff --git a/core/src/api/java/sciwhiz12/janitor/api/messages/emote/ReactionMessage.java b/core/src/api/java/sciwhiz12/janitor/api/messages/emote/ReactionMessage.java new file mode 100644 index 0000000..0e5bf8c --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/messages/emote/ReactionMessage.java @@ -0,0 +1,36 @@ +package sciwhiz12.janitor.api.messages.emote; + +import net.dv8tion.jda.api.entities.Emote; +import net.dv8tion.jda.api.entities.MessageReaction.ReactionEmote; +import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; +import sciwhiz12.janitor.api.JanitorBot; + +import java.util.Map; +import java.util.function.BiConsumer; + +public interface ReactionMessage { + ReactionMessage add(ReactionEmote emote, ReactionListener listener); + + ReactionMessage add(String emote, ReactionListener listener); + + ReactionMessage add(Emote emote, ReactionListener listener); + + ReactionMessage removeEmotes(boolean remove); + + ReactionMessage owner(long ownerID); + + void create(); + + long getOwnerID(); + + boolean isOwnerOnly(); + + Map getListeners(); + + JanitorBot getBot(); + + @FunctionalInterface + interface ReactionListener extends BiConsumer { + void accept(ReactionMessage message, MessageReactionAddEvent event); + } +} diff --git a/src/main/java/sciwhiz12/janitor/msg/substitution/ICustomSubstitutions.java b/core/src/api/java/sciwhiz12/janitor/api/messages/substitution/ModifiableSubstitutions.java similarity index 55% rename from src/main/java/sciwhiz12/janitor/msg/substitution/ICustomSubstitutions.java rename to core/src/api/java/sciwhiz12/janitor/api/messages/substitution/ModifiableSubstitutions.java index b0a1098..c226ee3 100644 --- a/src/main/java/sciwhiz12/janitor/msg/substitution/ICustomSubstitutions.java +++ b/core/src/api/java/sciwhiz12/janitor/api/messages/substitution/ModifiableSubstitutions.java @@ -1,9 +1,9 @@ -package sciwhiz12.janitor.msg.substitution; +package sciwhiz12.janitor.api.messages.substitution; import java.util.function.Consumer; import java.util.function.Supplier; -public interface ICustomSubstitutions> { +public interface ModifiableSubstitutions> { T with(String argument, Supplier value); T apply(Consumer consumer); diff --git a/core/src/api/java/sciwhiz12/janitor/api/messages/substitution/ModifiableSubstitutor.java b/core/src/api/java/sciwhiz12/janitor/api/messages/substitution/ModifiableSubstitutor.java new file mode 100644 index 0000000..05b9cec --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/messages/substitution/ModifiableSubstitutor.java @@ -0,0 +1,4 @@ +package sciwhiz12.janitor.api.messages.substitution; + +public interface ModifiableSubstitutor> extends ModifiableSubstitutions, Substitutor { +} diff --git a/core/src/api/java/sciwhiz12/janitor/api/messages/substitution/SubstitutionsMap.java b/core/src/api/java/sciwhiz12/janitor/api/messages/substitution/SubstitutionsMap.java new file mode 100644 index 0000000..3e674d4 --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/messages/substitution/SubstitutionsMap.java @@ -0,0 +1,45 @@ +package sciwhiz12.janitor.api.messages.substitution; + +import sciwhiz12.janitor.api.JanitorBot; + +import java.util.Map; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nullable; + +import static java.util.regex.Pattern.CASE_INSENSITIVE; + +public interface SubstitutionsMap extends Substitutor { + Pattern ARGUMENT_REGEX = Pattern.compile("\\$\\{(.+?)}", CASE_INSENSITIVE); + Pattern NULL_ARGUMENT_REGEX = Pattern.compile("nullcheck;(.+?);(.+)", CASE_INSENSITIVE); + + static String quote(@Nullable String input) { + return input != null ? Matcher.quoteReplacement(input) : ""; + } + + @Nullable + static String substitute(@Nullable String text, Map> arguments) { + if (text == null || text.isBlank()) return null; + final Matcher matcher = ARGUMENT_REGEX.matcher(text); + return matcher.replaceAll(matchResult -> { + final Matcher nullMatcher = NULL_ARGUMENT_REGEX.matcher(matchResult.group(1)); + if (nullMatcher.matches()) { + final String grp1 = nullMatcher.group(1); + return quote(arguments.getOrDefault( + grp1, + () -> arguments.getOrDefault(nullMatcher.group(2), () -> nullMatcher.group(2)).get() + ).get()); + } + return quote(arguments.getOrDefault(matchResult.group(1), () -> matchResult.group(0)).get()); + }); + } + + String with(String text, Map> substitutions); + + ModifiableSubstitutor with(Map> customSubstitutions); + + Map> createDefaultedMap(Map> custom); + + JanitorBot getBot(); +} diff --git a/core/src/api/java/sciwhiz12/janitor/api/messages/substitution/Substitutor.java b/core/src/api/java/sciwhiz12/janitor/api/messages/substitution/Substitutor.java new file mode 100644 index 0000000..0569273 --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/messages/substitution/Substitutor.java @@ -0,0 +1,15 @@ +package sciwhiz12.janitor.api.messages.substitution; + +import java.util.function.UnaryOperator; +import javax.annotation.Nullable; + +public interface Substitutor extends UnaryOperator { + @Override + @Nullable + default String apply(@Nullable String input) { + return substitute(input); + } + + @Nullable + String substitute(@Nullable String text); +} diff --git a/core/src/api/java/sciwhiz12/janitor/api/module/Module.java b/core/src/api/java/sciwhiz12/janitor/api/module/Module.java new file mode 100644 index 0000000..ca9f94d --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/module/Module.java @@ -0,0 +1,11 @@ +package sciwhiz12.janitor.api.module; + +import sciwhiz12.janitor.api.JanitorBot; + +public interface Module { + void activate(); + + void shutdown(); + + JanitorBot getBot(); +} diff --git a/core/src/api/java/sciwhiz12/janitor/api/module/ModuleKey.java b/core/src/api/java/sciwhiz12/janitor/api/module/ModuleKey.java new file mode 100644 index 0000000..837c53f --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/module/ModuleKey.java @@ -0,0 +1,40 @@ +package sciwhiz12.janitor.api.module; + +import com.google.common.base.Preconditions; + +import java.util.Objects; + +public class ModuleKey { + private final String moduleID; + private final Class type; + + public ModuleKey(String storageID, Class type) { + Preconditions.checkNotNull(storageID, "Module ID must not be null"); + Preconditions.checkArgument(!storageID.isBlank(), "Module ID must not be empty or blank"); + Preconditions.checkNotNull(type, "Class type must not be null"); + this.moduleID = storageID; + this.type = type; + } + + public String getModuleID() { + return moduleID; + } + + public Class getType() { + return type; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ModuleKey that = (ModuleKey) o; + return moduleID.equals(that.moduleID) && + type.equals(that.type); + } + + @Override + public int hashCode() { + return Objects.hash(moduleID, type); + } +} diff --git a/core/src/api/java/sciwhiz12/janitor/api/module/ModuleManager.java b/core/src/api/java/sciwhiz12/janitor/api/module/ModuleManager.java new file mode 100644 index 0000000..1b21660 --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/module/ModuleManager.java @@ -0,0 +1,23 @@ +package sciwhiz12.janitor.api.module; + +import sciwhiz12.janitor.api.JanitorBot; + +import java.util.Set; +import javax.annotation.Nullable; + +public interface ModuleManager { + void disableModule(String id); + + void enableModule(String id); + + Set> getAvailableModules(); + + Set> getActiveModules(); + + @Nullable + M getModule(ModuleKey moduleKey); + + boolean isActivated(); + + JanitorBot getBot(); +} diff --git a/core/src/api/java/sciwhiz12/janitor/api/module/ModuleProvider.java b/core/src/api/java/sciwhiz12/janitor/api/module/ModuleProvider.java new file mode 100644 index 0000000..8876aa0 --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/module/ModuleProvider.java @@ -0,0 +1,13 @@ +package sciwhiz12.janitor.api.module; + +import sciwhiz12.janitor.api.JanitorBot; + +import java.util.Set; +import javax.annotation.Nullable; + +public interface ModuleProvider { + Set> getAvailableModules(); + + @Nullable + M createModule(ModuleKey moduleID, JanitorBot bot); +} diff --git a/src/main/java/sciwhiz12/janitor/storage/AbstractStorage.java b/core/src/api/java/sciwhiz12/janitor/api/storage/AbstractStorage.java similarity index 75% rename from src/main/java/sciwhiz12/janitor/storage/AbstractStorage.java rename to core/src/api/java/sciwhiz12/janitor/api/storage/AbstractStorage.java index 567f3f6..94d7999 100644 --- a/src/main/java/sciwhiz12/janitor/storage/AbstractStorage.java +++ b/core/src/api/java/sciwhiz12/janitor/api/storage/AbstractStorage.java @@ -1,6 +1,6 @@ -package sciwhiz12.janitor.storage; +package sciwhiz12.janitor.api.storage; -public abstract class AbstractStorage implements IStorage { +public abstract class AbstractStorage implements Storage { private boolean dirty; public boolean isDirty() { diff --git a/core/src/api/java/sciwhiz12/janitor/api/storage/GuildStorageManager.java b/core/src/api/java/sciwhiz12/janitor/api/storage/GuildStorageManager.java new file mode 100644 index 0000000..afe7913 --- /dev/null +++ b/core/src/api/java/sciwhiz12/janitor/api/storage/GuildStorageManager.java @@ -0,0 +1,18 @@ +package sciwhiz12.janitor.api.storage; + +import net.dv8tion.jda.api.entities.Guild; +import sciwhiz12.janitor.api.JanitorBot; + +import java.util.function.Supplier; + +public interface GuildStorageManager { + default S getOrCreate(Guild guild, StorageKey key, Supplier defaultSupplier) { + return getOrCreate(guild.getIdLong(), key, defaultSupplier); + } + + S getOrCreate(long guildID, StorageKey key, Supplier defaultSupplier); + + void save(); + + JanitorBot getBot(); +} diff --git a/src/main/java/sciwhiz12/janitor/storage/JsonStorage.java b/core/src/api/java/sciwhiz12/janitor/api/storage/JsonStorage.java similarity index 96% rename from src/main/java/sciwhiz12/janitor/storage/JsonStorage.java rename to core/src/api/java/sciwhiz12/janitor/api/storage/JsonStorage.java index 5763234..64df7a9 100644 --- a/src/main/java/sciwhiz12/janitor/storage/JsonStorage.java +++ b/core/src/api/java/sciwhiz12/janitor/api/storage/JsonStorage.java @@ -1,4 +1,4 @@ -package sciwhiz12.janitor.storage; +package sciwhiz12.janitor.api.storage; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/src/main/java/sciwhiz12/janitor/storage/IStorage.java b/core/src/api/java/sciwhiz12/janitor/api/storage/Storage.java similarity index 75% rename from src/main/java/sciwhiz12/janitor/storage/IStorage.java rename to core/src/api/java/sciwhiz12/janitor/api/storage/Storage.java index 655a775..5febd0a 100644 --- a/src/main/java/sciwhiz12/janitor/storage/IStorage.java +++ b/core/src/api/java/sciwhiz12/janitor/api/storage/Storage.java @@ -1,11 +1,10 @@ -package sciwhiz12.janitor.storage; +package sciwhiz12.janitor.api.storage; import java.io.IOException; import java.io.Reader; import java.io.Writer; -public interface IStorage { - +public interface Storage { boolean dirty(); void write(Writer output) throws IOException; diff --git a/src/main/java/sciwhiz12/janitor/storage/StorageKey.java b/core/src/api/java/sciwhiz12/janitor/api/storage/StorageKey.java similarity index 74% rename from src/main/java/sciwhiz12/janitor/storage/StorageKey.java rename to core/src/api/java/sciwhiz12/janitor/api/storage/StorageKey.java index 950bda4..84656ae 100644 --- a/src/main/java/sciwhiz12/janitor/storage/StorageKey.java +++ b/core/src/api/java/sciwhiz12/janitor/api/storage/StorageKey.java @@ -1,15 +1,15 @@ -package sciwhiz12.janitor.storage; +package sciwhiz12.janitor.api.storage; import com.google.common.base.Preconditions; import java.util.Objects; /** - * A storage key, used to retrieve an instance of an {@link IStorage} from a {@link GuildStorage}. + * A storage key, used to retrieve an instance of an {@link Storage} from a {@link GuildStorageManager}. * - * @param the type of the {@link IStorage} + * @param the type of the {@link Storage} */ -public class StorageKey { +public class StorageKey { private final String storageID; private final Class type; @@ -31,9 +31,9 @@ public class StorageKey { } /** - * Returns the storage ID, used by {@link GuildStorage} to uniquely identify this storage's data. + * Returns the storage ID, used by {@link GuildStorageManager} to uniquely identify this storage's data. * - *

This is currently used by {@code GuildStorage} as the folder name of the storage. + *

This is currently used by {@code GuildStorageManager} as the folder name of the storage. * * @return the storage ID */ @@ -42,7 +42,7 @@ public class StorageKey { } /** - * Returns the class of the {@link IStorage} subtype that this storage key represents, which + * Returns the class of the {@link Storage} subtype that this storage key represents, which * is also used in the key's generics. * * @return the class of the generic type diff --git a/src/main/java/sciwhiz12/janitor/commands/util/CommandHelper.java b/core/src/api/java/sciwhiz12/janitor/api/utils/CommandHelper.java similarity index 85% rename from src/main/java/sciwhiz12/janitor/commands/util/CommandHelper.java rename to core/src/api/java/sciwhiz12/janitor/api/utils/CommandHelper.java index 7aab24f..e50ff06 100644 --- a/src/main/java/sciwhiz12/janitor/commands/util/CommandHelper.java +++ b/core/src/api/java/sciwhiz12/janitor/api/utils/CommandHelper.java @@ -1,11 +1,13 @@ -package sciwhiz12.janitor.commands.util; +package sciwhiz12.janitor.api.utils; import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -public class CommandHelper { +public final class CommandHelper { + private CommandHelper() {} + public static LiteralArgumentBuilder literal(String command) { return LiteralArgumentBuilder.literal(command); } diff --git a/src/main/java/sciwhiz12/janitor/msg/MessageHelper.java b/core/src/api/java/sciwhiz12/janitor/api/utils/MessageHelper.java similarity index 63% rename from src/main/java/sciwhiz12/janitor/msg/MessageHelper.java rename to core/src/api/java/sciwhiz12/janitor/api/utils/MessageHelper.java index 59eb603..18c9b48 100644 --- a/src/main/java/sciwhiz12/janitor/msg/MessageHelper.java +++ b/core/src/api/java/sciwhiz12/janitor/api/utils/MessageHelper.java @@ -1,4 +1,4 @@ -package sciwhiz12.janitor.msg; +package sciwhiz12.janitor.api.utils; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.IMentionable; @@ -6,9 +6,7 @@ import net.dv8tion.jda.api.entities.ISnowflake; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.User; -import sciwhiz12.janitor.moderation.notes.NoteEntry; -import sciwhiz12.janitor.moderation.warns.WarningEntry; -import sciwhiz12.janitor.msg.substitution.ICustomSubstitutions; +import sciwhiz12.janitor.api.messages.substitution.ModifiableSubstitutions; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; @@ -17,22 +15,22 @@ import java.util.function.Consumer; import static java.time.temporal.ChronoField.*; -public class MessageHelper { +public final class MessageHelper { private MessageHelper() {} - public static > Consumer snowflake(String head, ISnowflake snowflake) { + public static > Consumer snowflake(String head, ISnowflake snowflake) { return builder -> builder .with(head + ".id", snowflake::getId) .with(head + ".creation_datetime", () -> snowflake.getTimeCreated().format(DATE_TIME_FORMAT)); } - public static > Consumer mentionable(String head, IMentionable mentionable) { - return builder -> builder + public static > Consumer mentionable(String head, IMentionable mentionable) { + return builder -> builder .apply(snowflake(head, mentionable)) .with(head + ".mention", mentionable::getAsMention); } - public static > Consumer role(String head, Role role) { + public static > Consumer role(String head, Role role) { return builder -> builder .apply(mentionable(head, role)) .with(head + ".color_hex", () -> Integer.toHexString(role.getColorRaw())) @@ -40,7 +38,7 @@ public class MessageHelper { .with(head + ".permissions", role.getPermissions()::toString); } - public static > Consumer user(String head, User user) { + public static > Consumer user(String head, User user) { return builder -> builder .apply(mentionable(head, user)) .with(head + ".name", user::getName) @@ -49,7 +47,7 @@ public class MessageHelper { .with(head + ".flags", user.getFlags()::toString); } - public static > Consumer guild(String head, Guild guild) { + public static > Consumer guild(String head, Guild guild) { return builder -> builder .apply(snowflake(head, guild)) .with(head + ".name", guild::getName) @@ -62,7 +60,7 @@ public class MessageHelper { .with(head + ".icon_url", () -> Objects.toString(guild.getIconUrl(), "")); } - public static > Consumer member(String head, Member member) { + public static > Consumer member(String head, Member member) { return builder -> builder .apply(user(head, member.getUser())) .apply(guild(head + ".guild", member.getGuild())) @@ -72,24 +70,6 @@ public class MessageHelper { .with(head + ".color", () -> String.valueOf(member.getColorRaw())); } - public static > Consumer warningEntry(String head, int caseID, WarningEntry entry) { - return builder -> builder - .with(head + ".case_id", () -> String.valueOf(caseID)) - .apply(user(head + ".performer", entry.getPerformer())) - .apply(user(head + ".target", entry.getWarned())) - .with(head + ".date_time", () -> entry.getDateTime().format(DATE_TIME_FORMAT)) - .with(head + ".reason", entry::getReason); - } - - public static > Consumer noteEntry(String head, int noteID, NoteEntry entry) { - return builder -> builder - .with(head + ".note_id", () -> String.valueOf(noteID)) - .apply(user(head + ".performer", entry.getPerformer())) - .apply(user(head + ".target", entry.getTarget())) - .with(head + ".date_time", () -> entry.getDateTime().format(DATE_TIME_FORMAT)) - .with(head + ".contents", entry::getContents); - } - public static final DateTimeFormatter DATE_TIME_FORMAT = new DateTimeFormatterBuilder() .parseCaseInsensitive() .parseLenient() diff --git a/src/main/java/sciwhiz12/janitor/utils/StringReaderUtil.java b/core/src/api/java/sciwhiz12/janitor/api/utils/StringReaderUtil.java similarity index 92% rename from src/main/java/sciwhiz12/janitor/utils/StringReaderUtil.java rename to core/src/api/java/sciwhiz12/janitor/api/utils/StringReaderUtil.java index 9d2a5c7..9c084e7 100644 --- a/src/main/java/sciwhiz12/janitor/utils/StringReaderUtil.java +++ b/core/src/api/java/sciwhiz12/janitor/api/utils/StringReaderUtil.java @@ -1,11 +1,13 @@ -package sciwhiz12.janitor.utils; +package sciwhiz12.janitor.api.utils; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import static com.mojang.brigadier.StringReader.isQuotedStringStart; -public class StringReaderUtil { +public final class StringReaderUtil { + private StringReaderUtil() {} + public static boolean isAllowedInUnquotedString(final char c) { return c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' @@ -24,7 +26,7 @@ public class StringReaderUtil { return reader.getString().substring(start, reader.getCursor()); } - public String readQuotedString(StringReader reader) throws CommandSyntaxException { + public static String readQuotedString(StringReader reader) throws CommandSyntaxException { if (!reader.canRead()) { return ""; } diff --git a/src/main/java/sciwhiz12/janitor/BotConsole.java b/core/src/main/java/sciwhiz12/janitor/BotConsole.java similarity index 91% rename from src/main/java/sciwhiz12/janitor/BotConsole.java rename to core/src/main/java/sciwhiz12/janitor/BotConsole.java index fbf8c4e..37568b1 100644 --- a/src/main/java/sciwhiz12/janitor/BotConsole.java +++ b/core/src/main/java/sciwhiz12/janitor/BotConsole.java @@ -1,18 +1,20 @@ package sciwhiz12.janitor; import com.google.common.base.Strings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.InputStream; import java.util.Scanner; -import static sciwhiz12.janitor.Logging.CONSOLE; - public class BotConsole { - private final JanitorBot bot; + public static final Logger CONSOLE = LoggerFactory.getLogger("janitor.console"); + + private final JanitorBotImpl bot; private final Thread thread; private volatile boolean running = true; - public BotConsole(JanitorBot bot, InputStream input) { + public BotConsole(JanitorBotImpl bot, InputStream input) { this.bot = bot; this.thread = new Thread(this.new ConsoleThread(input)); this.thread.setName("janitor_console"); diff --git a/src/main/java/sciwhiz12/janitor/JanitorBot.java b/core/src/main/java/sciwhiz12/janitor/JanitorBotImpl.java similarity index 53% rename from src/main/java/sciwhiz12/janitor/JanitorBot.java rename to core/src/main/java/sciwhiz12/janitor/JanitorBotImpl.java index db44184..47030d6 100644 --- a/src/main/java/sciwhiz12/janitor/JanitorBot.java +++ b/core/src/main/java/sciwhiz12/janitor/JanitorBotImpl.java @@ -6,46 +6,52 @@ import net.dv8tion.jda.api.entities.Activity; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.PrivateChannel; import net.dv8tion.jda.api.entities.User; -import sciwhiz12.janitor.commands.CommandRegistry; -import sciwhiz12.janitor.config.BotConfig; -import sciwhiz12.janitor.config.ConfigManager; -import sciwhiz12.janitor.msg.Messages; -import sciwhiz12.janitor.msg.emote.ReactionManager; -import sciwhiz12.janitor.msg.substitution.SubstitutionMap; -import sciwhiz12.janitor.storage.GuildStorage; +import sciwhiz12.janitor.api.JanitorBot; +import sciwhiz12.janitor.commands.CommandRegistryImpl; +import sciwhiz12.janitor.config.BotConfigImpl; +import sciwhiz12.janitor.config.ConfigManagerImpl; +import sciwhiz12.janitor.messages.MessagesImpl; +import sciwhiz12.janitor.messages.emote.ReactionManagerImpl; +import sciwhiz12.janitor.messages.substitution.SubstitutionsMapImpl; +import sciwhiz12.janitor.module.ModuleManagerImpl; +import sciwhiz12.janitor.storage.GuildStorageManagerImpl; import sciwhiz12.janitor.utils.Util; import java.nio.file.Path; import java.util.concurrent.CompletableFuture; -import static sciwhiz12.janitor.Logging.JANITOR; -import static sciwhiz12.janitor.Logging.STATUS; +import static sciwhiz12.janitor.api.Logging.JANITOR; +import static sciwhiz12.janitor.api.Logging.STATUS; -public class JanitorBot { +public class JanitorBotImpl implements JanitorBot { private final JDA discord; - private final BotConfig config; + private final BotConfigImpl config; private final BotConsole console; - private final GuildStorage storage; - private final GuildStorage.SavingThread storageSavingThread; - private final ConfigManager configManager; - private final CommandRegistry cmdRegistry; - private final SubstitutionMap substitutions; - private final Messages messages; - private final ReactionManager reactions; + private final GuildStorageManagerImpl storage; + private final GuildStorageManagerImpl.SavingThread storageSavingThread; + private final ConfigManagerImpl configManager; + private final CommandRegistryImpl cmdRegistry; + private final SubstitutionsMapImpl substitutions; + private final MessagesImpl messages; + private final ReactionManagerImpl reactions; + private final ModuleManagerImpl modules; - public JanitorBot(JDA discord, BotConfig config) { + public JanitorBotImpl(JDA discord, BotConfigImpl config) { this.config = config; this.discord = discord; this.console = new BotConsole(this, System.in); - this.storage = new GuildStorage(this, Path.of(config.STORAGE_PATH.get())); - this.configManager = new ConfigManager(this, config.getConfigsFolder()); - this.cmdRegistry = new CommandRegistry(this); - this.substitutions = new SubstitutionMap(this); - this.messages = new Messages(this, config.getMessagesFolder()); - this.reactions = new ReactionManager(this); + this.storage = new GuildStorageManagerImpl(this, Path.of(config.STORAGE_PATH.get())); + this.configManager = new ConfigManagerImpl(this, config.getConfigsFolder()); + this.cmdRegistry = new CommandRegistryImpl(this); + this.substitutions = new SubstitutionsMapImpl(this); + this.messages = new MessagesImpl(this); + this.reactions = new ReactionManagerImpl(this); + this.modules = new ModuleManagerImpl(this); + modules.activateModules(); // TODO: find which of these can be loaded in parallel before the bot JDA is ready discord.addEventListener(cmdRegistry, reactions); discord.getPresence().setPresence(OnlineStatus.ONLINE, Activity.playing(" n' sweeping n' testing!")); + //noinspection ResultOfMethodCallIgnored discord.getGuilds().forEach(Guild::loadMembers); JANITOR.info("Ready!"); config.getOwnerID() @@ -59,38 +65,60 @@ public class JanitorBot { error -> JANITOR.error(STATUS, "Error while sending ready message to owner", error) ) ); - storageSavingThread = new GuildStorage.SavingThread(storage); + storageSavingThread = new GuildStorageManagerImpl.SavingThread(storage); storageSavingThread.start(); console.start(); } + @Override public JDA getDiscord() { return this.discord; } - public BotConfig getConfig() { + @Override + public BotConfigImpl getBotConfig() { return this.config; } - public Messages getMessages() { + @Override + public MessagesImpl getMessages() { return messages; } - public GuildStorage getStorage() { return this.storage; } + @Override + public SubstitutionsMapImpl getSubstitutions() { + return substitutions; + } - public ConfigManager getConfigManager() { return configManager; } + @Override + public GuildStorageManagerImpl getGuildStorage() { + return this.storage; + } - public CommandRegistry getCommandRegistry() { + @Override + public ConfigManagerImpl getConfigs() { + return configManager; + } + + @Override + public CommandRegistryImpl getCommands() { return this.cmdRegistry; } - public ReactionManager getReactionManager() { + @Override + public ReactionManagerImpl getReactions() { return this.reactions; } + @Override + public ModuleManagerImpl getModuleManager() { + return modules; + } + + @Override public void shutdown() { JANITOR.info(STATUS, "Shutting down!"); - getConfig().getOwnerID() + getBotConfig().getOwnerID() .map(discord::retrieveUserById) .map(owner -> owner @@ -108,15 +136,13 @@ public class JanitorBot { .error(STATUS, "Error while sending shutdown message to owner", err) )) ).ifPresent(CompletableFuture::join); + modules.shutdown(); discord.shutdown(); storageSavingThread.stopThread(); storage.save(); configManager.save(); configManager.close(); console.stop(); - } - - public SubstitutionMap getSubstitutions() { - return substitutions; + config.close(); } } diff --git a/src/main/java/sciwhiz12/janitor/commands/CommandRegistry.java b/core/src/main/java/sciwhiz12/janitor/commands/CommandRegistryImpl.java similarity index 59% rename from src/main/java/sciwhiz12/janitor/commands/CommandRegistry.java rename to core/src/main/java/sciwhiz12/janitor/commands/CommandRegistryImpl.java index e510748..f55d3c2 100644 --- a/src/main/java/sciwhiz12/janitor/commands/CommandRegistry.java +++ b/core/src/main/java/sciwhiz12/janitor/commands/CommandRegistryImpl.java @@ -8,60 +8,52 @@ import net.dv8tion.jda.api.events.GenericEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.hooks.EventListener; import org.jetbrains.annotations.NotNull; -import sciwhiz12.janitor.JanitorBot; +import sciwhiz12.janitor.JanitorBotImpl; +import sciwhiz12.janitor.api.command.Command; +import sciwhiz12.janitor.api.command.CommandRegistry; +import sciwhiz12.janitor.api.config.CoreConfigs; import sciwhiz12.janitor.commands.bot.ShutdownCommand; import sciwhiz12.janitor.commands.misc.HelloCommand; import sciwhiz12.janitor.commands.misc.OKCommand; import sciwhiz12.janitor.commands.misc.PingCommand; -import sciwhiz12.janitor.commands.moderation.BanCommand; -import sciwhiz12.janitor.commands.moderation.KickCommand; -import sciwhiz12.janitor.commands.moderation.NoteCommand; -import sciwhiz12.janitor.commands.moderation.UnbanCommand; -import sciwhiz12.janitor.commands.moderation.UnwarnCommand; -import sciwhiz12.janitor.commands.moderation.WarnCommand; -import sciwhiz12.janitor.commands.moderation.WarnListCommand; -import sciwhiz12.janitor.config.GuildConfig; import sciwhiz12.janitor.utils.Util; import java.util.HashMap; import java.util.Map; +import java.util.function.Function; -import static sciwhiz12.janitor.Logging.COMMANDS; -import static sciwhiz12.janitor.Logging.JANITOR; +import static sciwhiz12.janitor.api.Logging.COMMANDS; +import static sciwhiz12.janitor.api.Logging.JANITOR; -public class CommandRegistry implements EventListener { - private final JanitorBot bot; - private final Map registry = new HashMap<>(); +public class CommandRegistryImpl implements CommandRegistry, EventListener { + private final JanitorBotImpl bot; + private final Map registry = new HashMap<>(); private final CommandDispatcher dispatcher; - public CommandRegistry(JanitorBot bot) { + public CommandRegistryImpl(JanitorBotImpl bot) { this.bot = bot; this.dispatcher = new CommandDispatcher<>(); - addCommand(new PingCommand(this, "ping", "Pong!")); - addCommand(new PingCommand(this, "pong", "Ping!")); - addCommand(new OKCommand(this)); - addCommand(new HelloCommand(this)); - addCommand(new KickCommand(this)); - addCommand(new BanCommand(this)); - addCommand(new UnbanCommand(this)); - addCommand(new WarnCommand(this)); - addCommand(new WarnListCommand(this)); - addCommand(new UnwarnCommand(this)); - addCommand(new ShutdownCommand(this)); - addCommand(new NoteCommand(this)); + addCommand(reg -> new PingCommand(reg, "ping", "Pong!")); + addCommand(reg -> new PingCommand(reg, "pong", "Ping!")); + addCommand(OKCommand::new); + addCommand(HelloCommand::new); + addCommand(ShutdownCommand::new); } + @Override public CommandDispatcher getDispatcher() { return this.dispatcher; } - public JanitorBot getBot() { + @Override + public JanitorBotImpl getBot() { return this.bot; } - public void addCommand(BaseCommand instance) { - dispatcher.register(instance.getNode()); + @Override + public void addCommand(Function command) { + dispatcher.register(command.apply(this).getNode()); } @Override @@ -71,10 +63,10 @@ public class CommandRegistry implements EventListener { if (event.getAuthor().isBot()) return; final String prefix; if (event.isFromGuild()) { - prefix = getBot().getConfigManager().getConfig(event.getGuild().getIdLong()) - .forGuild(GuildConfig.COMMAND_PREFIX); + prefix = getBot().getConfigs().get(event.getGuild().getIdLong()) + .forGuild(CoreConfigs.COMMAND_PREFIX); } else { - prefix = getBot().getConfig().getCommandPrefix(); + prefix = getBot().getBotConfig().getCommandPrefix(); } String msg = event.getMessage().getContentRaw(); diff --git a/src/main/java/sciwhiz12/janitor/commands/bot/ShutdownCommand.java b/core/src/main/java/sciwhiz12/janitor/commands/bot/ShutdownCommand.java similarity index 81% rename from src/main/java/sciwhiz12/janitor/commands/bot/ShutdownCommand.java rename to core/src/main/java/sciwhiz12/janitor/commands/bot/ShutdownCommand.java index ca35e03..ce00661 100644 --- a/src/main/java/sciwhiz12/janitor/commands/bot/ShutdownCommand.java +++ b/core/src/main/java/sciwhiz12/janitor/commands/bot/ShutdownCommand.java @@ -3,12 +3,12 @@ package sciwhiz12.janitor.commands.bot; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import sciwhiz12.janitor.commands.BaseCommand; -import sciwhiz12.janitor.commands.CommandRegistry; +import sciwhiz12.janitor.api.command.BaseCommand; +import sciwhiz12.janitor.api.command.CommandRegistry; import sciwhiz12.janitor.utils.Util; -import static sciwhiz12.janitor.Logging.JANITOR; -import static sciwhiz12.janitor.commands.util.CommandHelper.literal; +import static sciwhiz12.janitor.api.Logging.JANITOR; +import static sciwhiz12.janitor.api.utils.CommandHelper.literal; public class ShutdownCommand extends BaseCommand { public ShutdownCommand(CommandRegistry registry) { @@ -18,7 +18,7 @@ public class ShutdownCommand extends BaseCommand { @Override public LiteralArgumentBuilder getNode() { return literal("shutdown") - .requires(ctx -> getBot().getConfig().getOwnerID().map( + .requires(ctx -> getBot().getBotConfig().getOwnerID().map( id -> id == ctx.getAuthor().getIdLong()).orElse(false) ) .executes(this::run); diff --git a/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java b/core/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java similarity index 80% rename from src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java rename to core/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java index 6ab00a6..33f9fe7 100644 --- a/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java +++ b/core/src/main/java/sciwhiz12/janitor/commands/misc/HelloCommand.java @@ -5,17 +5,17 @@ import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import sciwhiz12.janitor.commands.BaseCommand; -import sciwhiz12.janitor.commands.CommandRegistry; -import sciwhiz12.janitor.commands.arguments.GuildMemberArgument; +import sciwhiz12.janitor.api.command.BaseCommand; +import sciwhiz12.janitor.api.command.CommandRegistry; +import sciwhiz12.janitor.api.command.arguments.GuildMemberArgument; import sciwhiz12.janitor.utils.Util; import java.util.List; -import static sciwhiz12.janitor.Logging.JANITOR; -import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.getMembers; -import static sciwhiz12.janitor.commands.util.CommandHelper.argument; -import static sciwhiz12.janitor.commands.util.CommandHelper.literal; +import static sciwhiz12.janitor.api.Logging.JANITOR; +import static sciwhiz12.janitor.api.command.arguments.GuildMemberArgument.getMembers; +import static sciwhiz12.janitor.api.utils.CommandHelper.argument; +import static sciwhiz12.janitor.api.utils.CommandHelper.literal; public class HelloCommand extends BaseCommand { public HelloCommand(CommandRegistry registry) { @@ -38,7 +38,7 @@ public class HelloCommand extends BaseCommand { success -> { JANITOR.debug("Sent greeting message to {}, on cmd of {}", Util.toString(member.getUser()), Util.toString(ctx.getSource().getAuthor())); - getBot().getReactionManager().newMessage(success) + getBot().getReactions().newMessage(success) .add("\u274C", (msg, event) -> success.delete() .flatMap(v -> event.getChannel() .deleteMessageById(ctx.getSource().getMessageIdLong())) diff --git a/src/main/java/sciwhiz12/janitor/commands/misc/OKCommand.java b/core/src/main/java/sciwhiz12/janitor/commands/misc/OKCommand.java similarity index 81% rename from src/main/java/sciwhiz12/janitor/commands/misc/OKCommand.java rename to core/src/main/java/sciwhiz12/janitor/commands/misc/OKCommand.java index 501fa00..65c3b6d 100644 --- a/src/main/java/sciwhiz12/janitor/commands/misc/OKCommand.java +++ b/core/src/main/java/sciwhiz12/janitor/commands/misc/OKCommand.java @@ -3,12 +3,12 @@ package sciwhiz12.janitor.commands.misc; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import sciwhiz12.janitor.commands.BaseCommand; -import sciwhiz12.janitor.commands.CommandRegistry; +import sciwhiz12.janitor.api.command.BaseCommand; +import sciwhiz12.janitor.api.command.CommandRegistry; import sciwhiz12.janitor.utils.Util; -import static sciwhiz12.janitor.Logging.JANITOR; -import static sciwhiz12.janitor.commands.util.CommandHelper.literal; +import static sciwhiz12.janitor.api.Logging.JANITOR; +import static sciwhiz12.janitor.api.utils.CommandHelper.literal; public class OKCommand extends BaseCommand { public OKCommand(CommandRegistry registry) { diff --git a/src/main/java/sciwhiz12/janitor/commands/misc/PingCommand.java b/core/src/main/java/sciwhiz12/janitor/commands/misc/PingCommand.java similarity index 83% rename from src/main/java/sciwhiz12/janitor/commands/misc/PingCommand.java rename to core/src/main/java/sciwhiz12/janitor/commands/misc/PingCommand.java index 3f7e4bf..0bfd17a 100644 --- a/src/main/java/sciwhiz12/janitor/commands/misc/PingCommand.java +++ b/core/src/main/java/sciwhiz12/janitor/commands/misc/PingCommand.java @@ -3,12 +3,12 @@ package sciwhiz12.janitor.commands.misc; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import sciwhiz12.janitor.commands.BaseCommand; -import sciwhiz12.janitor.commands.CommandRegistry; +import sciwhiz12.janitor.api.command.BaseCommand; +import sciwhiz12.janitor.api.command.CommandRegistry; import sciwhiz12.janitor.utils.Util; -import static sciwhiz12.janitor.Logging.JANITOR; -import static sciwhiz12.janitor.commands.util.CommandHelper.literal; +import static sciwhiz12.janitor.api.Logging.JANITOR; +import static sciwhiz12.janitor.api.utils.CommandHelper.literal; public class PingCommand extends BaseCommand { private final String command; diff --git a/src/main/java/sciwhiz12/janitor/config/BotConfig.java b/core/src/main/java/sciwhiz12/janitor/config/BotConfigImpl.java similarity index 85% rename from src/main/java/sciwhiz12/janitor/config/BotConfig.java rename to core/src/main/java/sciwhiz12/janitor/config/BotConfigImpl.java index 37d8d9d..39a28ad 100644 --- a/src/main/java/sciwhiz12/janitor/config/BotConfig.java +++ b/core/src/main/java/sciwhiz12/janitor/config/BotConfigImpl.java @@ -4,16 +4,18 @@ import com.electronwill.nightconfig.core.file.CommentedFileConfig; import com.electronwill.nightconfig.core.file.FileNotFoundAction; import com.electronwill.nightconfig.core.file.FileWatcher; import com.electronwill.nightconfig.toml.TomlFormat; +import com.google.common.base.Preconditions; +import sciwhiz12.janitor.api.config.BotConfig; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; import javax.annotation.Nullable; -import static sciwhiz12.janitor.Logging.CONFIG; -import static sciwhiz12.janitor.Logging.JANITOR; +import static sciwhiz12.janitor.api.Logging.CONFIG; +import static sciwhiz12.janitor.api.Logging.JANITOR; -public class BotConfig { +public class BotConfigImpl implements BotConfig { public static final Path DEFAULT_CONFIG_PATH = Path.of("config.toml"); private final CommentedConfigSpec.ConfigValue CLIENT_TOKEN; @@ -33,7 +35,7 @@ public class BotConfig { private final CommentedConfigSpec spec; private final CommentedFileConfig config; - public BotConfig(BotOptions options) { + public BotConfigImpl(BotOptions options) { this.options = options; final CommentedConfigSpec.Builder builder = new CommentedConfigSpec.Builder(); @@ -86,12 +88,16 @@ public class BotConfig { } catch (IOException ex) { JANITOR.error("Error while building config from file {}", configPath, ex); } + Preconditions.checkArgument(!getToken().isEmpty(), "Supply a client token through config or command line"); + Preconditions.checkArgument(options.getConfigsFolder(). + or(() -> Optional.ofNullable(Path.of(CONFIGS_PATH.get()))).isPresent(), "No guilds config folder defined"); } public CommentedFileConfig getRawConfig() { return config; } + @Override @Nullable public Path getMessagesFolder() { return options.getMessagesFolder(). @@ -101,25 +107,25 @@ public class BotConfig { .orElse(null); } - @Nullable + @Override public Path getConfigsFolder() { return options.getConfigsFolder(). - or(() -> CONFIGS_PATH.get().isBlank() ? - Optional.empty() : - Optional.of(Path.of(CONFIGS_PATH.get()))) - .orElse(null); + orElseGet(() -> Path.of(CONFIGS_PATH.get())); } + @Override public String getToken() { return options.getToken().orElse(CLIENT_TOKEN.get()); } + @Override public String getCommandPrefix() { return options.getCommandPrefix().orElseGet(COMMAND_PREFIX::get); } + @Override public Optional getOwnerID() { - final Long ret = options.getOwnerID().orElse(OWNER_ID.get()); + final long ret = options.getOwnerID().orElse(OWNER_ID.get()); if (ret == 0) return Optional.empty(); return Optional.of(ret); } diff --git a/src/main/java/sciwhiz12/janitor/config/BotOptions.java b/core/src/main/java/sciwhiz12/janitor/config/BotOptions.java similarity index 100% rename from src/main/java/sciwhiz12/janitor/config/BotOptions.java rename to core/src/main/java/sciwhiz12/janitor/config/BotOptions.java diff --git a/src/main/java/sciwhiz12/janitor/config/CommentedConfigSpec.java b/core/src/main/java/sciwhiz12/janitor/config/CommentedConfigSpec.java similarity index 99% rename from src/main/java/sciwhiz12/janitor/config/CommentedConfigSpec.java rename to core/src/main/java/sciwhiz12/janitor/config/CommentedConfigSpec.java index 4236b1b..1a0e955 100644 --- a/src/main/java/sciwhiz12/janitor/config/CommentedConfigSpec.java +++ b/core/src/main/java/sciwhiz12/janitor/config/CommentedConfigSpec.java @@ -32,7 +32,7 @@ import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.collect.Lists; import com.google.common.collect.ObjectArrays; -import sciwhiz12.janitor.Logging; +import sciwhiz12.janitor.api.Logging; import sciwhiz12.janitor.utils.Pair; import java.util.ArrayList; @@ -60,7 +60,7 @@ import static com.google.common.base.Preconditions.checkState; * Like {@link com.electronwill.nightconfig.core.ConfigSpec} except in builder format, and extended to accept comments. * * @author @MinecraftForge - * @author SciWhiz12 (modified to remove unneede parts) + * @author SciWhiz12 (modified to remove unneeded parts) */ //TODO: Remove extends and pipe everything through getSpec/getValues? public class CommentedConfigSpec extends UnmodifiableConfigWrapper { diff --git a/core/src/main/java/sciwhiz12/janitor/config/ConfigManagerImpl.java b/core/src/main/java/sciwhiz12/janitor/config/ConfigManagerImpl.java new file mode 100644 index 0000000..7fb991b --- /dev/null +++ b/core/src/main/java/sciwhiz12/janitor/config/ConfigManagerImpl.java @@ -0,0 +1,72 @@ +package sciwhiz12.janitor.config; + +import com.electronwill.nightconfig.core.CommentedConfig; +import sciwhiz12.janitor.JanitorBotImpl; +import sciwhiz12.janitor.api.config.ConfigManager; +import sciwhiz12.janitor.api.config.ConfigNode; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static sciwhiz12.janitor.api.config.CoreConfigs.COMMAND_PREFIX; +import static sciwhiz12.janitor.api.config.CoreConfigs.ENABLE; + +public class ConfigManagerImpl implements ConfigManager { + private final JanitorBotImpl bot; + private final Path configPath; + private final Map configMap = new HashMap<>(); + private final List> configNodes = new ArrayList<>(); + + public ConfigManagerImpl(JanitorBotImpl bot, Path configPath) { + this.bot = bot; + this.configPath = configPath; + registerNodes(ENABLE, COMMAND_PREFIX); + } + + @Override + public GuildConfigImpl get(long guildID) { + final GuildConfigImpl config = configMap.computeIfAbsent(guildID, (id) -> new GuildConfigImpl(id, getFile(guildID))); + configNodes.forEach(config::forGuild); // Ensures the config is correct + return config; + } + + @Override + public JanitorBotImpl getBot() { + return bot; + } + + @Override + public void save() { + configMap.values().forEach(GuildConfigImpl::save); + } + + @Override + public void registerNode(ConfigNode node) { + configNodes.add(node); + } + + @Override + public void close() { + configMap.values().forEach(GuildConfigImpl::close); + for (Iterator iterator = configMap.values().iterator(); iterator.hasNext(); ) { + iterator.next().close(); + iterator.remove(); + } + } + + private Path getFile(long guildID) { + final Path file = Path.of(Long.toHexString(guildID) + ".toml"); + return configPath.resolve(file); + } + + static void ensureComment(CommentedConfig config, String path, String expectedComment) { + if (!Objects.equals(config.getComment(path), expectedComment)) { + config.setComment(path, expectedComment); + } + } +} diff --git a/core/src/main/java/sciwhiz12/janitor/config/GuildConfigImpl.java b/core/src/main/java/sciwhiz12/janitor/config/GuildConfigImpl.java new file mode 100644 index 0000000..70f7081 --- /dev/null +++ b/core/src/main/java/sciwhiz12/janitor/config/GuildConfigImpl.java @@ -0,0 +1,140 @@ +package sciwhiz12.janitor.config; + +import com.electronwill.nightconfig.core.CommentedConfig; +import com.electronwill.nightconfig.core.file.CommentedFileConfig; +import com.electronwill.nightconfig.core.file.FileNotFoundAction; +import com.electronwill.nightconfig.core.file.FileWatcher; +import com.electronwill.nightconfig.toml.TomlFormat; +import com.google.common.base.Joiner; +import net.dv8tion.jda.api.entities.GuildChannel; +import sciwhiz12.janitor.api.config.ConfigNode; +import sciwhiz12.janitor.api.config.GuildConfig; + +import java.io.IOException; +import java.nio.file.Path; + +import static sciwhiz12.janitor.api.Logging.CONFIG; +import static sciwhiz12.janitor.api.Logging.JANITOR; +import static sciwhiz12.janitor.config.ConfigManagerImpl.ensureComment; + +public class GuildConfigImpl implements GuildConfig { + private static final Joiner NEWLINE = Joiner.on("\n"); + + private final long guild; + private final CommentedFileConfig config; + private boolean closed = false; + + GuildConfigImpl(long guild, Path configPath) { + this.guild = guild; + this.config = CommentedFileConfig.builder(configPath, TomlFormat.instance()) + .onFileNotFound(FileNotFoundAction.CREATE_EMPTY) + .preserveInsertionOrder() + .autosave() + .build(); + try { + CONFIG.info("Building guild config for {} from {}", Long.toHexString(this.guild), configPath); + config.load(); + FileWatcher.defaultInstance().addWatch(configPath, this::onFileChange); + // ConfigNode.nodes.forEach(this::forGuild); + save(); + } catch (IOException ex) { + JANITOR.error("Error while building config from file {}", configPath, ex); + } + } + + @Override + public CommentedConfig getChannelOverrides() { + final String channelOverridesID = "channel_overrides"; + CommentedConfig channelConfigs = config.get(channelOverridesID); + if (channelConfigs == null) { + channelConfigs = config.createSubConfig(); + config.set(channelOverridesID, channelConfigs); + config.setComment(channelOverridesID, "Channel overrides for certain configuration options"); + } + return channelConfigs; + } + + @Override + public CommentedConfig getChannelConfig(GuildChannel channel) { + final String id = channel.getId(); + CommentedConfig overrides = getChannelOverrides(); + CommentedConfig channelOverride = overrides.get(id); + if (channelOverride == null) { + channelOverride = overrides.createSubConfig(); + overrides.set(id, channelOverride); + overrides.setComment(id, "Channel overrides for channel with name " + channel.getName()); + } + return channelOverride; + } + + @Override + public T forGuild(ConfigNode node) { + ensureComment(config, node.path(), node.comment()); + T value = config.get(node.path()); + if (value == null) { + value = node.defaultValue().get(); + config.set(node.path(), value); + } + return value; + } + + @Override + public void forGuild(ConfigNode node, T newValue) { + ensureComment(config, node.path(), node.comment()); + config.set(node.path(), newValue); + } + + @Override + public T forChannel(GuildChannel channel, ConfigNode node) { + CommentedConfig channelConfig = getChannelConfig(channel); + ensureComment(channelConfig, node.path(), node.comment()); + T value = channelConfig.getRaw(node.path()); + if (value == null) { + value = node.defaultValue().get(); + channelConfig.set(node.path(), node.defaultValue().get()); + } + return value; + } + + @Override + public void forChannel(GuildChannel channel, ConfigNode node, T newValue) { + CommentedConfig channelConfig = getChannelConfig(channel); + ensureComment(channelConfig, node.path(), node.comment()); + channelConfig.set(node.path(), newValue); + } + + @Override + public long getGuildID() { + return guild; + } + + @Override + public CommentedFileConfig getRawConfig() { + return config; + } + + @Override + public void save() { + if (!closed) { + config.save(); + } + } + + @Override + public void close() { + if (!closed) { + closed = true; + config.close(); + } + } + + void onFileChange() { + if (closed) return; + try { + CONFIG.info("Reloading config due to file change {}", config.getNioPath()); + config.load(); + } catch (Exception ex) { + CONFIG.error("Error while reloading config from {}", config.getNioPath(), ex); + } + } +} diff --git a/src/main/java/sciwhiz12/janitor/msg/ListingMessageBuilder.java b/core/src/main/java/sciwhiz12/janitor/messages/ListingMessageBuilder.java similarity index 54% rename from src/main/java/sciwhiz12/janitor/msg/ListingMessageBuilder.java rename to core/src/main/java/sciwhiz12/janitor/messages/ListingMessageBuilder.java index f8fb969..b51daca 100644 --- a/src/main/java/sciwhiz12/janitor/msg/ListingMessageBuilder.java +++ b/core/src/main/java/sciwhiz12/janitor/messages/ListingMessageBuilder.java @@ -1,30 +1,37 @@ -package sciwhiz12.janitor.msg; +package sciwhiz12.janitor.messages; import com.google.common.collect.ImmutableList; +import com.google.common.primitives.Ints; +import joptsimple.internal.Strings; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.entities.MessageEmbed; -import sciwhiz12.janitor.JanitorBot; -import sciwhiz12.janitor.msg.json.ListingMessage; -import sciwhiz12.janitor.msg.substitution.CustomSubstitutions; -import sciwhiz12.janitor.msg.substitution.ICustomSubstitutions; -import sciwhiz12.janitor.msg.substitution.SubstitutionMap; +import net.dv8tion.jda.api.entities.Role; +import sciwhiz12.janitor.api.JanitorBot; +import sciwhiz12.janitor.api.messages.ListingMessage; +import sciwhiz12.janitor.api.messages.substitution.ModifiableSubstitutor; +import sciwhiz12.janitor.api.messages.substitution.SubstitutionsMap; +import sciwhiz12.janitor.api.messages.substitution.Substitutor; +import sciwhiz12.janitor.messages.substitution.CustomSubstitutions; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; -public class ListingMessageBuilder implements ICustomSubstitutions> { +public class ListingMessageBuilder implements ListingMessage.Builder { private final ListingMessage message; private final Map> customSubstitutions; private int amountPerPage = 6; - private BiConsumer entryApplier = (entry, sub) -> {}; + private BiConsumer> entryApplier = (entry, sub) -> {}; public ListingMessageBuilder(ListingMessage message, Map> customSubstitutions) { this.message = message; @@ -40,12 +47,12 @@ public class ListingMessageBuilder implements ICustomSubstitutions setEntryApplier(BiConsumer entryApplier) { + public ListingMessageBuilder setEntryApplier(BiConsumer> entryApplier) { this.entryApplier = entryApplier; return this; } - public ListingMessageBuilder apply(Consumer> consumer) { + public ListingMessageBuilder apply(Consumer> consumer) { consumer.accept(this); return this; } @@ -56,16 +63,16 @@ public class ListingMessageBuilder implements ICustomSubstitutions entries) { - final CustomSubstitutions customSubs = globalSubstitutions.with(customSubstitutions); + final ModifiableSubstitutor customSubs = globalSubstitutions.with(customSubstitutions); final ImmutableList list = ImmutableList.copyOf(entries); final PagedMessage pagedMessage = new PagedMessage(message, list, amountPerPage); channel.sendMessage(pagedMessage.createMessage(customSubs, entryApplier)) - .queue(listMsg -> globalSubstitutions.getBot().getReactionManager().newMessage(listMsg) + .queue(listMsg -> globalSubstitutions.getBot().getReactions().newMessage(listMsg) .owner(triggerMessage.getAuthor().getIdLong()) .removeEmotes(true) .add("\u2b05", (msg, event) -> { // PREVIOUS @@ -138,11 +145,12 @@ public class ListingMessageBuilder implements ICustomSubstitutions applier) { + public MessageEmbed createMessage(ModifiableSubstitutor substitutions, + BiConsumer> applier) { if (currentPage != lastPage) { - cachedMessage = message.create( - substitutions.with(new HashMap<>()) + cachedMessage = create( + message, + substitutions .with("page.max", () -> String.valueOf(maxPages + 1)) .with("page.current", () -> String.valueOf(currentPage + 1)), list.stream() @@ -159,4 +167,72 @@ public class ListingMessageBuilder implements ICustomSubstitutions EmbedBuilder create( + ListingMessage message, + Substitutor global, + Iterable iterable, + BiConsumer> entryApplier + ) { + final EmbedBuilder builder = new EmbedBuilder(); + builder.setTitle(global.substitute(message.getTitle()), global.substitute(message.getUrl())); + builder.setColor(parseColor(global.substitute(message.getColor()))); + builder.setAuthor(global.substitute(message.getAuthorName()), global.substitute(message.getAuthorUrl()), + global.substitute(message.getAuthorIconUrl())); + builder.setDescription(global.substitute(message.getDescription())); + builder.setImage(global.substitute(message.getImageUrl())); + builder.setThumbnail(global.substitute(message.getThumbnailUrl())); + builder.setTimestamp(OffsetDateTime.now(ZoneOffset.UTC)); + builder.setFooter(global.substitute(message.getFooterText()), global.substitute(message.getFooterIconUrl())); + for (MessageEmbed.Field field : message.getBeforeFields()) { + builder.addField(global.substitute(field.getName()), global.substitute(field.getValue()), field.isInline()); + } + + final ListingMessage.Entry entry = message.getEntry(); + + final CustomSubstitutions entrySubs = new CustomSubstitutions(); + final Function entryFunc = str -> str != null ? entrySubs.substitute(global.substitute(str)) : null; + int count = 0; + for (T listEntry : iterable) { + entryApplier.accept(listEntry, entrySubs); + if (entry instanceof ListingMessage.FieldEntry) { + ListingMessage.FieldEntry fieldEntry = (ListingMessage.FieldEntry) entry; + builder.addField( + entryFunc.apply(fieldEntry.getFieldName()), + entryFunc.apply(fieldEntry.getFieldValue()), + fieldEntry.isInline() + ); + } else if (entry instanceof ListingMessage.DescriptionEntry) { + ListingMessage.DescriptionEntry descEntry = (ListingMessage.DescriptionEntry) entry; + builder.getDescriptionBuilder().append(entryFunc.apply(descEntry.getDescription())); + builder.getDescriptionBuilder().append(descEntry.getJoiner()); + } + count++; + } + if (count < 1) { + builder.getDescriptionBuilder().append(global.substitute(message.getEmptyText())); + } + + for (MessageEmbed.Field field : message.getAfterFields()) { + builder.addField(global.substitute(field.getName()), global.substitute(field.getValue()), field.isInline()); + } + return builder; + } + + private static int parseColor(String str) { + if (Strings.isNullOrEmpty(str)) return Role.DEFAULT_COLOR_RAW; + if (str.startsWith("0x")) { + // noinspection UnstableApiUsage + final Integer res = Ints.tryParse(str.substring(2), 16); + if (res != null) { + return res; + } + } + // noinspection UnstableApiUsage + final Integer res = Ints.tryParse(str, 10); + if (res != null) { + return res; + } + return Role.DEFAULT_COLOR_RAW; + } } diff --git a/core/src/main/java/sciwhiz12/janitor/messages/MessagesImpl.java b/core/src/main/java/sciwhiz12/janitor/messages/MessagesImpl.java new file mode 100644 index 0000000..dcf1a31 --- /dev/null +++ b/core/src/main/java/sciwhiz12/janitor/messages/MessagesImpl.java @@ -0,0 +1,159 @@ +package sciwhiz12.janitor.messages; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import sciwhiz12.janitor.JanitorBotImpl; +import sciwhiz12.janitor.api.messages.ListingMessage; +import sciwhiz12.janitor.api.messages.Messages; +import sciwhiz12.janitor.api.messages.RegularMessage; +import sciwhiz12.janitor.messages.json.ListingMessageDeserializer; +import sciwhiz12.janitor.messages.json.RegularMessageDeserializer; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static sciwhiz12.janitor.api.Logging.JANITOR; +import static sciwhiz12.janitor.api.Logging.MESSAGES; + +public class MessagesImpl implements Messages { + public static final String JSON_FILE_SUFFIX = ".json"; + public static final String MESSAGE_LIST_FILENAME = "messages.json"; + public static final String MESSAGES_FOLDER = "messages/"; + public static final TypeReference> LIST_TYPE = new TypeReference<>() {}; + + private final JanitorBotImpl bot; + + private final Map defaultRegularMessages = new HashMap<>(); + private final Map defaultListingMessages = new HashMap<>(); + private final Map customRegularMessages = new HashMap<>(); + private final Map customListingMessages = new HashMap<>(); + private final ObjectMapper jsonMapper = new ObjectMapper(); + + public MessagesImpl(JanitorBotImpl bot) { + this.bot = bot; + SimpleModule messageModule = new SimpleModule(); + messageModule.addDeserializer(ListingMessage.class, new ListingMessageDeserializer()); + messageModule.addDeserializer(RegularMessage.class, new RegularMessageDeserializer()); + jsonMapper.registerModule(messageModule); + loadMessages(); + } + + public JanitorBotImpl getBot() { + return bot; + } + + public void loadMessages() { + ClassLoader ctxLoader = Thread.currentThread().getContextClassLoader(); + + JANITOR.info(MESSAGES, "Loading default messages"); + defaultListingMessages.clear(); + ctxLoader.resources(MESSAGES_FOLDER + MESSAGE_LIST_FILENAME) + .forEach(url -> { + JANITOR.info("Loading messages from {}", url.getPath()); + try (Reader keyReader = new InputStreamReader(url.openStream())) { + int loadedCount = 0; + for (String messageKey : jsonMapper.readValue(keyReader, LIST_TYPE)) { + InputStream resourceStream = ctxLoader + .getResourceAsStream(MESSAGES_FOLDER + messageKey + JSON_FILE_SUFFIX); + if (resourceStream == null) { + JANITOR.warn("Defined message {} cannot be found", messageKey); + continue; + } + loadedCount += readMessage(messageKey, + () -> new InputStreamReader(resourceStream), + defaultRegularMessages, + defaultListingMessages); + } + JANITOR.debug(MESSAGES, "Loaded {} messages", loadedCount); + } catch (Exception e) { + JANITOR.error(MESSAGES, "Error while loading default messages from {}", url.getPath(), e); + } + }); + + Path messagesFolder = bot.getBotConfig().getMessagesFolder(); + if (messagesFolder != null) { + JANITOR.info(MESSAGES, "Loading custom messages from folder {}", messagesFolder); + try (Reader keyReader = Files.newBufferedReader(messagesFolder.resolve(MESSAGE_LIST_FILENAME))) { + int loadedCount = 0; + for (String messageKey : jsonMapper.readValue(keyReader, LIST_TYPE)) { + final Path messagePath = messagesFolder.resolve(messageKey + JSON_FILE_SUFFIX); + if (Files.notExists(messagePath)) { + JANITOR.warn("Defined message {} cannot be found", messageKey); + continue; + } + readMessage(messageKey, () -> Files.newBufferedReader(messagePath), customRegularMessages, + customListingMessages); + loadedCount++; + } + JANITOR.debug(MESSAGES, "Loaded {} messages", loadedCount); + } catch (Exception e) { + JANITOR.error(MESSAGES, "Error while loading custom messages", e); + } + } else { + JANITOR.info(MESSAGES, "No custom messages folder specified"); + } + } + + @FunctionalInterface + interface ThrowableSupplier { + T get() throws E; + } + + private int readMessage(String messageKey, ThrowableSupplier input, + Map regularMessages, Map listingMessages) { + try { + final JsonNode tree = jsonMapper.readTree(input.get()); + final String type = tree.path("type").asText("regular"); + switch (type) { + case "regular": { + regularMessages.put(messageKey, jsonMapper.convertValue(tree, RegularMessage.class)); + break; + } + case "listing": { + listingMessages.put(messageKey, jsonMapper.convertValue(tree, ListingMessage.class)); + break; + } + default: { + JANITOR.warn(MESSAGES, "Unknown message type {} for {}", tree.path("type").asText(), messageKey); + return 0; + } + } + return 1; + } catch (Exception e) { + JANITOR.error(MESSAGES, "Error while loading message {}", messageKey, e); + return 0; + } + } + + public RegularMessageBuilder getRegularMessage(String messageKey) { + RegularMessage msg = customRegularMessages.get(messageKey); + if (msg == null) { + msg = defaultRegularMessages.get(messageKey); + } + if (msg == null) { + JANITOR.warn(MESSAGES, "Attempted to get unknown regular message with key {}", messageKey); + return new RegularMessageBuilder(UNKNOWN_REGULAR_MESSAGE).with("key", () -> messageKey); + } + return new RegularMessageBuilder(msg); + } + + public ListingMessageBuilder getListingMessage(String messageKey) { + ListingMessage msg = customListingMessages.get(messageKey); + if (msg == null) { + msg = defaultListingMessages.get(messageKey); + } + if (msg == null) { + JANITOR.warn(MESSAGES, "Attempted to get unknown listing message with key {}", messageKey); + return new ListingMessageBuilder(UNKNOWN_LISTING_MESSAGE).with("key", () -> messageKey); + } + return new ListingMessageBuilder<>(msg); + } +} diff --git a/core/src/main/java/sciwhiz12/janitor/messages/RegularMessageBuilder.java b/core/src/main/java/sciwhiz12/janitor/messages/RegularMessageBuilder.java new file mode 100644 index 0000000..a590d7d --- /dev/null +++ b/core/src/main/java/sciwhiz12/janitor/messages/RegularMessageBuilder.java @@ -0,0 +1,93 @@ +package sciwhiz12.janitor.messages; + +import com.google.common.primitives.Ints; +import joptsimple.internal.Strings; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.MessageChannel; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.requests.restaction.MessageAction; +import sciwhiz12.janitor.JanitorBotImpl; +import sciwhiz12.janitor.api.JanitorBot; +import sciwhiz12.janitor.api.messages.RegularMessage; +import sciwhiz12.janitor.api.messages.substitution.SubstitutionsMap; +import sciwhiz12.janitor.api.messages.substitution.Substitutor; + +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class RegularMessageBuilder implements RegularMessage.Builder { + private final RegularMessage message; + private final Map> customSubstitutions; + + public RegularMessageBuilder(RegularMessage message, Map> customSubstitutions) { + this.message = message; + this.customSubstitutions = customSubstitutions; + } + + public RegularMessageBuilder(RegularMessage message) { + this(message, new HashMap<>()); + } + + public RegularMessageBuilder apply(Consumer consumer) { + consumer.accept(this); + return this; + } + + public RegularMessageBuilder with(final String argument, final Supplier value) { + customSubstitutions.put(argument, value); + return this; + } + + @Override + public MessageEmbed build(SubstitutionsMap substitutions) { + return create(message, substitutions.with(customSubstitutions)).build(); + } + + @Override + public MessageEmbed build(JanitorBot bot) { + return build(bot.getSubstitutions()); + } + + public MessageAction send(JanitorBotImpl bot, MessageChannel channel) { + return channel.sendMessage(build(bot)); + } + + public static EmbedBuilder create(RegularMessage message, Substitutor subs) { + final EmbedBuilder builder = new EmbedBuilder(); + builder.setTitle(subs.substitute(message.getTitle()), subs.substitute(message.getUrl())); + builder.setColor(parseColor(subs.substitute(message.getColor()))); + builder.setAuthor(subs.substitute(message.getAuthorName()), subs.substitute(message.getAuthorUrl()), subs.substitute( + message.getAuthorIconUrl())); + builder.setDescription(subs.substitute(message.getDescription())); + builder.setImage(subs.substitute(message.getImageUrl())); + builder.setThumbnail(subs.substitute(message.getThumbnailUrl())); + builder.setTimestamp(OffsetDateTime.now(ZoneOffset.UTC)); + builder.setFooter(subs.substitute(message.getFooterText()), subs.substitute(message.getFooterIconUrl())); + for (MessageEmbed.Field field : message.getFields()) { + builder.addField(subs.substitute(field.getName()), subs.substitute(field.getValue()), field.isInline()); + } + return builder; + } + + private static int parseColor(String str) { + if (Strings.isNullOrEmpty(str)) return Role.DEFAULT_COLOR_RAW; + if (str.startsWith("0x")) { + // noinspection UnstableApiUsage + final Integer res = Ints.tryParse(str.substring(2), 16); + if (res != null) { + return res; + } + } + // noinspection UnstableApiUsage + final Integer res = Ints.tryParse(str, 10); + if (res != null) { + return res; + } + return Role.DEFAULT_COLOR_RAW; + } +} diff --git a/src/main/java/sciwhiz12/janitor/msg/emote/ReactionManager.java b/core/src/main/java/sciwhiz12/janitor/messages/emote/ReactionManagerImpl.java similarity index 64% rename from src/main/java/sciwhiz12/janitor/msg/emote/ReactionManager.java rename to core/src/main/java/sciwhiz12/janitor/messages/emote/ReactionManagerImpl.java index 17204a8..edb37e7 100644 --- a/src/main/java/sciwhiz12/janitor/msg/emote/ReactionManager.java +++ b/core/src/main/java/sciwhiz12/janitor/messages/emote/ReactionManagerImpl.java @@ -1,27 +1,29 @@ -package sciwhiz12.janitor.msg.emote; +package sciwhiz12.janitor.messages.emote; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.events.message.MessageDeleteEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; -import sciwhiz12.janitor.JanitorBot; +import sciwhiz12.janitor.JanitorBotImpl; +import sciwhiz12.janitor.api.messages.emote.ReactionManager; +import sciwhiz12.janitor.api.messages.emote.ReactionMessage; import java.util.HashMap; import java.util.Map; import javax.annotation.Nonnull; -public class ReactionManager extends ListenerAdapter { - private final JanitorBot bot; +public class ReactionManagerImpl extends ListenerAdapter implements ReactionManager { + private final JanitorBotImpl bot; private final Map messageMap = new HashMap<>(); - public ReactionManager(JanitorBot bot) { + public ReactionManagerImpl(JanitorBotImpl bot) { this.bot = bot; } - public ReactionMessage newMessage(Message message) { + public ReactionMessageImpl newMessage(Message message) { if (messageMap.containsKey(message.getIdLong())) { throw new IllegalArgumentException("Reaction message already exists for message with id " + message.getIdLong()); } - final ReactionMessage msg = new ReactionMessage(bot, message); + final ReactionMessageImpl msg = new ReactionMessageImpl(bot, message); messageMap.put(message.getIdLong(), msg); return msg; } @@ -34,6 +36,11 @@ public class ReactionManager extends ListenerAdapter { return messageMap; } + @Override + public JanitorBotImpl getBot() { + return bot; + } + @Override public void onMessageDelete(@Nonnull MessageDeleteEvent event) { if (messageMap.containsKey(event.getMessageIdLong())) { diff --git a/src/main/java/sciwhiz12/janitor/msg/emote/ReactionMessage.java b/core/src/main/java/sciwhiz12/janitor/messages/emote/ReactionMessageImpl.java similarity index 71% rename from src/main/java/sciwhiz12/janitor/msg/emote/ReactionMessage.java rename to core/src/main/java/sciwhiz12/janitor/messages/emote/ReactionMessageImpl.java index 6d4e924..a7c4887 100644 --- a/src/main/java/sciwhiz12/janitor/msg/emote/ReactionMessage.java +++ b/core/src/main/java/sciwhiz12/janitor/messages/emote/ReactionMessageImpl.java @@ -1,4 +1,4 @@ -package sciwhiz12.janitor.msg.emote; +package sciwhiz12.janitor.messages.emote; import net.dv8tion.jda.api.entities.Emote; import net.dv8tion.jda.api.entities.Message; @@ -7,54 +7,54 @@ import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; import net.dv8tion.jda.api.exceptions.ErrorHandler; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.requests.ErrorResponse; -import sciwhiz12.janitor.JanitorBot; +import sciwhiz12.janitor.JanitorBotImpl; +import sciwhiz12.janitor.api.messages.emote.ReactionMessage; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; -import java.util.function.BiConsumer; import javax.annotation.Nonnull; import static net.dv8tion.jda.api.Permission.MESSAGE_MANAGE; -public class ReactionMessage extends ListenerAdapter { - private final JanitorBot bot; +public class ReactionMessageImpl extends ListenerAdapter implements ReactionMessage { + private final JanitorBotImpl bot; private final Message message; - private final Map emotes = new LinkedHashMap<>(); + private final Map emotes = new LinkedHashMap<>(); private boolean removeEmotes = true; private long ownerID; private boolean onlyOwner; - public ReactionMessage(JanitorBot bot, Message message, boolean onlyOwner, long ownerID) { + public ReactionMessageImpl(JanitorBotImpl bot, Message message, boolean onlyOwner, long ownerID) { this.bot = bot; this.message = message; this.ownerID = ownerID; this.onlyOwner = onlyOwner; } - public ReactionMessage(JanitorBot bot, Message message) { + public ReactionMessageImpl(JanitorBotImpl bot, Message message) { this(bot, message, false, 0); } - public ReactionMessage add(ReactionEmote emote, IReactionListener listener) { + public ReactionMessageImpl add(ReactionEmote emote, ReactionListener listener) { emotes.put(emote, listener); return this; } - public ReactionMessage add(String emote, IReactionListener listener) { + public ReactionMessageImpl add(String emote, ReactionListener listener) { return add(ReactionEmote.fromUnicode(emote, bot.getDiscord()), listener); } - public ReactionMessage add(Emote emote, IReactionListener listener) { + public ReactionMessageImpl add(Emote emote, ReactionListener listener) { return add(ReactionEmote.fromCustom(emote), listener); } - public ReactionMessage removeEmotes(boolean remove) { + public ReactionMessageImpl removeEmotes(boolean remove) { this.removeEmotes = remove; return this; } - public ReactionMessage owner(long ownerID) { + public ReactionMessageImpl owner(long ownerID) { this.ownerID = ownerID; this.onlyOwner = true; return this; @@ -89,7 +89,7 @@ public class ReactionMessage extends ListenerAdapter { } } - public JanitorBot getBot() { + public JanitorBotImpl getBot() { return bot; } @@ -101,12 +101,7 @@ public class ReactionMessage extends ListenerAdapter { return onlyOwner; } - public Map getListeners() { + public Map getListeners() { return Collections.unmodifiableMap(emotes); } - - @FunctionalInterface - public interface IReactionListener extends BiConsumer { - void accept(ReactionMessage message, MessageReactionAddEvent event); - } } diff --git a/src/main/java/sciwhiz12/janitor/msg/json/ListingMessageDeserializer.java b/core/src/main/java/sciwhiz12/janitor/messages/json/ListingMessageDeserializer.java similarity index 97% rename from src/main/java/sciwhiz12/janitor/msg/json/ListingMessageDeserializer.java rename to core/src/main/java/sciwhiz12/janitor/messages/json/ListingMessageDeserializer.java index 8e5c69b..e94d538 100644 --- a/src/main/java/sciwhiz12/janitor/msg/json/ListingMessageDeserializer.java +++ b/core/src/main/java/sciwhiz12/janitor/messages/json/ListingMessageDeserializer.java @@ -1,4 +1,4 @@ -package sciwhiz12.janitor.msg.json; +package sciwhiz12.janitor.messages.json; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.MessageEmbed; +import sciwhiz12.janitor.api.messages.ListingMessage; import java.io.IOException; import java.util.ArrayList; diff --git a/src/main/java/sciwhiz12/janitor/msg/json/RegularMessageDeserializer.java b/core/src/main/java/sciwhiz12/janitor/messages/json/RegularMessageDeserializer.java similarity index 97% rename from src/main/java/sciwhiz12/janitor/msg/json/RegularMessageDeserializer.java rename to core/src/main/java/sciwhiz12/janitor/messages/json/RegularMessageDeserializer.java index 36857e3..536de5e 100644 --- a/src/main/java/sciwhiz12/janitor/msg/json/RegularMessageDeserializer.java +++ b/core/src/main/java/sciwhiz12/janitor/messages/json/RegularMessageDeserializer.java @@ -1,10 +1,11 @@ -package sciwhiz12.janitor.msg.json; +package sciwhiz12.janitor.messages.json; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import net.dv8tion.jda.api.entities.MessageEmbed; +import sciwhiz12.janitor.api.messages.RegularMessage; import java.io.IOException; import java.util.ArrayList; diff --git a/src/main/java/sciwhiz12/janitor/msg/substitution/CustomSubstitutions.java b/core/src/main/java/sciwhiz12/janitor/messages/substitution/CustomSubstitutions.java similarity index 79% rename from src/main/java/sciwhiz12/janitor/msg/substitution/CustomSubstitutions.java rename to core/src/main/java/sciwhiz12/janitor/messages/substitution/CustomSubstitutions.java index a69f90e..7ab9b88 100644 --- a/src/main/java/sciwhiz12/janitor/msg/substitution/CustomSubstitutions.java +++ b/core/src/main/java/sciwhiz12/janitor/messages/substitution/CustomSubstitutions.java @@ -1,7 +1,9 @@ -package sciwhiz12.janitor.msg.substitution; +package sciwhiz12.janitor.messages.substitution; import org.apache.commons.collections4.TransformerUtils; import org.apache.commons.collections4.map.DefaultedMap; +import sciwhiz12.janitor.api.messages.substitution.ModifiableSubstitutor; +import sciwhiz12.janitor.api.messages.substitution.SubstitutionsMap; import java.util.HashMap; import java.util.Map; @@ -9,7 +11,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; import javax.annotation.Nullable; -public class CustomSubstitutions implements ISubstitutor, ICustomSubstitutions { +public class CustomSubstitutions implements ModifiableSubstitutor { private final Map> map; public CustomSubstitutions(Map> map) { @@ -23,7 +25,7 @@ public class CustomSubstitutions implements ISubstitutor, ICustomSubstitutions consumer) { diff --git a/src/main/java/sciwhiz12/janitor/msg/substitution/SubstitutionMap.java b/core/src/main/java/sciwhiz12/janitor/messages/substitution/SubstitutionsMapImpl.java similarity index 82% rename from src/main/java/sciwhiz12/janitor/msg/substitution/SubstitutionMap.java rename to core/src/main/java/sciwhiz12/janitor/messages/substitution/SubstitutionsMapImpl.java index 8b95d27..24d37ff 100644 --- a/src/main/java/sciwhiz12/janitor/msg/substitution/SubstitutionMap.java +++ b/core/src/main/java/sciwhiz12/janitor/messages/substitution/SubstitutionsMapImpl.java @@ -1,8 +1,9 @@ -package sciwhiz12.janitor.msg.substitution; +package sciwhiz12.janitor.messages.substitution; import org.apache.commons.collections4.TransformerUtils; import org.apache.commons.collections4.map.DefaultedMap; -import sciwhiz12.janitor.JanitorBot; +import sciwhiz12.janitor.JanitorBotImpl; +import sciwhiz12.janitor.api.messages.substitution.SubstitutionsMap; import java.time.OffsetDateTime; import java.time.ZoneOffset; @@ -14,9 +15,9 @@ import java.util.regex.Pattern; import javax.annotation.Nullable; import static java.util.regex.Pattern.CASE_INSENSITIVE; -import static sciwhiz12.janitor.msg.MessageHelper.DATE_TIME_FORMAT; +import static sciwhiz12.janitor.api.utils.MessageHelper.DATE_TIME_FORMAT; -public class SubstitutionMap implements ISubstitutor { +public class SubstitutionsMapImpl implements SubstitutionsMap { public static final Pattern ARGUMENT_REGEX = Pattern.compile("\\$\\{(.+?)}", CASE_INSENSITIVE); public static final Pattern NULL_ARGUMENT_REGEX = Pattern.compile("nullcheck;(.+?);(.+)", CASE_INSENSITIVE); @@ -41,10 +42,10 @@ public class SubstitutionMap implements ISubstitutor { }); } - private final JanitorBot bot; + private final JanitorBotImpl bot; private final Map> defaultSubstitutions = new HashMap<>(); - public SubstitutionMap(JanitorBot bot) { + public SubstitutionsMapImpl(JanitorBotImpl bot) { this.bot = bot; defaultSubstitutions.put("time.now", () -> OffsetDateTime.now(ZoneOffset.UTC).format(DATE_TIME_FORMAT)); defaultSubstitutions.put("moderation.color", () -> "0xF1BD25"); @@ -53,17 +54,17 @@ public class SubstitutionMap implements ISubstitutor { defaultSubstitutions.put("general.error.color", () -> "0xF73132"); } - public JanitorBot getBot() { + public JanitorBotImpl getBot() { return bot; } @Nullable public String substitute(@Nullable String text) { - return SubstitutionMap.substitute(text, defaultSubstitutions); + return SubstitutionsMap.substitute(text, defaultSubstitutions); } public String with(String text, Map> substitutions) { - return SubstitutionMap.substitute(text, createDefaultedMap(substitutions)); + return SubstitutionsMap.substitute(text, createDefaultedMap(substitutions)); } public CustomSubstitutions with(Map> customSubstitutions) { @@ -73,5 +74,4 @@ public class SubstitutionMap implements ISubstitutor { public Map> createDefaultedMap(Map> custom) { return DefaultedMap.defaultedMap(custom, TransformerUtils.mapTransformer(defaultSubstitutions)); } - } diff --git a/core/src/main/java/sciwhiz12/janitor/module/ModuleManagerImpl.java b/core/src/main/java/sciwhiz12/janitor/module/ModuleManagerImpl.java new file mode 100644 index 0000000..722b36e --- /dev/null +++ b/core/src/main/java/sciwhiz12/janitor/module/ModuleManagerImpl.java @@ -0,0 +1,158 @@ +package sciwhiz12.janitor.module; + +import com.google.common.collect.ImmutableSet; +import sciwhiz12.janitor.JanitorBotImpl; +import sciwhiz12.janitor.api.module.Module; +import sciwhiz12.janitor.api.module.ModuleKey; +import sciwhiz12.janitor.api.module.ModuleManager; +import sciwhiz12.janitor.api.module.ModuleProvider; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.Set; +import javax.annotation.Nullable; + +import static sciwhiz12.janitor.api.Logging.JANITOR; +import static sciwhiz12.janitor.api.Logging.MODULE; + +public class ModuleManagerImpl implements ModuleManager { + private final JanitorBotImpl bot; + private final ServiceLoader moduleProviders; + private boolean active = false; + private final Set> availableModules = new HashSet<>(); + private final Set disabledModules = new HashSet<>(); + private final Map> activeModules = new HashMap<>(); + + public ModuleManagerImpl(JanitorBotImpl bot) { + this.bot = bot; + this.moduleProviders = ServiceLoader.load(ModuleProvider.class); + loadProviders(); + } + + public void loadProviders() { + final Set knownModules = new HashSet<>(); + moduleProviders.reload(); + availableModules.clear(); + for (ModuleProvider provider : moduleProviders) { + for (ModuleKey moduleID : provider.getAvailableModules()) { + if (knownModules.contains(moduleID.getModuleID())) + throw new RuntimeException("Duplicate modules with module id " + moduleID); + availableModules.add(moduleID); + knownModules.add(moduleID.getModuleID()); + } + } + } + + public void activateModules() { + if (active) + throw new IllegalStateException("Modules are already activated"); + active = true; + JANITOR.debug(MODULE, "Activating modules..."); + for (ModuleProvider provider : moduleProviders) { + for (ModuleKey moduleID : provider.getAvailableModules()) { + String providerName = provider.getClass().getName(); + if (disabledModules.contains(moduleID.getModuleID())) { + JANITOR.debug(MODULE, "Module with ID {} from provider {} is disabled, skipping", moduleID, providerName); + } else if (!availableModules.contains(moduleID)) { + JANITOR + .debug(MODULE, "Module with ID {} from provider {} was not previously available at loading, skipping", + moduleID, providerName); + } else { + if (activateModule(provider, moduleID)) { + JANITOR.debug(MODULE, "Module with ID {} from provider {} is now activated", moduleID, providerName); + } else { + JANITOR.warn(MODULE, + "Module with ID {} was declared to be available by provider {}, but did not create a module; " + + "skipping", + moduleID, providerName); + } + + } + } + } + JANITOR.info(MODULE, "Modules are now activated"); + } + + private boolean activateModule(ModuleProvider provider, ModuleKey moduleID) { + final M module = provider.createModule(moduleID, bot); + if (module == null) { + return false; + } + module.activate(); + activeModules.put(moduleID.getModuleID(), new InnerStorage<>(moduleID, module)); + return true; + } + + public void shutdown() { + if (!active) + throw new IllegalStateException("Modules are not activated"); + } + + @Override + public void disableModule(String id) { + if (active) + throw new IllegalStateException("Cannot disable modules, as modules are already activated"); + disabledModules.add(id); + } + + @Override + public void enableModule(String id) { + if (active) + throw new IllegalStateException("Cannot reenable modules, as modules are already activated"); + disabledModules.remove(id); + } + + @Override + public Set> getAvailableModules() { + return Collections.unmodifiableSet(availableModules); + } + + @Override + public Set> getActiveModules() { + return activeModules.values().stream() + .map(InnerStorage::getKey) + .collect(ImmutableSet.toImmutableSet()); + } + + @Nullable + @Override + public M getModule(ModuleKey moduleKey) { + if (activeModules.containsKey(moduleKey.getModuleID())) { + return moduleKey.getType().cast(activeModules.get(moduleKey.getModuleID()).module); + } + return null; + } + + public boolean isActivated() { + return active; + } + + @Override + public JanitorBotImpl getBot() { + return bot; + } + + /** + * For internal use only. + */ + static class InnerStorage { + private final ModuleKey key; + private final M module; + + InnerStorage(ModuleKey key, M storage) { + this.key = key; + this.module = storage; + } + + public ModuleKey getKey() { + return key; + } + + public M getModule() { + return module; + } + } +} diff --git a/src/main/java/sciwhiz12/janitor/storage/GuildStorage.java b/core/src/main/java/sciwhiz12/janitor/storage/GuildStorageManagerImpl.java similarity index 81% rename from src/main/java/sciwhiz12/janitor/storage/GuildStorage.java rename to core/src/main/java/sciwhiz12/janitor/storage/GuildStorageManagerImpl.java index 8ac6fca..fb74af0 100644 --- a/src/main/java/sciwhiz12/janitor/storage/GuildStorage.java +++ b/core/src/main/java/sciwhiz12/janitor/storage/GuildStorageManagerImpl.java @@ -2,8 +2,11 @@ package sciwhiz12.janitor.storage; import com.google.common.base.Preconditions; import net.dv8tion.jda.api.entities.Guild; -import sciwhiz12.janitor.JanitorBot; -import sciwhiz12.janitor.Logging; +import sciwhiz12.janitor.JanitorBotImpl; +import sciwhiz12.janitor.api.Logging; +import sciwhiz12.janitor.api.storage.GuildStorageManager; +import sciwhiz12.janitor.api.storage.Storage; +import sciwhiz12.janitor.api.storage.StorageKey; import java.io.IOException; import java.io.Reader; @@ -19,26 +22,24 @@ import static java.nio.file.StandardOpenOption.*; /** * A storage system for guild-specific data. */ -public class GuildStorage { - private final JanitorBot bot; +public class GuildStorageManagerImpl implements GuildStorageManager { + private final JanitorBotImpl bot; private final Path mainFolder; private final Map>> guildStorages = new HashMap<>(); - public GuildStorage(JanitorBot bot, Path mainFolder) { + public GuildStorageManagerImpl(JanitorBotImpl bot, Path mainFolder) { Preconditions.checkArgument(Files.isDirectory(mainFolder) || Files.notExists(mainFolder)); this.bot = bot; this.mainFolder = mainFolder; } - public JanitorBot getBot() { + @Override + public JanitorBotImpl getBot() { return bot; } - public S getOrCreate(Guild guild, StorageKey key, Supplier defaultSupplier) { - return getOrCreate(guild.getIdLong(), key, defaultSupplier); - } - - public S getOrCreate(long guildID, StorageKey key, Supplier defaultSupplier) { + @Override + public S getOrCreate(long guildID, StorageKey key, Supplier defaultSupplier) { final Map> storageMappy = guildStorages.computeIfAbsent(guildID, id -> new HashMap<>()); return key.getType().cast(storageMappy.computeIfAbsent(key.getStorageID(), k -> new InnerStorage<>(key, load(guildID, key.getStorageID(), defaultSupplier.get()))).getStorage()); @@ -50,7 +51,7 @@ public class GuildStorage { return mainFolder.resolve(guildFolder).resolve(file); } - public T load(long guildID, String key, T storage) { + public T load(long guildID, String key, T storage) { final Path file = getFile(guildID, key); if (Files.notExists(file)) return storage; @@ -63,6 +64,7 @@ public class GuildStorage { return storage; } + @Override public void save() { save(false); } @@ -96,13 +98,13 @@ public class GuildStorage { } /** - * A thread that calls {@link GuildStorage#save(boolean)} between specified delays. + * A thread that calls {@link GuildStorageManagerImpl#save(boolean)} between specified delays. */ public static class SavingThread extends Thread { - private final GuildStorage storage; + private final GuildStorageManagerImpl storage; private volatile boolean running = true; - public SavingThread(GuildStorage storage) { + public SavingThread(GuildStorageManagerImpl storage) { this.storage = storage; this.setName("GuildStorage-Saving-Thread"); this.setDaemon(true); @@ -118,7 +120,7 @@ public class GuildStorage { while (running) { storage.save(true); try { - Thread.sleep(storage.getBot().getConfig().AUTOSAVE_INTERVAL.get() * 1000); + Thread.sleep(storage.getBot().getBotConfig().AUTOSAVE_INTERVAL.get() * 1000); } catch (InterruptedException ignored) {} } } @@ -127,7 +129,7 @@ public class GuildStorage { /** * For internal use only. */ - static class InnerStorage { + static class InnerStorage { private final StorageKey key; private final S storage; diff --git a/src/main/java/sciwhiz12/janitor/utils/Pair.java b/core/src/main/java/sciwhiz12/janitor/utils/Pair.java similarity index 100% rename from src/main/java/sciwhiz12/janitor/utils/Pair.java rename to core/src/main/java/sciwhiz12/janitor/utils/Pair.java diff --git a/src/main/java/sciwhiz12/janitor/utils/Util.java b/core/src/main/java/sciwhiz12/janitor/utils/Util.java similarity index 92% rename from src/main/java/sciwhiz12/janitor/utils/Util.java rename to core/src/main/java/sciwhiz12/janitor/utils/Util.java index fe22818..bc2a9e5 100644 --- a/src/main/java/sciwhiz12/janitor/utils/Util.java +++ b/core/src/main/java/sciwhiz12/janitor/utils/Util.java @@ -21,7 +21,7 @@ public class Util { public static String toString(@Nullable final User user) { return user != null ? - String.format("{User,%s#%s}:%s", user.getName(), user.getDiscriminator(), getID(user)) : + String.format("{User,%s#%s}:%s", user.getName(), user.getDiscriminator(), user.getAsMention()) : "unknown"; } @@ -51,10 +51,6 @@ public class Util { return String.format("<%s%s>", prefix, entity.getId()); } - public static String nameFor(User user) { - return user.getName().concat("#").concat(user.getDiscriminator()); - } - public static BiConsumer handle(final Consumer success, final Consumer exceptionally) { return (suc, ex) -> { diff --git a/src/main/resources/messages/general/error/ambiguous_member.json b/core/src/main/resources/messages/general/error/ambiguous_member.json similarity index 100% rename from src/main/resources/messages/general/error/ambiguous_member.json rename to core/src/main/resources/messages/general/error/ambiguous_member.json diff --git a/src/main/resources/messages/general/error/cannot_action_performer.json b/core/src/main/resources/messages/general/error/cannot_action_performer.json similarity index 100% rename from src/main/resources/messages/general/error/cannot_action_performer.json rename to core/src/main/resources/messages/general/error/cannot_action_performer.json diff --git a/src/main/resources/messages/general/error/cannot_action_self.json b/core/src/main/resources/messages/general/error/cannot_action_self.json similarity index 100% rename from src/main/resources/messages/general/error/cannot_action_self.json rename to core/src/main/resources/messages/general/error/cannot_action_self.json diff --git a/src/main/resources/messages/general/error/cannot_interact.json b/core/src/main/resources/messages/general/error/cannot_interact.json similarity index 100% rename from src/main/resources/messages/general/error/cannot_interact.json rename to core/src/main/resources/messages/general/error/cannot_interact.json diff --git a/src/main/resources/messages/general/error/guild_only_command.json b/core/src/main/resources/messages/general/error/guild_only_command.json similarity index 100% rename from src/main/resources/messages/general/error/guild_only_command.json rename to core/src/main/resources/messages/general/error/guild_only_command.json diff --git a/src/main/resources/messages/general/error/insufficient_permissions.json b/core/src/main/resources/messages/general/error/insufficient_permissions.json similarity index 100% rename from src/main/resources/messages/general/error/insufficient_permissions.json rename to core/src/main/resources/messages/general/error/insufficient_permissions.json diff --git a/core/src/main/resources/messages/messages.json b/core/src/main/resources/messages/messages.json new file mode 100644 index 0000000..d2f6d88 --- /dev/null +++ b/core/src/main/resources/messages/messages.json @@ -0,0 +1,8 @@ +[ + "general/error/ambiguous_member", + "general/error/guild_only_command", + "general/error/insufficient_permissions", + "general/error/cannot_interact", + "general/error/cannot_action_self", + "general/error/cannot_action_performer" +] \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6ce793f..562e2c8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/moderation/src/api/java/sciwhiz12/janitor/moderation/ModerationConfigs.java b/moderation/src/api/java/sciwhiz12/janitor/moderation/ModerationConfigs.java new file mode 100644 index 0000000..b5144cc --- /dev/null +++ b/moderation/src/api/java/sciwhiz12/janitor/moderation/ModerationConfigs.java @@ -0,0 +1,27 @@ +package sciwhiz12.janitor.moderation; + +import sciwhiz12.janitor.api.config.ConfigNode; + +public class ModerationConfigs { + public static final ConfigNode ENABLE_WARNS = new ConfigNode<>( + "sciwhiz12.janitor.moderation.warns.enable", () -> true, + "Whether to enable the warnings system. If disabled, the related commands are force-disabled."); + + public static final ConfigNode WARNS_RESPECT_MOD_ROLES = new ConfigNode<>( + "sciwhiz12.janitor.moderation.warns.respect_mod_roles", () -> false, + "Whether to prevent lower-ranked moderators (in the role hierarchy) from removing warnings " + + "issued by higher-ranked moderators."); + public static final ConfigNode ALLOW_WARN_OTHER_MODERATORS = new ConfigNode<>( + "sciwhiz12.janitor.moderation.warns.warn_other_moderators", () -> true, + "Whether to allow moderators to issue warnings against other moderators."); + public static final ConfigNode ALLOW_REMOVE_SELF_WARNINGS = new ConfigNode<>( + "sciwhiz12.janitor.moderation.warns.remove_self_warnings", () -> false, + "Whether to allow moderators to remove warnings from themselves."); + + public static final ConfigNode ENABLE_NOTES = new ConfigNode<>( + "sciwhiz12.janitor.moderation.notes.enable", () -> true, + "Whether to enable the notes system. If disabled, the related commands are force-disabled."); + public static final ConfigNode MAX_NOTES_PER_MOD = new ConfigNode<>( + "sciwhiz12.janitor.moderation.notes.max_amount", () -> Integer.MAX_VALUE, + "The max amount of notes for a user per moderator."); +} diff --git a/moderation/src/api/java/sciwhiz12/janitor/moderation/ModerationHelper.java b/moderation/src/api/java/sciwhiz12/janitor/moderation/ModerationHelper.java new file mode 100644 index 0000000..0b18c0d --- /dev/null +++ b/moderation/src/api/java/sciwhiz12/janitor/moderation/ModerationHelper.java @@ -0,0 +1,64 @@ +package sciwhiz12.janitor.moderation; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.requests.restaction.AuditableRestAction; +import sciwhiz12.janitor.api.messages.substitution.ModifiableSubstitutions; +import sciwhiz12.janitor.moderation.notes.NoteEntry; +import sciwhiz12.janitor.moderation.warns.WarningEntry; + +import java.time.Instant; +import java.time.ZoneOffset; +import java.util.function.Consumer; +import javax.annotation.Nullable; + +import static sciwhiz12.janitor.api.utils.MessageHelper.DATE_TIME_FORMAT; +import static sciwhiz12.janitor.api.utils.MessageHelper.user; + +public class ModerationHelper { + public static AuditableRestAction kickUser(Guild guild, Member performer, Member target, @Nullable String reason) { + StringBuilder auditReason = new StringBuilder(); + auditReason.append("Kicked by ") + .append(performer.getUser().getAsTag()) + .append(" on ") + .append(Instant.now().atOffset(ZoneOffset.UTC).format(DATE_TIME_FORMAT)); + if (reason != null) + auditReason.append(" for reason: ").append(reason); + return guild.kick(target, auditReason.toString()); + } + + public static AuditableRestAction banUser(Guild guild, Member performer, Member target, int deleteDuration, + @Nullable String reason) { + StringBuilder auditReason = new StringBuilder(); + auditReason.append("Banned by ") + .append(performer.getUser().getAsTag()) + .append(" on ") + .append(Instant.now().atOffset(ZoneOffset.UTC).format(DATE_TIME_FORMAT)); + if (reason != null) + auditReason.append(" for reason: ").append(reason); + return guild.ban(target, deleteDuration, auditReason.toString()); + } + + public static AuditableRestAction unbanUser(Guild guild, User target) { + return guild.unban(target); + } + + public static > Consumer warningEntry(String head, int caseID, WarningEntry entry) { + return builder -> builder + .with(head + ".case_id", () -> String.valueOf(caseID)) + .apply(user(head + ".performer", entry.getPerformer())) + .apply(user(head + ".target", entry.getWarned())) + .with(head + ".date_time", () -> entry.getDateTime().format(DATE_TIME_FORMAT)) + .with(head + ".reason", entry::getReason); + } + + public static > Consumer noteEntry(String head, int noteID, NoteEntry entry) { + return builder -> builder + .with(head + ".note_id", () -> String.valueOf(noteID)) + .apply(user(head + ".performer", entry.getPerformer())) + .apply(user(head + ".target", entry.getTarget())) + .with(head + ".date_time", () -> entry.getDateTime().format(DATE_TIME_FORMAT)) + .with(head + ".contents", entry::getContents); + } +} diff --git a/moderation/src/api/java/sciwhiz12/janitor/moderation/ModerationModule.java b/moderation/src/api/java/sciwhiz12/janitor/moderation/ModerationModule.java new file mode 100644 index 0000000..b3bdf13 --- /dev/null +++ b/moderation/src/api/java/sciwhiz12/janitor/moderation/ModerationModule.java @@ -0,0 +1,18 @@ +package sciwhiz12.janitor.moderation; + +import net.dv8tion.jda.api.entities.Guild; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sciwhiz12.janitor.api.module.Module; +import sciwhiz12.janitor.api.module.ModuleKey; +import sciwhiz12.janitor.moderation.notes.NoteStorage; +import sciwhiz12.janitor.moderation.warns.WarningStorage; + +public interface ModerationModule extends Module { + Logger LOGGER = LoggerFactory.getLogger("janitor.moderation"); + ModuleKey ID = new ModuleKey<>("moderation", ModerationModule.class); + + NoteStorage getNotes(Guild guild); + + WarningStorage getWarns(Guild guild); +} diff --git a/moderation/src/api/java/sciwhiz12/janitor/moderation/notes/NoteEntry.java b/moderation/src/api/java/sciwhiz12/janitor/moderation/notes/NoteEntry.java new file mode 100644 index 0000000..b5339a4 --- /dev/null +++ b/moderation/src/api/java/sciwhiz12/janitor/moderation/notes/NoteEntry.java @@ -0,0 +1,53 @@ +package sciwhiz12.janitor.moderation.notes; + +import net.dv8tion.jda.api.entities.User; + +import java.time.OffsetDateTime; +import java.util.Objects; + +public class NoteEntry { + private final User performer; + private final User target; + private final OffsetDateTime dateTime; + private final String contents; + + public NoteEntry(User performer, User target, OffsetDateTime dateTime, String contents) { + this.performer = performer; + this.target = target; + this.dateTime = dateTime; + this.contents = contents; + } + + public User getPerformer() { + return performer; + } + + public User getTarget() { + return target; + } + + public OffsetDateTime getDateTime() { + return dateTime; + } + + public String getContents() { + return contents; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NoteEntry noteEntry = (NoteEntry) o; + return getPerformer().equals(noteEntry.getPerformer()) && + getTarget().equals(noteEntry.getTarget()) && + getDateTime().equals(noteEntry.getDateTime()) && + getContents().equals(noteEntry.getContents()); + } + + @Override + public int hashCode() { + return Objects.hash(getPerformer(), getTarget(), getDateTime(), getContents()); + } + +} diff --git a/moderation/src/api/java/sciwhiz12/janitor/moderation/notes/NoteStorage.java b/moderation/src/api/java/sciwhiz12/janitor/moderation/notes/NoteStorage.java new file mode 100644 index 0000000..9c7de0a --- /dev/null +++ b/moderation/src/api/java/sciwhiz12/janitor/moderation/notes/NoteStorage.java @@ -0,0 +1,25 @@ +package sciwhiz12.janitor.moderation.notes; + +import net.dv8tion.jda.api.entities.User; +import org.checkerframework.checker.nullness.qual.Nullable; +import sciwhiz12.janitor.api.JanitorBot; +import sciwhiz12.janitor.api.storage.Storage; +import sciwhiz12.janitor.api.storage.StorageKey; + +import java.util.Map; + +public interface NoteStorage extends Storage { + StorageKey KEY = new StorageKey<>("notes", NoteStorage.class); + + int addNote(NoteEntry entry); + + @Nullable NoteEntry getNote(int noteID); + + void removeNote(int noteID); + + int getAmountOfNotes(User target); + + Map getNotes(); + + JanitorBot getBot(); +} diff --git a/moderation/src/api/java/sciwhiz12/janitor/moderation/warns/WarningEntry.java b/moderation/src/api/java/sciwhiz12/janitor/moderation/warns/WarningEntry.java new file mode 100644 index 0000000..6fd7b19 --- /dev/null +++ b/moderation/src/api/java/sciwhiz12/janitor/moderation/warns/WarningEntry.java @@ -0,0 +1,56 @@ +package sciwhiz12.janitor.moderation.warns; + +import net.dv8tion.jda.api.entities.User; + +import java.time.OffsetDateTime; +import java.util.Objects; +import javax.annotation.Nullable; + +public class WarningEntry { + private final User performer; + private final User warned; + private final OffsetDateTime dateTime; + @Nullable + private final String reason; + + public WarningEntry(User performer, User warned, OffsetDateTime dateTime, @Nullable String reason) { + this.performer = performer; + this.warned = warned; + this.dateTime = dateTime; + this.reason = reason; + } + + public User getPerformer() { + return performer; + } + + public User getWarned() { + return warned; + } + + public OffsetDateTime getDateTime() { + return dateTime; + } + + @Nullable + public String getReason() { + return reason; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WarningEntry that = (WarningEntry) o; + return getPerformer().equals(that.getPerformer()) && + getWarned().equals(that.getWarned()) && + getDateTime().equals(that.getDateTime()) && + Objects.equals(getReason(), that.getReason()); + } + + @Override + public int hashCode() { + return Objects.hash(getPerformer(), getWarned(), getDateTime(), getReason()); + } + +} diff --git a/moderation/src/api/java/sciwhiz12/janitor/moderation/warns/WarningStorage.java b/moderation/src/api/java/sciwhiz12/janitor/moderation/warns/WarningStorage.java new file mode 100644 index 0000000..d9e59bd --- /dev/null +++ b/moderation/src/api/java/sciwhiz12/janitor/moderation/warns/WarningStorage.java @@ -0,0 +1,22 @@ +package sciwhiz12.janitor.moderation.warns; + +import org.checkerframework.checker.nullness.qual.Nullable; +import sciwhiz12.janitor.api.JanitorBot; +import sciwhiz12.janitor.api.storage.Storage; +import sciwhiz12.janitor.api.storage.StorageKey; + +import java.util.Map; + +public interface WarningStorage extends Storage { + StorageKey KEY = new StorageKey<>("warnings", WarningStorage.class); + + JanitorBot getBot(); + + int addWarning(WarningEntry entry); + + @Nullable WarningEntry getWarning(int caseID); + + void removeWarning(int caseID); + + Map getWarnings(); +} diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/BanCommand.java b/moderation/src/main/java/sciwhiz12/janitor/moderation/BanCommand.java similarity index 87% rename from src/main/java/sciwhiz12/janitor/commands/moderation/BanCommand.java rename to moderation/src/main/java/sciwhiz12/janitor/moderation/BanCommand.java index 58da11d..8ba39bd 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/BanCommand.java +++ b/moderation/src/main/java/sciwhiz12/janitor/moderation/BanCommand.java @@ -1,4 +1,4 @@ -package sciwhiz12.janitor.commands.moderation; +package sciwhiz12.janitor.moderation; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; @@ -8,10 +8,8 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import sciwhiz12.janitor.commands.BaseCommand; -import sciwhiz12.janitor.commands.CommandRegistry; -import sciwhiz12.janitor.commands.util.ModerationHelper; -import sciwhiz12.janitor.msg.MessageHelper; +import sciwhiz12.janitor.api.command.CommandRegistry; +import sciwhiz12.janitor.api.utils.MessageHelper; import java.util.EnumSet; import java.util.List; @@ -22,22 +20,16 @@ import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger; import static com.mojang.brigadier.arguments.IntegerArgumentType.integer; import static com.mojang.brigadier.arguments.StringArgumentType.getString; import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; -import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.getMembers; -import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.member; -import static sciwhiz12.janitor.commands.util.CommandHelper.argument; -import static sciwhiz12.janitor.commands.util.CommandHelper.literal; +import static sciwhiz12.janitor.api.command.arguments.GuildMemberArgument.getMembers; +import static sciwhiz12.janitor.api.command.arguments.GuildMemberArgument.member; +import static sciwhiz12.janitor.api.utils.CommandHelper.argument; +import static sciwhiz12.janitor.api.utils.CommandHelper.literal; -public class BanCommand extends BaseCommand { +public class BanCommand extends ModBaseCommand { public static final EnumSet BAN_PERMISSION = EnumSet.of(Permission.BAN_MEMBERS); - /* - ban command - !ban [reason] - !ban delete [reason] - */ - - public BanCommand(CommandRegistry registry) { - super(registry); + public BanCommand(ModerationModuleImpl module, CommandRegistry registry) { + super(module, registry); } @Override diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java b/moderation/src/main/java/sciwhiz12/janitor/moderation/KickCommand.java similarity index 89% rename from src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java rename to moderation/src/main/java/sciwhiz12/janitor/moderation/KickCommand.java index e2cb129..ea565b1 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/KickCommand.java +++ b/moderation/src/main/java/sciwhiz12/janitor/moderation/KickCommand.java @@ -1,4 +1,4 @@ -package sciwhiz12.janitor.commands.moderation; +package sciwhiz12.janitor.moderation; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; @@ -8,11 +8,9 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import sciwhiz12.janitor.commands.BaseCommand; -import sciwhiz12.janitor.commands.CommandRegistry; -import sciwhiz12.janitor.commands.util.CommandHelper; -import sciwhiz12.janitor.commands.util.ModerationHelper; -import sciwhiz12.janitor.msg.MessageHelper; +import sciwhiz12.janitor.api.command.CommandRegistry; +import sciwhiz12.janitor.api.utils.CommandHelper; +import sciwhiz12.janitor.api.utils.MessageHelper; import java.util.EnumSet; import java.util.List; @@ -21,14 +19,14 @@ import javax.annotation.Nullable; import static com.mojang.brigadier.arguments.StringArgumentType.getString; import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; -import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.getMembers; -import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.member; +import static sciwhiz12.janitor.api.command.arguments.GuildMemberArgument.getMembers; +import static sciwhiz12.janitor.api.command.arguments.GuildMemberArgument.member; -public class KickCommand extends BaseCommand { +public class KickCommand extends ModBaseCommand { public static final EnumSet KICK_PERMISSION = EnumSet.of(Permission.KICK_MEMBERS); - public KickCommand(CommandRegistry registry) { - super(registry); + public KickCommand(ModerationModuleImpl module, CommandRegistry registry) { + super(module, registry); } @Override diff --git a/moderation/src/main/java/sciwhiz12/janitor/moderation/ModBaseCommand.java b/moderation/src/main/java/sciwhiz12/janitor/moderation/ModBaseCommand.java new file mode 100644 index 0000000..4b0e4eb --- /dev/null +++ b/moderation/src/main/java/sciwhiz12/janitor/moderation/ModBaseCommand.java @@ -0,0 +1,24 @@ +package sciwhiz12.janitor.moderation; + +import net.dv8tion.jda.api.entities.Guild; +import sciwhiz12.janitor.api.command.BaseCommand; +import sciwhiz12.janitor.api.command.CommandRegistry; +import sciwhiz12.janitor.moderation.notes.NoteStorage; +import sciwhiz12.janitor.moderation.warns.WarningStorage; + +public abstract class ModBaseCommand extends BaseCommand { + protected ModerationModuleImpl module; + + public ModBaseCommand(ModerationModuleImpl module, CommandRegistry registry) { + super(registry); + this.module = module; + } + + protected NoteStorage getNotes(Guild guild) { + return module.getNotes(guild); + } + + protected WarningStorage getWarns(Guild guild) { + return module.getWarns(guild); + } +} diff --git a/moderation/src/main/java/sciwhiz12/janitor/moderation/ModerationModuleImpl.java b/moderation/src/main/java/sciwhiz12/janitor/moderation/ModerationModuleImpl.java new file mode 100644 index 0000000..5ec3bd9 --- /dev/null +++ b/moderation/src/main/java/sciwhiz12/janitor/moderation/ModerationModuleImpl.java @@ -0,0 +1,84 @@ +package sciwhiz12.janitor.moderation; + +import com.google.common.collect.ImmutableSet; +import net.dv8tion.jda.api.entities.Guild; +import sciwhiz12.janitor.api.JanitorBot; +import sciwhiz12.janitor.api.module.Module; +import sciwhiz12.janitor.api.module.ModuleKey; +import sciwhiz12.janitor.api.module.ModuleProvider; +import sciwhiz12.janitor.moderation.notes.NoteCommand; +import sciwhiz12.janitor.moderation.notes.NoteStorage; +import sciwhiz12.janitor.moderation.notes.NoteStorageImpl; +import sciwhiz12.janitor.moderation.warns.UnwarnCommand; +import sciwhiz12.janitor.moderation.warns.WarnCommand; +import sciwhiz12.janitor.moderation.warns.WarnListCommand; +import sciwhiz12.janitor.moderation.warns.WarningStorage; +import sciwhiz12.janitor.moderation.warns.WarningStorageImpl; + +import java.util.Set; +import javax.annotation.Nullable; + +import static sciwhiz12.janitor.moderation.ModerationConfigs.*; + +public class ModerationModuleImpl implements ModerationModule { + private final JanitorBot bot; + + ModerationModuleImpl(JanitorBot bot) { + this.bot = bot; + } + + @Override + public void activate() { + bot.getCommands().addCommand(reg -> new KickCommand(this, reg)); + bot.getCommands().addCommand(reg -> new BanCommand(this, reg)); + bot.getCommands().addCommand(reg -> new UnbanCommand(this, reg)); + bot.getCommands().addCommand(reg -> new WarnCommand(this, reg)); + bot.getCommands().addCommand(reg -> new WarnListCommand(this, reg)); + bot.getCommands().addCommand(reg -> new UnwarnCommand(this, reg)); + bot.getCommands().addCommand(reg -> new NoteCommand(this, reg)); + bot.getConfigs().registerNodes( + ENABLE_WARNS, + WARNS_RESPECT_MOD_ROLES, + ALLOW_WARN_OTHER_MODERATORS, + ALLOW_REMOVE_SELF_WARNINGS, + ENABLE_NOTES, + MAX_NOTES_PER_MOD + ); + LOGGER.info("Moderation module is activated"); + } + + @Override + public void shutdown() {} + + @Override + public JanitorBot getBot() { + return bot; + } + + @Override + public NoteStorage getNotes(Guild guild) { + return bot.getGuildStorage().getOrCreate(guild, NoteStorage.KEY, () -> new NoteStorageImpl(bot)); + } + + @Override + public WarningStorage getWarns(Guild guild) { + return bot.getGuildStorage().getOrCreate(guild, WarningStorage.KEY, () -> new WarningStorageImpl(bot)); + } + + public static class Provider implements ModuleProvider { + @Override + public Set> getAvailableModules() { + return ImmutableSet.of(ID); + } + + @Nullable + @Override + public M createModule(ModuleKey moduleID, JanitorBot bot) { + if (ID.equals(moduleID)) { + //noinspection unchecked + return (M) new ModerationModuleImpl(bot); + } + return null; + } + } +} diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/UnbanCommand.java b/moderation/src/main/java/sciwhiz12/janitor/moderation/UnbanCommand.java similarity index 90% rename from src/main/java/sciwhiz12/janitor/commands/moderation/UnbanCommand.java rename to moderation/src/main/java/sciwhiz12/janitor/moderation/UnbanCommand.java index 7cdcfa8..6b2c1f8 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/UnbanCommand.java +++ b/moderation/src/main/java/sciwhiz12/janitor/moderation/UnbanCommand.java @@ -1,4 +1,4 @@ -package sciwhiz12.janitor.commands.moderation; +package sciwhiz12.janitor.moderation; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; @@ -9,10 +9,8 @@ import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import sciwhiz12.janitor.commands.BaseCommand; -import sciwhiz12.janitor.commands.CommandRegistry; -import sciwhiz12.janitor.commands.util.ModerationHelper; -import sciwhiz12.janitor.msg.MessageHelper; +import sciwhiz12.janitor.api.command.CommandRegistry; +import sciwhiz12.janitor.api.utils.MessageHelper; import java.util.EnumSet; import java.util.Locale; @@ -22,14 +20,14 @@ import java.util.stream.Collectors; import static com.mojang.brigadier.arguments.LongArgumentType.getLong; import static com.mojang.brigadier.arguments.LongArgumentType.longArg; import static com.mojang.brigadier.arguments.StringArgumentType.getString; -import static sciwhiz12.janitor.commands.util.CommandHelper.argument; -import static sciwhiz12.janitor.commands.util.CommandHelper.literal; +import static sciwhiz12.janitor.api.utils.CommandHelper.argument; +import static sciwhiz12.janitor.api.utils.CommandHelper.literal; -public class UnbanCommand extends BaseCommand { +public class UnbanCommand extends ModBaseCommand { public static final EnumSet UNBAN_PERMISSION = EnumSet.of(Permission.BAN_MEMBERS); - public UnbanCommand(CommandRegistry registry) { - super(registry); + public UnbanCommand(ModerationModuleImpl module, CommandRegistry registry) { + super(module, registry); } @Override diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java b/moderation/src/main/java/sciwhiz12/janitor/moderation/notes/NoteCommand.java similarity index 87% rename from src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java rename to moderation/src/main/java/sciwhiz12/janitor/moderation/notes/NoteCommand.java index 835ce11..549d8d7 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/NoteCommand.java +++ b/moderation/src/main/java/sciwhiz12/janitor/moderation/notes/NoteCommand.java @@ -1,4 +1,4 @@ -package sciwhiz12.janitor.commands.moderation; +package sciwhiz12.janitor.moderation.notes; import com.google.common.collect.ImmutableList; import com.mojang.brigadier.builder.LiteralArgumentBuilder; @@ -9,11 +9,11 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import sciwhiz12.janitor.commands.BaseCommand; -import sciwhiz12.janitor.commands.CommandRegistry; -import sciwhiz12.janitor.moderation.notes.NoteEntry; -import sciwhiz12.janitor.moderation.notes.NoteStorage; -import sciwhiz12.janitor.msg.MessageHelper; +import sciwhiz12.janitor.api.command.CommandRegistry; +import sciwhiz12.janitor.api.utils.MessageHelper; +import sciwhiz12.janitor.moderation.ModBaseCommand; +import sciwhiz12.janitor.moderation.ModerationHelper; +import sciwhiz12.janitor.moderation.ModerationModuleImpl; import java.time.OffsetDateTime; import java.time.ZoneOffset; @@ -29,20 +29,20 @@ import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger; import static com.mojang.brigadier.arguments.IntegerArgumentType.integer; import static com.mojang.brigadier.arguments.StringArgumentType.getString; import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; -import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.getMembers; -import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.member; -import static sciwhiz12.janitor.commands.moderation.NoteCommand.ModeratorFilter.*; -import static sciwhiz12.janitor.commands.util.CommandHelper.argument; -import static sciwhiz12.janitor.commands.util.CommandHelper.literal; -import static sciwhiz12.janitor.config.GuildConfig.ENABLE_NOTES; -import static sciwhiz12.janitor.config.GuildConfig.MAX_NOTES_PER_MOD; -import static sciwhiz12.janitor.msg.MessageHelper.*; +import static sciwhiz12.janitor.api.command.arguments.GuildMemberArgument.getMembers; +import static sciwhiz12.janitor.api.command.arguments.GuildMemberArgument.member; +import static sciwhiz12.janitor.api.utils.CommandHelper.argument; +import static sciwhiz12.janitor.api.utils.CommandHelper.literal; +import static sciwhiz12.janitor.api.utils.MessageHelper.user; +import static sciwhiz12.janitor.moderation.ModerationConfigs.ENABLE_NOTES; +import static sciwhiz12.janitor.moderation.ModerationConfigs.MAX_NOTES_PER_MOD; +import static sciwhiz12.janitor.moderation.notes.NoteCommand.ModeratorFilter.*; -public class NoteCommand extends BaseCommand { +public class NoteCommand extends ModBaseCommand { public static EnumSet NOTE_PERMISSION = EnumSet.of(Permission.KICK_MEMBERS); - public NoteCommand(CommandRegistry registry) { - super(registry); + public NoteCommand(ModerationModuleImpl module, CommandRegistry registry) { + super(module, registry); } @Override @@ -127,7 +127,8 @@ public class NoteCommand extends BaseCommand { .send(getBot(), channel).queue(); } else { - final NoteStorage storage = NoteStorage.get(getBot().getStorage(), guild); + final NoteStorage storage = getNotes(guild); + final int maxAmount = config(ctx.getSource()).forGuild(MAX_NOTES_PER_MOD); if (storage.getAmountOfNotes(target.getUser()) >= maxAmount) { messages().getRegularMessage("moderation/error/insufficient_permissions") @@ -142,7 +143,7 @@ public class NoteCommand extends BaseCommand { messages().getRegularMessage("moderation/note/add") .apply(MessageHelper.member("performer", performer)) - .apply(noteEntry("note_entry", noteID, entry)) + .apply(ModerationHelper.noteEntry("note_entry", noteID, entry)) .send(getBot(), channel).queue(); } @@ -204,10 +205,10 @@ public class NoteCommand extends BaseCommand { messages().>getListingMessage("moderation/note/list") .apply(MessageHelper.member("performer", performer)) .setEntryApplier((entry, subs) -> subs - .apply(noteEntry("note_entry", entry.getKey(), entry.getValue())) + .apply(ModerationHelper.noteEntry("note_entry", entry.getKey(), entry.getValue())) ) .build(channel, getBot(), ctx.getSource().getMessage(), - NoteStorage.get(getBot().getStorage(), guild) + getNotes(guild) .getNotes() .entrySet().stream() .filter(predicate) @@ -237,7 +238,7 @@ public class NoteCommand extends BaseCommand { .send(getBot(), channel).queue(); } else { - final NoteStorage storage = NoteStorage.get(getBot().getStorage(), guild); + final NoteStorage storage = getNotes(guild); @Nullable final NoteEntry entry = storage.getNote(noteID); if (entry == null) { @@ -251,7 +252,7 @@ public class NoteCommand extends BaseCommand { messages().getRegularMessage("moderation/note/remove") .apply(MessageHelper.member("performer", performer)) - .apply(noteEntry("note_entry", noteID, entry)) + .apply(ModerationHelper.noteEntry("note_entry", noteID, entry)) .send(getBot(), channel).queue(); } } diff --git a/moderation/src/main/java/sciwhiz12/janitor/moderation/notes/NoteEntryDeserializer.java b/moderation/src/main/java/sciwhiz12/janitor/moderation/notes/NoteEntryDeserializer.java new file mode 100644 index 0000000..ab9f876 --- /dev/null +++ b/moderation/src/main/java/sciwhiz12/janitor/moderation/notes/NoteEntryDeserializer.java @@ -0,0 +1,33 @@ +package sciwhiz12.janitor.moderation.notes; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import net.dv8tion.jda.api.entities.User; +import sciwhiz12.janitor.api.JanitorBot; + +import java.io.IOException; +import java.time.OffsetDateTime; +import java.util.function.Supplier; + +public class NoteEntryDeserializer extends StdDeserializer { + private static final long serialVersionUID = 1L; + + private final Supplier bot; + + public NoteEntryDeserializer(Supplier bot) { + super(NoteEntry.class); + this.bot = bot; + } + + @Override + public NoteEntry deserialize(JsonParser p, DeserializationContext ctx) throws IOException { + final JsonNode obj = ctx.readTree(p); + User performer = bot.get().getDiscord().retrieveUserById(obj.get("performer").asLong()).complete(); + User target = bot.get().getDiscord().retrieveUserById(obj.get("target").asLong()).complete(); + OffsetDateTime dateTime = OffsetDateTime.parse(obj.get("dateTime").asText()); + String contents = obj.get("contents").asText(); + return new NoteEntry(performer, target, dateTime, contents); + } +} diff --git a/moderation/src/main/java/sciwhiz12/janitor/moderation/notes/NoteEntrySerializer.java b/moderation/src/main/java/sciwhiz12/janitor/moderation/notes/NoteEntrySerializer.java new file mode 100644 index 0000000..d201e0e --- /dev/null +++ b/moderation/src/main/java/sciwhiz12/janitor/moderation/notes/NoteEntrySerializer.java @@ -0,0 +1,25 @@ +package sciwhiz12.janitor.moderation.notes; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; + +public class NoteEntrySerializer extends StdSerializer { + private static final long serialVersionUID = 1L; + + public NoteEntrySerializer() { + super(NoteEntry.class); + } + + @Override + public void serialize(NoteEntry value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("performer", value.getPerformer().getIdLong()); + gen.writeNumberField("target", value.getTarget().getIdLong()); + gen.writeStringField("dateTime", value.getDateTime().toString()); + gen.writeStringField("contents", value.getContents()); + gen.writeEndObject(); + } +} diff --git a/src/main/java/sciwhiz12/janitor/moderation/notes/NoteStorage.java b/moderation/src/main/java/sciwhiz12/janitor/moderation/notes/NoteStorageImpl.java similarity index 74% rename from src/main/java/sciwhiz12/janitor/moderation/notes/NoteStorage.java rename to moderation/src/main/java/sciwhiz12/janitor/moderation/notes/NoteStorageImpl.java index 78a583a..f1a1b38 100644 --- a/src/main/java/sciwhiz12/janitor/moderation/notes/NoteStorage.java +++ b/moderation/src/main/java/sciwhiz12/janitor/moderation/notes/NoteStorageImpl.java @@ -6,31 +6,23 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.node.ObjectNode; -import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.User; import org.checkerframework.checker.nullness.qual.Nullable; -import sciwhiz12.janitor.JanitorBot; -import sciwhiz12.janitor.storage.GuildStorage; -import sciwhiz12.janitor.storage.JsonStorage; -import sciwhiz12.janitor.storage.StorageKey; +import sciwhiz12.janitor.api.JanitorBot; +import sciwhiz12.janitor.api.storage.JsonStorage; import java.io.IOException; import java.util.HashMap; import java.util.Map; -public class NoteStorage extends JsonStorage { +public class NoteStorageImpl extends JsonStorage implements NoteStorage { private static final TypeReference> NOTE_MAP_TYPE = new TypeReference<>() {}; - public static final StorageKey KEY = new StorageKey<>("notes", NoteStorage.class); - - public static NoteStorage get(GuildStorage storage, Guild guild) { - return storage.getOrCreate(guild, KEY, () -> new NoteStorage(storage.getBot())); - } private final JanitorBot bot; private int lastID = 1; private final Map notes = new ObservedMap<>(new HashMap<>(), this::markDirty); - public NoteStorage(JanitorBot bot) { + public NoteStorageImpl(JanitorBot bot) { this.bot = bot; } @@ -68,8 +60,8 @@ public class NoteStorage extends JsonStorage { super.initialize(mapper); mapper.registerModule( new SimpleModule() - .addSerializer(NoteEntry.class, new NoteEntry.Serializer()) - .addDeserializer(NoteEntry.class, new NoteEntry.Deserializer(this::getBot)) + .addSerializer(NoteEntry.class, new NoteEntrySerializer()) + .addDeserializer(NoteEntry.class, new NoteEntryDeserializer(this::getBot)) ); } diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/UnwarnCommand.java b/moderation/src/main/java/sciwhiz12/janitor/moderation/warns/UnwarnCommand.java similarity index 77% rename from src/main/java/sciwhiz12/janitor/commands/moderation/UnwarnCommand.java rename to moderation/src/main/java/sciwhiz12/janitor/moderation/warns/UnwarnCommand.java index 0c44209..644f678 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/UnwarnCommand.java +++ b/moderation/src/main/java/sciwhiz12/janitor/moderation/warns/UnwarnCommand.java @@ -1,4 +1,4 @@ -package sciwhiz12.janitor.commands.moderation; +package sciwhiz12.janitor.moderation.warns; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; @@ -8,26 +8,25 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import sciwhiz12.janitor.commands.BaseCommand; -import sciwhiz12.janitor.commands.CommandRegistry; -import sciwhiz12.janitor.config.GuildConfig; -import sciwhiz12.janitor.moderation.warns.WarningEntry; -import sciwhiz12.janitor.moderation.warns.WarningStorage; -import sciwhiz12.janitor.msg.MessageHelper; +import sciwhiz12.janitor.api.command.CommandRegistry; +import sciwhiz12.janitor.api.utils.MessageHelper; +import sciwhiz12.janitor.moderation.ModBaseCommand; +import sciwhiz12.janitor.moderation.ModerationHelper; +import sciwhiz12.janitor.moderation.ModerationModuleImpl; import java.util.EnumSet; import java.util.Objects; import javax.annotation.Nullable; -import static sciwhiz12.janitor.commands.util.CommandHelper.argument; -import static sciwhiz12.janitor.commands.util.CommandHelper.literal; -import static sciwhiz12.janitor.config.GuildConfig.*; +import static sciwhiz12.janitor.api.utils.CommandHelper.argument; +import static sciwhiz12.janitor.api.utils.CommandHelper.literal; +import static sciwhiz12.janitor.moderation.ModerationConfigs.*; -public class UnwarnCommand extends BaseCommand { +public class UnwarnCommand extends ModBaseCommand { public static final EnumSet WARN_PERMISSION = EnumSet.of(Permission.KICK_MEMBERS); - public UnwarnCommand(CommandRegistry registry) { - super(registry); + public UnwarnCommand(ModerationModuleImpl module, CommandRegistry registry) { + super(module, registry); } @Override @@ -64,7 +63,7 @@ public class UnwarnCommand extends BaseCommand { .send(getBot(), channel).queue(); } else { - final WarningStorage storage = WarningStorage.get(getBot().getStorage(), guild); + final WarningStorage storage = getWarns(guild); @Nullable final WarningEntry entry = storage.getWarning(caseID); Member temp; @@ -78,21 +77,21 @@ public class UnwarnCommand extends BaseCommand { && !config(guild).forGuild(ALLOW_REMOVE_SELF_WARNINGS)) { messages().getRegularMessage("moderation/error/unwarn/cannot_unwarn_self") .apply(MessageHelper.member("performer", performer)) - .apply(MessageHelper.warningEntry("warning_entry", caseID, entry)) + .apply(ModerationHelper.warningEntry("warning_entry", caseID, entry)) .send(getBot(), channel).queue(); } else if (config(guild).forGuild(WARNS_RESPECT_MOD_ROLES) && (temp = guild.getMember(entry.getPerformer())) != null && !performer.canInteract(temp)) { messages().getRegularMessage("moderation/error/unwarn/cannot_remove_higher_mod") .apply(MessageHelper.member("performer", performer)) - .apply(MessageHelper.warningEntry("warning_entry", caseID, entry)) + .apply(ModerationHelper.warningEntry("warning_entry", caseID, entry)) .send(getBot(), channel).queue(); } else { storage.removeWarning(caseID); messages().getRegularMessage("moderation/unwarn/info") .apply(MessageHelper.member("performer", performer)) - .apply(MessageHelper.warningEntry("warning_entry", caseID, entry)) + .apply(ModerationHelper.warningEntry("warning_entry", caseID, entry)) .send(getBot(), channel).queue(); } diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/WarnCommand.java b/moderation/src/main/java/sciwhiz12/janitor/moderation/warns/WarnCommand.java similarity index 76% rename from src/main/java/sciwhiz12/janitor/commands/moderation/WarnCommand.java rename to moderation/src/main/java/sciwhiz12/janitor/moderation/warns/WarnCommand.java index 40f5b8e..b69be2a 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/WarnCommand.java +++ b/moderation/src/main/java/sciwhiz12/janitor/moderation/warns/WarnCommand.java @@ -1,4 +1,4 @@ -package sciwhiz12.janitor.commands.moderation; +package sciwhiz12.janitor.moderation.warns; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; @@ -8,12 +8,11 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import sciwhiz12.janitor.commands.BaseCommand; -import sciwhiz12.janitor.commands.CommandRegistry; -import sciwhiz12.janitor.config.GuildConfig; -import sciwhiz12.janitor.moderation.warns.WarningEntry; -import sciwhiz12.janitor.moderation.warns.WarningStorage; -import sciwhiz12.janitor.msg.MessageHelper; +import sciwhiz12.janitor.api.command.CommandRegistry; +import sciwhiz12.janitor.api.utils.MessageHelper; +import sciwhiz12.janitor.moderation.ModBaseCommand; +import sciwhiz12.janitor.moderation.ModerationHelper; +import sciwhiz12.janitor.moderation.ModerationModuleImpl; import java.time.OffsetDateTime; import java.time.ZoneOffset; @@ -23,23 +22,24 @@ import java.util.Objects; import static com.mojang.brigadier.arguments.StringArgumentType.getString; import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; -import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.getMembers; -import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.member; -import static sciwhiz12.janitor.commands.util.CommandHelper.argument; -import static sciwhiz12.janitor.commands.util.CommandHelper.literal; -import static sciwhiz12.janitor.config.GuildConfig.ALLOW_WARN_OTHER_MODERATORS; +import static sciwhiz12.janitor.api.command.arguments.GuildMemberArgument.getMembers; +import static sciwhiz12.janitor.api.command.arguments.GuildMemberArgument.member; +import static sciwhiz12.janitor.api.utils.CommandHelper.argument; +import static sciwhiz12.janitor.api.utils.CommandHelper.literal; +import static sciwhiz12.janitor.moderation.ModerationConfigs.ALLOW_WARN_OTHER_MODERATORS; +import static sciwhiz12.janitor.moderation.ModerationConfigs.ENABLE_WARNS; -public class WarnCommand extends BaseCommand { +public class WarnCommand extends ModBaseCommand { public static final EnumSet WARN_PERMISSION = EnumSet.of(Permission.KICK_MEMBERS); - public WarnCommand(CommandRegistry registry) { - super(registry); + public WarnCommand(ModerationModuleImpl module, CommandRegistry registry) { + super(module, registry); } @Override public LiteralArgumentBuilder getNode() { return literal("warn") - .requires(ctx -> config(ctx).forGuild(GuildConfig.ENABLE_WARNS)) + .requires(ctx -> config(ctx).forGuild(ENABLE_WARNS)) .then(argument("member", member()) .then(argument("reason", greedyString()) .executes(ctx -> this.run(ctx, getString(ctx, "reason"))) @@ -93,19 +93,19 @@ public class WarnCommand extends BaseCommand { .send(getBot(), channel).queue(); } else { - WarningEntry entry = new WarningEntry(target.getUser(), performer.getUser(), dateTime, reason); - int caseId = WarningStorage.get(getBot().getStorage(), guild).addWarning(entry); + WarningEntry entry = new WarningEntry(performer.getUser(), target.getUser(), dateTime, reason); + int caseId = getWarns(guild).addWarning(entry); target.getUser().openPrivateChannel() .flatMap(dm -> messages().getRegularMessage("moderation/warn/dm") .apply(MessageHelper.member("performer", performer)) - .apply(MessageHelper.warningEntry("warning_entry", caseId, entry)) + .apply(ModerationHelper.warningEntry("warning_entry", caseId, entry)) .send(getBot(), dm) ) .mapToResult() .flatMap(res -> messages().getRegularMessage("moderation/warn/info") .apply(MessageHelper.member("performer", performer)) - .apply(MessageHelper.warningEntry("warning_entry", caseId, entry)) + .apply(ModerationHelper.warningEntry("warning_entry", caseId, entry)) .with("private_message", () -> res.isSuccess() ? "\u2705" : "\u274C") .send(getBot(), channel) ) diff --git a/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java b/moderation/src/main/java/sciwhiz12/janitor/moderation/warns/WarnListCommand.java similarity index 75% rename from src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java rename to moderation/src/main/java/sciwhiz12/janitor/moderation/warns/WarnListCommand.java index c7ed366..0b7855c 100644 --- a/src/main/java/sciwhiz12/janitor/commands/moderation/WarnListCommand.java +++ b/moderation/src/main/java/sciwhiz12/janitor/moderation/warns/WarnListCommand.java @@ -1,4 +1,4 @@ -package sciwhiz12.janitor.commands.moderation; +package sciwhiz12.janitor.moderation.warns; import com.google.common.collect.ImmutableList; import com.mojang.brigadier.builder.LiteralArgumentBuilder; @@ -9,11 +9,9 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import sciwhiz12.janitor.commands.BaseCommand; -import sciwhiz12.janitor.commands.CommandRegistry; -import sciwhiz12.janitor.moderation.warns.WarningEntry; -import sciwhiz12.janitor.moderation.warns.WarningStorage; -import sciwhiz12.janitor.msg.MessageHelper; +import sciwhiz12.janitor.api.command.CommandRegistry; +import sciwhiz12.janitor.moderation.ModBaseCommand; +import sciwhiz12.janitor.moderation.ModerationModuleImpl; import java.util.Comparator; import java.util.EnumSet; @@ -22,17 +20,20 @@ import java.util.Map; import java.util.Objects; import java.util.function.Predicate; -import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.getMembers; -import static sciwhiz12.janitor.commands.arguments.GuildMemberArgument.member; -import static sciwhiz12.janitor.commands.util.CommandHelper.argument; -import static sciwhiz12.janitor.commands.util.CommandHelper.literal; -import static sciwhiz12.janitor.config.GuildConfig.ENABLE_WARNS; +import static sciwhiz12.janitor.api.command.arguments.GuildMemberArgument.getMembers; +import static sciwhiz12.janitor.api.command.arguments.GuildMemberArgument.member; +import static sciwhiz12.janitor.api.utils.CommandHelper.argument; +import static sciwhiz12.janitor.api.utils.CommandHelper.literal; +import static sciwhiz12.janitor.api.utils.MessageHelper.member; +import static sciwhiz12.janitor.api.utils.MessageHelper.user; +import static sciwhiz12.janitor.moderation.ModerationConfigs.ENABLE_WARNS; +import static sciwhiz12.janitor.moderation.ModerationHelper.warningEntry; -public class WarnListCommand extends BaseCommand { +public class WarnListCommand extends ModBaseCommand { public static final EnumSet WARN_PERMISSION = EnumSet.of(Permission.KICK_MEMBERS); - public WarnListCommand(CommandRegistry registry) { - super(registry); + public WarnListCommand(ModerationModuleImpl module, CommandRegistry registry) { + super(module, registry); } @Override @@ -61,7 +62,7 @@ public class WarnListCommand extends BaseCommand { MessageChannel channel = ctx.getSource().getChannel(); if (!ctx.getSource().isFromGuild()) { messages().getRegularMessage("general/error/guild_only_command") - .apply(MessageHelper.user("performer", ctx.getSource().getAuthor())) + .apply(user("performer", ctx.getSource().getAuthor())) .send(getBot(), channel).queue(); return 1; @@ -76,7 +77,7 @@ public class WarnListCommand extends BaseCommand { final Member target = members.get(0); if (guild.getSelfMember().equals(target)) { messages().getRegularMessage("general/error/cannot_interact") - .apply(MessageHelper.member("target", target)) + .apply(member("target", target)) .send(getBot(), channel).queue(); return 1; @@ -92,18 +93,18 @@ public class WarnListCommand extends BaseCommand { if (!performer.hasPermission(WARN_PERMISSION)) { messages().getRegularMessage("moderation/error/insufficient_permissions") - .apply(MessageHelper.member("performer", performer)) + .apply(member("performer", performer)) .with("required_permissions", WARN_PERMISSION::toString) .send(getBot(), channel).queue(); } else { messages().>getListingMessage("moderation/warn/list") - .apply(MessageHelper.member("performer", performer)) + .apply(member("performer", performer)) .setEntryApplier((entry, subs) -> - subs.apply(MessageHelper.warningEntry("warning_entry", entry.getKey(), entry.getValue())) + subs.apply(warningEntry("warning_entry", entry.getKey(), entry.getValue())) ) .build(channel, getBot(), ctx.getSource().getMessage(), - WarningStorage.get(getBot().getStorage(), guild) + getWarns(guild) .getWarnings() .entrySet().stream() .filter(predicate) diff --git a/moderation/src/main/java/sciwhiz12/janitor/moderation/warns/WarningEntryDeserializer.java b/moderation/src/main/java/sciwhiz12/janitor/moderation/warns/WarningEntryDeserializer.java new file mode 100644 index 0000000..f14a9c0 --- /dev/null +++ b/moderation/src/main/java/sciwhiz12/janitor/moderation/warns/WarningEntryDeserializer.java @@ -0,0 +1,33 @@ +package sciwhiz12.janitor.moderation.warns; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import net.dv8tion.jda.api.entities.User; +import sciwhiz12.janitor.api.JanitorBot; + +import java.io.IOException; +import java.time.OffsetDateTime; +import java.util.function.Supplier; + +public class WarningEntryDeserializer extends StdDeserializer { + private static final long serialVersionUID = 1L; + + private final Supplier bot; + + public WarningEntryDeserializer(Supplier bot) { + super(WarningEntry.class); + this.bot = bot; + } + + @Override + public WarningEntry deserialize(JsonParser p, DeserializationContext ctx) throws IOException { + final JsonNode obj = ctx.readTree(p); + User performer = bot.get().getDiscord().retrieveUserById(obj.get("performer").asLong()).complete(); + User warned = bot.get().getDiscord().retrieveUserById(obj.get("warned").asLong()).complete(); + OffsetDateTime dateTime = OffsetDateTime.parse(obj.get("dateTime").asText()); + String contents = obj.get("reason").asText(); + return new WarningEntry(performer, warned, dateTime, contents); + } +} diff --git a/moderation/src/main/java/sciwhiz12/janitor/moderation/warns/WarningEntrySerializer.java b/moderation/src/main/java/sciwhiz12/janitor/moderation/warns/WarningEntrySerializer.java new file mode 100644 index 0000000..ab8d402 --- /dev/null +++ b/moderation/src/main/java/sciwhiz12/janitor/moderation/warns/WarningEntrySerializer.java @@ -0,0 +1,25 @@ +package sciwhiz12.janitor.moderation.warns; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; + +public class WarningEntrySerializer extends StdSerializer { + private static final long serialVersionUID = 1L; + + public WarningEntrySerializer() { + super(WarningEntry.class); + } + + @Override + public void serialize(WarningEntry value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeStartObject(); + gen.writeNumberField("performer", value.getPerformer().getIdLong()); + gen.writeNumberField("warned", value.getWarned().getIdLong()); + gen.writeStringField("dateTime", value.getDateTime().toString()); + gen.writeStringField("reason", value.getReason()); + gen.writeEndObject(); + } +} diff --git a/src/main/java/sciwhiz12/janitor/moderation/warns/WarningStorage.java b/moderation/src/main/java/sciwhiz12/janitor/moderation/warns/WarningStorageImpl.java similarity index 76% rename from src/main/java/sciwhiz12/janitor/moderation/warns/WarningStorage.java rename to moderation/src/main/java/sciwhiz12/janitor/moderation/warns/WarningStorageImpl.java index 041ef5a..a1162d1 100644 --- a/src/main/java/sciwhiz12/janitor/moderation/warns/WarningStorage.java +++ b/moderation/src/main/java/sciwhiz12/janitor/moderation/warns/WarningStorageImpl.java @@ -6,29 +6,21 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.node.ObjectNode; -import net.dv8tion.jda.api.entities.Guild; import org.checkerframework.checker.nullness.qual.Nullable; -import sciwhiz12.janitor.JanitorBot; -import sciwhiz12.janitor.storage.GuildStorage; -import sciwhiz12.janitor.storage.JsonStorage; -import sciwhiz12.janitor.storage.StorageKey; +import sciwhiz12.janitor.api.JanitorBot; +import sciwhiz12.janitor.api.storage.JsonStorage; import java.util.HashMap; import java.util.Map; -public class WarningStorage extends JsonStorage { +public class WarningStorageImpl extends JsonStorage implements WarningStorage { private static final TypeReference> WARNING_MAP_TYPE = new TypeReference<>() {}; - public static final StorageKey KEY = new StorageKey<>("warnings", WarningStorage.class); - - public static WarningStorage get(GuildStorage storage, Guild guild) { - return storage.getOrCreate(guild, KEY, () -> new WarningStorage(storage.getBot())); - } private final JanitorBot bot; private int lastID = 1; private final Map warnings = new ObservedMap<>(new HashMap<>(), this::markDirty); - public WarningStorage(JanitorBot bot) { + public WarningStorageImpl(JanitorBot bot) { this.bot = bot; } @@ -60,8 +52,8 @@ public class WarningStorage extends JsonStorage { super.initialize(mapper); mapper.registerModule( new SimpleModule() - .addSerializer(WarningEntry.class, new WarningEntry.Serializer()) - .addDeserializer(WarningEntry.class, new WarningEntry.Deserializer(this::getBot)) + .addSerializer(WarningEntry.class, new WarningEntrySerializer()) + .addDeserializer(WarningEntry.class, new WarningEntryDeserializer(this::getBot)) ); } diff --git a/moderation/src/main/resources/META-INF/services/sciwhiz12.janitor.api.module.ModuleProvider b/moderation/src/main/resources/META-INF/services/sciwhiz12.janitor.api.module.ModuleProvider new file mode 100644 index 0000000..222e7a7 --- /dev/null +++ b/moderation/src/main/resources/META-INF/services/sciwhiz12.janitor.api.module.ModuleProvider @@ -0,0 +1 @@ +sciwhiz12.janitor.moderation.ModerationModuleImpl$Provider \ No newline at end of file diff --git a/src/main/resources/messages/messages.json b/moderation/src/main/resources/messages/messages.json similarity index 74% rename from src/main/resources/messages/messages.json rename to moderation/src/main/resources/messages/messages.json index fe64a1b..dbc26ed 100644 --- a/src/main/resources/messages/messages.json +++ b/moderation/src/main/resources/messages/messages.json @@ -1,10 +1,4 @@ [ - "general/error/ambiguous_member", - "general/error/guild_only_command", - "general/error/insufficient_permissions", - "general/error/cannot_interact", - "general/error/cannot_action_self", - "general/error/cannot_action_performer", "moderation/error/cannot_interact", "moderation/error/insufficient_permissions", "moderation/kick/info", diff --git a/src/main/resources/messages/moderation/ban/dm.json b/moderation/src/main/resources/messages/moderation/ban/dm.json similarity index 100% rename from src/main/resources/messages/moderation/ban/dm.json rename to moderation/src/main/resources/messages/moderation/ban/dm.json diff --git a/src/main/resources/messages/moderation/ban/info.json b/moderation/src/main/resources/messages/moderation/ban/info.json similarity index 100% rename from src/main/resources/messages/moderation/ban/info.json rename to moderation/src/main/resources/messages/moderation/ban/info.json diff --git a/src/main/resources/messages/moderation/error/cannot_interact.json b/moderation/src/main/resources/messages/moderation/error/cannot_interact.json similarity index 100% rename from src/main/resources/messages/moderation/error/cannot_interact.json rename to moderation/src/main/resources/messages/moderation/error/cannot_interact.json diff --git a/src/main/resources/messages/moderation/error/insufficient_permissions.json b/moderation/src/main/resources/messages/moderation/error/insufficient_permissions.json similarity index 100% rename from src/main/resources/messages/moderation/error/insufficient_permissions.json rename to moderation/src/main/resources/messages/moderation/error/insufficient_permissions.json diff --git a/src/main/resources/messages/moderation/error/note/max_amount_of_notes.json b/moderation/src/main/resources/messages/moderation/error/note/max_amount_of_notes.json similarity index 100% rename from src/main/resources/messages/moderation/error/note/max_amount_of_notes.json rename to moderation/src/main/resources/messages/moderation/error/note/max_amount_of_notes.json diff --git a/src/main/resources/messages/moderation/error/note/no_note_found.json b/moderation/src/main/resources/messages/moderation/error/note/no_note_found.json similarity index 100% rename from src/main/resources/messages/moderation/error/note/no_note_found.json rename to moderation/src/main/resources/messages/moderation/error/note/no_note_found.json diff --git a/src/main/resources/messages/moderation/error/unwarn/cannot_remove_higher_mod.json b/moderation/src/main/resources/messages/moderation/error/unwarn/cannot_remove_higher_mod.json similarity index 100% rename from src/main/resources/messages/moderation/error/unwarn/cannot_remove_higher_mod.json rename to moderation/src/main/resources/messages/moderation/error/unwarn/cannot_remove_higher_mod.json diff --git a/src/main/resources/messages/moderation/error/unwarn/cannot_unwarn_self.json b/moderation/src/main/resources/messages/moderation/error/unwarn/cannot_unwarn_self.json similarity index 100% rename from src/main/resources/messages/moderation/error/unwarn/cannot_unwarn_self.json rename to moderation/src/main/resources/messages/moderation/error/unwarn/cannot_unwarn_self.json diff --git a/src/main/resources/messages/moderation/error/unwarn/no_case_found.json b/moderation/src/main/resources/messages/moderation/error/unwarn/no_case_found.json similarity index 100% rename from src/main/resources/messages/moderation/error/unwarn/no_case_found.json rename to moderation/src/main/resources/messages/moderation/error/unwarn/no_case_found.json diff --git a/src/main/resources/messages/moderation/error/warn/cannot_warn_mods.json b/moderation/src/main/resources/messages/moderation/error/warn/cannot_warn_mods.json similarity index 100% rename from src/main/resources/messages/moderation/error/warn/cannot_warn_mods.json rename to moderation/src/main/resources/messages/moderation/error/warn/cannot_warn_mods.json diff --git a/src/main/resources/messages/moderation/kick/dm.json b/moderation/src/main/resources/messages/moderation/kick/dm.json similarity index 100% rename from src/main/resources/messages/moderation/kick/dm.json rename to moderation/src/main/resources/messages/moderation/kick/dm.json diff --git a/src/main/resources/messages/moderation/kick/info.json b/moderation/src/main/resources/messages/moderation/kick/info.json similarity index 100% rename from src/main/resources/messages/moderation/kick/info.json rename to moderation/src/main/resources/messages/moderation/kick/info.json diff --git a/src/main/resources/messages/moderation/note/add.json b/moderation/src/main/resources/messages/moderation/note/add.json similarity index 100% rename from src/main/resources/messages/moderation/note/add.json rename to moderation/src/main/resources/messages/moderation/note/add.json diff --git a/src/main/resources/messages/moderation/note/list.json b/moderation/src/main/resources/messages/moderation/note/list.json similarity index 100% rename from src/main/resources/messages/moderation/note/list.json rename to moderation/src/main/resources/messages/moderation/note/list.json diff --git a/src/main/resources/messages/moderation/note/remove.json b/moderation/src/main/resources/messages/moderation/note/remove.json similarity index 100% rename from src/main/resources/messages/moderation/note/remove.json rename to moderation/src/main/resources/messages/moderation/note/remove.json diff --git a/src/main/resources/messages/moderation/unban/info.json b/moderation/src/main/resources/messages/moderation/unban/info.json similarity index 100% rename from src/main/resources/messages/moderation/unban/info.json rename to moderation/src/main/resources/messages/moderation/unban/info.json diff --git a/src/main/resources/messages/moderation/unwarn/info.json b/moderation/src/main/resources/messages/moderation/unwarn/info.json similarity index 100% rename from src/main/resources/messages/moderation/unwarn/info.json rename to moderation/src/main/resources/messages/moderation/unwarn/info.json diff --git a/src/main/resources/messages/moderation/warn/dm.json b/moderation/src/main/resources/messages/moderation/warn/dm.json similarity index 100% rename from src/main/resources/messages/moderation/warn/dm.json rename to moderation/src/main/resources/messages/moderation/warn/dm.json diff --git a/src/main/resources/messages/moderation/warn/info.json b/moderation/src/main/resources/messages/moderation/warn/info.json similarity index 100% rename from src/main/resources/messages/moderation/warn/info.json rename to moderation/src/main/resources/messages/moderation/warn/info.json diff --git a/src/main/resources/messages/moderation/warn/list.json b/moderation/src/main/resources/messages/moderation/warn/list.json similarity index 100% rename from src/main/resources/messages/moderation/warn/list.json rename to moderation/src/main/resources/messages/moderation/warn/list.json diff --git a/settings.gradle b/settings.gradle index 86bb791..ef0172b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,10 +1,2 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * The settings file is used to specify which projects to include in your build. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user manual at https://docs.gradle.org/6.6.1/userguide/multi_project_builds.html - */ - rootProject.name = 'Janitor' +include 'core', 'moderation' diff --git a/src/main/java/sciwhiz12/janitor/BotStartup.java b/src/main/java/sciwhiz12/janitor/BotStartup.java index 80a4563..c554896 100644 --- a/src/main/java/sciwhiz12/janitor/BotStartup.java +++ b/src/main/java/sciwhiz12/janitor/BotStartup.java @@ -1,6 +1,5 @@ package sciwhiz12.janitor; -import com.google.common.base.Preconditions; import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.entities.Activity; @@ -8,20 +7,19 @@ import net.dv8tion.jda.api.events.ReadyEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.requests.GatewayIntent; import org.jetbrains.annotations.NotNull; -import sciwhiz12.janitor.config.BotConfig; +import sciwhiz12.janitor.config.BotConfigImpl; import sciwhiz12.janitor.config.BotOptions; import java.util.EnumSet; -import static sciwhiz12.janitor.Logging.JANITOR; +import static sciwhiz12.janitor.api.Logging.JANITOR; public class BotStartup { public static void main(String[] args) { JANITOR.info("Starting..."); BotOptions options = new BotOptions(args); - BotConfig config = new BotConfig(options); - Preconditions.checkArgument(!config.getToken().isEmpty(), "Supply a client token through config or command line"); + BotConfigImpl config = new BotConfigImpl(options); JANITOR.info("Building bot instance and connecting to Discord..."); @@ -33,12 +31,13 @@ public class BotStartup { .addEventListeners(new ListenerAdapter() { @Override public void onReady(@NotNull ReadyEvent event) { - new JanitorBot(event.getJDA(), config); + new JanitorBotImpl(event.getJDA(), config); } }) .build(); } catch (Exception ex) { JANITOR.error("Error while building Discord connection", ex); + System.exit(1); } } } diff --git a/src/main/java/sciwhiz12/janitor/commands/BaseCommand.java b/src/main/java/sciwhiz12/janitor/commands/BaseCommand.java deleted file mode 100644 index e892d8c..0000000 --- a/src/main/java/sciwhiz12/janitor/commands/BaseCommand.java +++ /dev/null @@ -1,36 +0,0 @@ -package sciwhiz12.janitor.commands; - -import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import sciwhiz12.janitor.JanitorBot; -import sciwhiz12.janitor.config.GuildConfig; -import sciwhiz12.janitor.msg.Messages; - -public abstract class BaseCommand { - private final CommandRegistry registry; - - public BaseCommand(CommandRegistry registry) { - this.registry = registry; - } - - public CommandRegistry getRegistry() { - return registry; - } - - public JanitorBot getBot() { - return registry.getBot(); - } - - protected Messages messages() { - return getBot().getMessages(); - } - - protected GuildConfig config(MessageReceivedEvent event) { return config(event.getGuild().getIdLong()); } - - protected GuildConfig config(Guild guild) { return config(guild.getIdLong()); } - - protected GuildConfig config(long guildID) { return getBot().getConfigManager().getConfig(guildID); } - - public abstract LiteralArgumentBuilder getNode(); -} diff --git a/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java b/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java deleted file mode 100644 index 069d503..0000000 --- a/src/main/java/sciwhiz12/janitor/commands/util/ModerationHelper.java +++ /dev/null @@ -1,42 +0,0 @@ -package sciwhiz12.janitor.commands.util; - -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.User; -import net.dv8tion.jda.api.requests.restaction.AuditableRestAction; - -import java.time.Instant; -import java.time.ZoneOffset; -import javax.annotation.Nullable; - -import static sciwhiz12.janitor.msg.MessageHelper.DATE_TIME_FORMAT; -import static sciwhiz12.janitor.utils.Util.nameFor; - -public class ModerationHelper { - public static AuditableRestAction kickUser(Guild guild, Member performer, Member target, @Nullable String reason) { - StringBuilder auditReason = new StringBuilder(); - auditReason.append("Kicked by ") - .append(nameFor(performer.getUser())) - .append(" on ") - .append(Instant.now().atOffset(ZoneOffset.UTC).format(DATE_TIME_FORMAT)); - if (reason != null) - auditReason.append(" for reason: ").append(reason); - return guild.kick(target, auditReason.toString()); - } - - public static AuditableRestAction banUser(Guild guild, Member performer, Member target, int deleteDuration, - @Nullable String reason) { - StringBuilder auditReason = new StringBuilder(); - auditReason.append("Banned by ") - .append(nameFor(performer.getUser())) - .append(" on ") - .append(Instant.now().atOffset(ZoneOffset.UTC).format(DATE_TIME_FORMAT)); - if (reason != null) - auditReason.append(" for reason: ").append(reason); - return guild.ban(target, deleteDuration, auditReason.toString()); - } - - public static AuditableRestAction unbanUser(Guild guild, User target) { - return guild.unban(target); - } -} diff --git a/src/main/java/sciwhiz12/janitor/config/ConfigManager.java b/src/main/java/sciwhiz12/janitor/config/ConfigManager.java deleted file mode 100644 index 4e14d9c..0000000 --- a/src/main/java/sciwhiz12/janitor/config/ConfigManager.java +++ /dev/null @@ -1,44 +0,0 @@ -package sciwhiz12.janitor.config; - -import sciwhiz12.janitor.JanitorBot; - -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -public class ConfigManager { - private final JanitorBot bot; - private final Path configPath; - private final Map configMap = new HashMap<>(); - - public ConfigManager(JanitorBot bot, Path configPath) { - this.bot = bot; - this.configPath = configPath; - } - - public GuildConfig getConfig(long guildID) { - return configMap.computeIfAbsent(guildID, (id) -> new GuildConfig(id, getFile(guildID))); - } - - public JanitorBot getBot() { - return bot; - } - - public void save() { - configMap.values().forEach(GuildConfig::save); - } - - public void close() { - configMap.values().forEach(GuildConfig::close); - for (Iterator iterator = configMap.values().iterator(); iterator.hasNext(); ) { - iterator.next().close(); - iterator.remove(); - } - } - - private Path getFile(long guildID) { - final Path file = Path.of(Long.toHexString(guildID) + ".toml"); - return configPath.resolve(file); - } -} diff --git a/src/main/java/sciwhiz12/janitor/config/GuildConfig.java b/src/main/java/sciwhiz12/janitor/config/GuildConfig.java deleted file mode 100644 index 26078f1..0000000 --- a/src/main/java/sciwhiz12/janitor/config/GuildConfig.java +++ /dev/null @@ -1,183 +0,0 @@ -package sciwhiz12.janitor.config; - -import com.electronwill.nightconfig.core.CommentedConfig; -import com.electronwill.nightconfig.core.file.CommentedFileConfig; -import com.electronwill.nightconfig.core.file.FileNotFoundAction; -import com.electronwill.nightconfig.core.file.FileWatcher; -import com.electronwill.nightconfig.toml.TomlFormat; -import com.google.common.base.Joiner; -import net.dv8tion.jda.api.entities.GuildChannel; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.function.Supplier; - -import static sciwhiz12.janitor.Logging.CONFIG; -import static sciwhiz12.janitor.Logging.JANITOR; - -public class GuildConfig { - private static final Joiner NEWLINE = Joiner.on("\n"); - - private final long guild; - private final CommentedFileConfig config; - private boolean closed = false; - - public static final ConfigNode ENABLE = new ConfigNode<>( - "enable", () -> true, - "Whether the bot is enabled for this guild.", - "Can be used to temporarily disable the bot in emergency situations."); - - public static final ConfigNode COMMAND_PREFIX = new ConfigNode<>( - "commands.prefix", () -> "!", - "The prefix for all commands."); - - public static final ConfigNode ENABLE_WARNS = new ConfigNode<>( - "moderation.warns.enable", () -> true, - "Whether to enable the warnings system. If disabled, the related commands are force-disabled."); - - public static final ConfigNode WARNS_RESPECT_MOD_ROLES = new ConfigNode<>( - "moderation.warns.respect_mod_roles", () -> false, - "Whether to prevent lower-ranked moderators (in the role hierarchy) from removing warnings " + - "issued by higher-ranked moderators."); - public static final ConfigNode ALLOW_WARN_OTHER_MODERATORS = new ConfigNode<>( - "moderation.warns.warn_other_moderators", () -> true, - "Whether to allow moderators to issue warnings against other moderators."); - public static final ConfigNode ALLOW_REMOVE_SELF_WARNINGS = new ConfigNode<>( - "moderation.warns.remove_self_warnings", () -> false, - "Whether to allow moderators to remove warnings from themselves."); - - public static final ConfigNode ENABLE_NOTES = new ConfigNode<>( - "moderation.notes.enable", () -> true, - "Whether to enable the notes system. If disabled, the related commands are force-disabled."); - public static final ConfigNode MAX_NOTES_PER_MOD = new ConfigNode<>( - "moderation.notes.max_amount", () -> Integer.MAX_VALUE, - "The max amount of notes for a user per moderator."); - - GuildConfig(long guild, Path configPath) { - this.guild = guild; - this.config = CommentedFileConfig.builder(configPath, TomlFormat.instance()) - .onFileNotFound(FileNotFoundAction.CREATE_EMPTY) - .preserveInsertionOrder() - .autosave() - .build(); - try { - CONFIG.info("Building guild config for {} from {}", Long.toHexString(this.guild), configPath); - config.load(); - FileWatcher.defaultInstance().addWatch(configPath, this::onFileChange); - ConfigNode.nodes.forEach(this::forGuild); - save(); - } catch (IOException ex) { - JANITOR.error("Error while building config from file {}", configPath, ex); - } - } - - protected CommentedConfig getChannelOverrides() { - final String channelOverridesID = "channel_overrides"; - CommentedConfig channelConfigs = config.get(channelOverridesID); - if (channelConfigs == null) { - channelConfigs = config.createSubConfig(); - config.set(channelOverridesID, channelConfigs); - config.setComment(channelOverridesID, "Channel overrides for certain configuration options"); - } - return channelConfigs; - } - - public CommentedConfig getChannelConfig(GuildChannel channel) { - final String id = channel.getId(); - CommentedConfig overrides = getChannelOverrides(); - CommentedConfig channelOverride = overrides.get(id); - if (channelOverride == null) { - channelOverride = overrides.createSubConfig(); - overrides.set(id, channelOverride); - overrides.setComment(id, "Channel overrides for channel with name " + channel.getName()); - } - return channelOverride; - } - - private static void ensureComment(CommentedConfig config, String path, String expectedComment) { - if (!Objects.equals(config.getComment(path), expectedComment)) { - config.setComment(path, expectedComment); - } - } - - public T forGuild(ConfigNode node) { - ensureComment(config, node.path, node.comment); - T value = config.get(node.path); - if (value == null) { - value = node.defaultValue.get(); - config.set(node.path, value); - } - return value; - } - - public void forGuild(ConfigNode node, T newValue) { - ensureComment(config, node.path, node.comment); - config.set(node.path, newValue); - } - - public T forChannel(GuildChannel channel, ConfigNode node) { - CommentedConfig channelConfig = getChannelConfig(channel); - ensureComment(channelConfig, node.path, node.comment); - T value = channelConfig.getRaw(node.path); - if (value == null) { - value = node.defaultValue.get(); - channelConfig.set(node.path, node.defaultValue.get()); - } - return value; - } - - public void forChannel(GuildChannel channel, ConfigNode node, T newValue) { - CommentedConfig channelConfig = getChannelConfig(channel); - ensureComment(channelConfig, node.path, node.comment); - channelConfig.set(node.path, newValue); - } - - public long getGuildID() { - return guild; - } - - public CommentedFileConfig getRawConfig() { - return config; - } - - public void save() { - if (!closed) { - config.save(); - } - } - - public void close() { - if (!closed) { - closed = true; - config.close(); - } - } - - void onFileChange() { - if (closed) return; - try { - CONFIG.info("Reloading config due to file change {}", config.getNioPath()); - config.load(); - } catch (Exception ex) { - CONFIG.error("Error while reloading config from {}", config.getNioPath(), ex); - } - } - - public static class ConfigNode { - static final List> nodes = new ArrayList<>(); - - public final String path; - final String comment; - final Supplier defaultValue; - - ConfigNode(String path, Supplier defaultValue, String... comment) { - this.path = path; - this.defaultValue = defaultValue; - this.comment = NEWLINE.join(comment); - nodes.add(this); - } - } -} diff --git a/src/main/java/sciwhiz12/janitor/moderation/notes/NoteEntry.java b/src/main/java/sciwhiz12/janitor/moderation/notes/NoteEntry.java deleted file mode 100644 index 94ded43..0000000 --- a/src/main/java/sciwhiz12/janitor/moderation/notes/NoteEntry.java +++ /dev/null @@ -1,101 +0,0 @@ -package sciwhiz12.janitor.moderation.notes; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import net.dv8tion.jda.api.entities.User; -import sciwhiz12.janitor.JanitorBot; - -import java.io.IOException; -import java.time.OffsetDateTime; -import java.util.Objects; -import java.util.function.Supplier; - -public class NoteEntry { - private final User performer; - private final User target; - private final OffsetDateTime dateTime; - private final String contents; - - public NoteEntry(User performer, User target, OffsetDateTime dateTime, String contents) { - this.performer = performer; - this.target = target; - this.dateTime = dateTime; - this.contents = contents; - } - - public User getPerformer() { - return performer; - } - - public User getTarget() { - return target; - } - - public OffsetDateTime getDateTime() { - return dateTime; - } - - public String getContents() { - return contents; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - NoteEntry noteEntry = (NoteEntry) o; - return getPerformer().equals(noteEntry.getPerformer()) && - getTarget().equals(noteEntry.getTarget()) && - getDateTime().equals(noteEntry.getDateTime()) && - getContents().equals(noteEntry.getContents()); - } - - @Override - public int hashCode() { - return Objects.hash(getPerformer(), getTarget(), getDateTime(), getContents()); - } - - public static class Serializer extends StdSerializer { - private static final long serialVersionUID = 1L; - - public Serializer() { - super(NoteEntry.class); - } - - @Override - public void serialize(NoteEntry value, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeStartObject(); - gen.writeNumberField("performer", value.getPerformer().getIdLong()); - gen.writeNumberField("target", value.getTarget().getIdLong()); - gen.writeStringField("dateTime", value.getDateTime().toString()); - gen.writeStringField("contents", value.getContents()); - gen.writeEndObject(); - } - } - - public static class Deserializer extends StdDeserializer { - private static final long serialVersionUID = 1L; - - private final Supplier bot; - - public Deserializer(Supplier bot) { - super(NoteEntry.class); - this.bot = bot; - } - - @Override - public NoteEntry deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - final JsonNode obj = ctx.readTree(p); - User performer = bot.get().getDiscord().retrieveUserById(obj.get("performer").asLong()).complete(); - User target = bot.get().getDiscord().retrieveUserById(obj.get("target").asLong()).complete(); - OffsetDateTime dateTime = OffsetDateTime.parse(obj.get("dateTime").asText()); - String contents = obj.get("contents").asText(); - return new NoteEntry(performer, target, dateTime, contents); - } - } -} diff --git a/src/main/java/sciwhiz12/janitor/moderation/warns/WarningEntry.java b/src/main/java/sciwhiz12/janitor/moderation/warns/WarningEntry.java deleted file mode 100644 index 2623c2d..0000000 --- a/src/main/java/sciwhiz12/janitor/moderation/warns/WarningEntry.java +++ /dev/null @@ -1,104 +0,0 @@ -package sciwhiz12.janitor.moderation.warns; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import net.dv8tion.jda.api.entities.User; -import sciwhiz12.janitor.JanitorBot; - -import java.io.IOException; -import java.time.OffsetDateTime; -import java.util.Objects; -import java.util.function.Supplier; -import javax.annotation.Nullable; - -public class WarningEntry { - private final User performer; - private final User warned; - private final OffsetDateTime dateTime; - @Nullable - private final String reason; - - public WarningEntry(User warned, User performer, OffsetDateTime dateTime, @Nullable String reason) { - this.performer = performer; - this.warned = warned; - this.dateTime = dateTime; - this.reason = reason; - } - - public User getPerformer() { - return performer; - } - - public User getWarned() { - return warned; - } - - public OffsetDateTime getDateTime() { - return dateTime; - } - - @Nullable - public String getReason() { - return reason; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - WarningEntry that = (WarningEntry) o; - return getPerformer().equals(that.getPerformer()) && - getWarned().equals(that.getWarned()) && - getDateTime().equals(that.getDateTime()) && - Objects.equals(getReason(), that.getReason()); - } - - @Override - public int hashCode() { - return Objects.hash(getPerformer(), getWarned(), getDateTime(), getReason()); - } - - public static class Serializer extends StdSerializer { - private static final long serialVersionUID = 1L; - - public Serializer() { - super(WarningEntry.class); - } - - @Override - public void serialize(WarningEntry value, JsonGenerator gen, SerializerProvider provider) throws IOException { - gen.writeStartObject(); - gen.writeNumberField("performer", value.getPerformer().getIdLong()); - gen.writeNumberField("warned", value.getWarned().getIdLong()); - gen.writeStringField("dateTime", value.getDateTime().toString()); - gen.writeStringField("reason", value.getReason()); - gen.writeEndObject(); - } - } - - public static class Deserializer extends StdDeserializer { - private static final long serialVersionUID = 1L; - - private final Supplier bot; - - public Deserializer(Supplier bot) { - super(WarningEntry.class); - this.bot = bot; - } - - @Override - public WarningEntry deserialize(JsonParser p, DeserializationContext ctx) throws IOException { - final JsonNode obj = ctx.readTree(p); - User performer = bot.get().getDiscord().retrieveUserById(obj.get("performer").asLong()).complete(); - User warned = bot.get().getDiscord().retrieveUserById(obj.get("warned").asLong()).complete(); - OffsetDateTime dateTime = OffsetDateTime.parse(obj.get("dateTime").asText()); - String contents = obj.get("reason").asText(); - return new WarningEntry(performer, warned, dateTime, contents); - } - } -} diff --git a/src/main/java/sciwhiz12/janitor/msg/Messages.java b/src/main/java/sciwhiz12/janitor/msg/Messages.java deleted file mode 100644 index 5cf47da..0000000 --- a/src/main/java/sciwhiz12/janitor/msg/Messages.java +++ /dev/null @@ -1,161 +0,0 @@ -package sciwhiz12.janitor.msg; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import net.dv8tion.jda.api.entities.MessageEmbed; -import sciwhiz12.janitor.JanitorBot; -import sciwhiz12.janitor.msg.json.ListingMessage; -import sciwhiz12.janitor.msg.json.RegularMessage; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static com.google.common.io.Resources.getResource; -import static sciwhiz12.janitor.Logging.JANITOR; -import static sciwhiz12.janitor.Logging.MESSAGES; - -public class Messages { - public static final String JSON_FILE_SUFFIX = ".json"; - public static final String MESSAGES_FILENAME = "messages"; - public static final String DEFAULT_MESSAGES_FOLDER = "messages/"; - public static final TypeReference> LIST_TYPE = new TypeReference<>() {}; - - private final JanitorBot bot; - private final Path messagesFolder; - private final Map regularMessages = new HashMap<>(); - private final Map listingMessages = new HashMap<>(); - private final ObjectMapper jsonMapper = new ObjectMapper(); - - public Messages(JanitorBot bot, Path messagesFolder) { - this.bot = bot; - this.messagesFolder = messagesFolder; - loadMessages(); - } - - public JanitorBot getBot() { - return bot; - } - - public void loadMessages() { - boolean success = false; - - if (messagesFolder != null) { - JANITOR.debug(MESSAGES, "Loading messages from folder {}", messagesFolder); - success = loadMessages( - path -> Files.newBufferedReader(messagesFolder.resolve(path + JSON_FILE_SUFFIX)) - ); - } else { - JANITOR.info(MESSAGES, "No custom messages folder specified"); - } - - if (!success) { - JANITOR.info(MESSAGES, "Loading default messages"); - //noinspection UnstableApiUsage - loadMessages( - file -> new InputStreamReader(getResource(DEFAULT_MESSAGES_FOLDER + file + JSON_FILE_SUFFIX).openStream()) - ); - } - } - - boolean loadMessages(FileOpener files) { - try (Reader keyReader = files.open(MESSAGES_FILENAME)) { - List keysList = jsonMapper.readValue(keyReader, LIST_TYPE); - regularMessages.clear(); - for (String messageKey : keysList) { - final String path = messageKey.replace("/", FileSystems.getDefault().getSeparator()); - try (Reader reader = files.open(path)) { - final JsonNode tree = jsonMapper.readTree(reader); - final String type = tree.path("type").asText("regular"); - if ("regular".equals(type)) { - regularMessages.put(messageKey, jsonMapper.convertValue(tree, RegularMessage.class)); - } else if ("listing".equals(type)) { - listingMessages.put(messageKey, jsonMapper.convertValue(tree, ListingMessage.class)); - } else { - JANITOR.warn(MESSAGES, "Unknown message type {} for {}", tree.path("type").asText(), messageKey); - } - } catch (Exception e) { - JANITOR.error(MESSAGES, "Error while loading message {}", path, e); - } - } - JANITOR.info(MESSAGES, "Loaded {} messages", regularMessages.size()); - return true; - } catch (Exception e) { - JANITOR.error(MESSAGES, "Error while loading messages", e); - return false; - } - } - - public Map getRegularMessages() { - return Collections.unmodifiableMap(regularMessages); - } - - public RegularMessageBuilder getRegularMessage(String messageKey) { - final RegularMessage msg = regularMessages.get(messageKey); - if (msg == null) { - JANITOR.warn(MESSAGES, "Attempted to get unknown regular message with key {}", messageKey); - return new RegularMessageBuilder(UNKNOWN_REGULAR_MESSAGE).with("key", () -> messageKey); - } - return new RegularMessageBuilder(msg); - } - - public Map getListingMessages() { - return listingMessages; - } - - public ListingMessageBuilder getListingMessage(String messageKey) { - final ListingMessage msg = listingMessages.get(messageKey); - if (msg == null) { - JANITOR.warn(MESSAGES, "Attempted to get unknown listing message with key {}", messageKey); - return new ListingMessageBuilder(UNKNOWN_LISTING_MESSAGE).with("key", () -> messageKey); - } - return new ListingMessageBuilder<>(msg); - } - - interface FileOpener { - Reader open(String filePath) throws IOException; - } - - public static final RegularMessage UNKNOWN_REGULAR_MESSAGE = new RegularMessage( - "UNKNOWN MESSAGE!", - null, - "A regular message was tried to be looked up, but was not found. " + - "Please report this to your bot maintainer/administrator.", - String.valueOf(0xFF0000), - null, - null, - null, - null, - null, - null, - null, - Collections.singletonList(new MessageEmbed.Field("Message Key", "${key}", false)) - ); - - public static final ListingMessage UNKNOWN_LISTING_MESSAGE = new ListingMessage( - "UNKNOWN MESSAGE!", - null, - "A listing message was tried to be looked up, but was not found. " + - "Please report this to your bot maintainer/administrator.", - String.valueOf(0xFF0000), - null, - null, - null, - null, - null, - null, - null, - null, - new ListingMessage.DescriptionEntry(null, ""), - Collections.singletonList(new MessageEmbed.Field("Message Key", "${key}", false)), - Collections.emptyList() - ); -} diff --git a/src/main/java/sciwhiz12/janitor/msg/RegularMessageBuilder.java b/src/main/java/sciwhiz12/janitor/msg/RegularMessageBuilder.java deleted file mode 100644 index 43a3370..0000000 --- a/src/main/java/sciwhiz12/janitor/msg/RegularMessageBuilder.java +++ /dev/null @@ -1,50 +0,0 @@ -package sciwhiz12.janitor.msg; - -import net.dv8tion.jda.api.entities.MessageChannel; -import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.requests.restaction.MessageAction; -import sciwhiz12.janitor.JanitorBot; -import sciwhiz12.janitor.msg.json.RegularMessage; -import sciwhiz12.janitor.msg.substitution.ICustomSubstitutions; -import sciwhiz12.janitor.msg.substitution.SubstitutionMap; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class RegularMessageBuilder implements ICustomSubstitutions { - private final RegularMessage message; - private final Map> customSubstitutions; - - public RegularMessageBuilder(RegularMessage message, Map> customSubstitutions) { - this.message = message; - this.customSubstitutions = customSubstitutions; - } - - public RegularMessageBuilder(RegularMessage message) { - this(message, new HashMap<>()); - } - - public RegularMessageBuilder apply(Consumer consumer) { - consumer.accept(this); - return this; - } - - public RegularMessageBuilder with(final String argument, final Supplier value) { - customSubstitutions.put(argument, value); - return this; - } - - public MessageEmbed build(SubstitutionMap substitutions) { - return message.create(substitutions.with(customSubstitutions)).build(); - } - - public MessageEmbed build(JanitorBot bot) { - return build(bot.getSubstitutions()); - } - - public MessageAction send(JanitorBot bot, MessageChannel channel) { - return channel.sendMessage(build(bot)); - } -} diff --git a/src/main/java/sciwhiz12/janitor/msg/substitution/ISubstitutor.java b/src/main/java/sciwhiz12/janitor/msg/substitution/ISubstitutor.java deleted file mode 100644 index cb13d3b..0000000 --- a/src/main/java/sciwhiz12/janitor/msg/substitution/ISubstitutor.java +++ /dev/null @@ -1,8 +0,0 @@ -package sciwhiz12.janitor.msg.substitution; - -import javax.annotation.Nullable; - -public interface ISubstitutor { - @Nullable - String substitute(@Nullable String text); -}