diff --git a/src/main/java/net/neoforged/camelot/BotMain.java b/src/main/java/net/neoforged/camelot/BotMain.java index 6e5d06e..77efff1 100644 --- a/src/main/java/net/neoforged/camelot/BotMain.java +++ b/src/main/java/net/neoforged/camelot/BotMain.java @@ -27,7 +27,7 @@ import net.neoforged.camelot.log.JoinsLogging; import net.neoforged.camelot.log.MessageLogging; import net.neoforged.camelot.log.ModerationActionRecorder; -import net.neoforged.camelot.module.CamelotModule; +import net.neoforged.camelot.module.api.CamelotModule; import net.neoforged.camelot.module.StatsModule; import net.neoforged.camelot.util.AuthUtil; import net.neoforged.camelot.util.Utils; diff --git a/src/main/java/net/neoforged/camelot/configuration/ConfigMigrator.java b/src/main/java/net/neoforged/camelot/configuration/ConfigMigrator.java index 78f66ed..4ad8781 100644 --- a/src/main/java/net/neoforged/camelot/configuration/ConfigMigrator.java +++ b/src/main/java/net/neoforged/camelot/configuration/ConfigMigrator.java @@ -9,7 +9,7 @@ import net.neoforged.camelot.config.module.ModuleConfiguration; import net.neoforged.camelot.config.module.Tricks; import net.neoforged.camelot.config.module.WebServer; -import net.neoforged.camelot.module.CamelotModule; +import net.neoforged.camelot.module.api.CamelotModule; import java.nio.file.Files; import java.nio.file.Path; diff --git a/src/main/java/net/neoforged/camelot/module/BanAppealModule.java b/src/main/java/net/neoforged/camelot/module/BanAppealModule.java index 9c1b818..c31099a 100644 --- a/src/main/java/net/neoforged/camelot/module/BanAppealModule.java +++ b/src/main/java/net/neoforged/camelot/module/BanAppealModule.java @@ -2,7 +2,6 @@ import com.google.auto.service.AutoService; import com.google.common.primitives.Ints; -import io.javalin.Javalin; import io.javalin.http.Context; import io.javalin.http.Cookie; import io.javalin.http.HttpStatus; @@ -40,6 +39,7 @@ import net.neoforged.camelot.db.transactionals.BanAppealsDAO; import net.neoforged.camelot.db.transactionals.ModLogsDAO; import net.neoforged.camelot.log.ModerationActionRecorder; +import net.neoforged.camelot.module.api.CamelotModule; import net.neoforged.camelot.server.WebServer; import net.neoforged.camelot.util.DateUtils; import net.neoforged.camelot.util.MailService; @@ -89,6 +89,12 @@ public class BanAppealModule extends CamelotModule.Base { public BanAppealModule() { super(BanAppeals.class); + accept(WebServerModule.SERVER, javalin -> { + javalin.get("/ban-appeals/discord", this::verifyOauth); + javalin.get("/ban-appeals/", this::onAccess); + javalin.post("/ban-appeals/followup/", this::onSubmitFollowup); + javalin.post("/ban-appeals/", this::onSubmitAppeal); + }); } private OAuthClient client; @@ -115,16 +121,6 @@ public void setup(JDA jda) { mail = MailService.from(config().getMail()); } - @Override - public void acceptFrom(String moduleId, Object object) { - if (moduleId.equals("webserver") && object instanceof Javalin javalin) { - javalin.get("/ban-appeals/discord", this::verifyOauth); - javalin.get("/ban-appeals/", this::onAccess); - javalin.post("/ban-appeals/followup/", this::onSubmitFollowup); - javalin.post("/ban-appeals/", this::onSubmitAppeal); - } - } - @Override public void registerListeners(JDABuilder builder) { builder.addEventListeners((EventListener) (GenericEvent gevent) -> { diff --git a/src/main/java/net/neoforged/camelot/module/CountersModule.java b/src/main/java/net/neoforged/camelot/module/CountersModule.java index dbfaf29..eba1df9 100644 --- a/src/main/java/net/neoforged/camelot/module/CountersModule.java +++ b/src/main/java/net/neoforged/camelot/module/CountersModule.java @@ -4,6 +4,7 @@ import net.dv8tion.jda.api.JDABuilder; import net.neoforged.camelot.config.module.Counters; import net.neoforged.camelot.listener.CountersListener; +import net.neoforged.camelot.module.api.CamelotModule; /** * The module for counters. diff --git a/src/main/java/net/neoforged/camelot/module/CustomPingsModule.java b/src/main/java/net/neoforged/camelot/module/CustomPingsModule.java index 9ee8c15..6db4ecc 100644 --- a/src/main/java/net/neoforged/camelot/module/CustomPingsModule.java +++ b/src/main/java/net/neoforged/camelot/module/CustomPingsModule.java @@ -6,6 +6,7 @@ import net.neoforged.camelot.commands.utility.CustomPingsCommand; import net.neoforged.camelot.config.module.CustomPings; import net.neoforged.camelot.listener.CustomPingListener; +import net.neoforged.camelot.module.api.CamelotModule; @AutoService(CamelotModule.class) public class CustomPingsModule extends CamelotModule.Base { diff --git a/src/main/java/net/neoforged/camelot/module/FilePreviewModule.java b/src/main/java/net/neoforged/camelot/module/FilePreviewModule.java index 778833c..64b31aa 100644 --- a/src/main/java/net/neoforged/camelot/module/FilePreviewModule.java +++ b/src/main/java/net/neoforged/camelot/module/FilePreviewModule.java @@ -7,6 +7,7 @@ import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; import net.neoforged.camelot.config.module.FilePreview; +import net.neoforged.camelot.module.api.CamelotModule; import net.neoforged.camelot.util.Utils; import java.net.URI; diff --git a/src/main/java/net/neoforged/camelot/module/InfoChannelsModule.java b/src/main/java/net/neoforged/camelot/module/InfoChannelsModule.java index ff52ce1..09e116d 100644 --- a/src/main/java/net/neoforged/camelot/module/InfoChannelsModule.java +++ b/src/main/java/net/neoforged/camelot/module/InfoChannelsModule.java @@ -7,6 +7,7 @@ import net.neoforged.camelot.commands.information.InfoChannelCommand; import net.neoforged.camelot.commands.information.RuleCommand; import net.neoforged.camelot.config.module.InfoChannels; +import net.neoforged.camelot.module.api.CamelotModule; import java.util.concurrent.TimeUnit; diff --git a/src/main/java/net/neoforged/camelot/module/MessageReferencingModule.java b/src/main/java/net/neoforged/camelot/module/MessageReferencingModule.java index e5c0fd5..3aec1f3 100644 --- a/src/main/java/net/neoforged/camelot/module/MessageReferencingModule.java +++ b/src/main/java/net/neoforged/camelot/module/MessageReferencingModule.java @@ -4,6 +4,7 @@ import net.dv8tion.jda.api.JDA; import net.neoforged.camelot.config.module.MessageReferencing; import net.neoforged.camelot.listener.ReferencingListener; +import net.neoforged.camelot.module.api.CamelotModule; /** * Module for message referencing using {@code .} replies. diff --git a/src/main/java/net/neoforged/camelot/module/MinecraftVerificationModule.java b/src/main/java/net/neoforged/camelot/module/MinecraftVerificationModule.java index e052bbf..1e8d492 100644 --- a/src/main/java/net/neoforged/camelot/module/MinecraftVerificationModule.java +++ b/src/main/java/net/neoforged/camelot/module/MinecraftVerificationModule.java @@ -2,7 +2,6 @@ import com.google.auto.service.AutoService; import com.jagrosh.jdautilities.command.CommandClientBuilder; -import io.javalin.Javalin; import io.javalin.http.BadRequestResponse; import io.javalin.http.Context; import io.javalin.http.Cookie; @@ -23,6 +22,7 @@ import net.neoforged.camelot.db.transactionals.McVerificationDAO; import net.neoforged.camelot.listener.ReferencingListener; import net.neoforged.camelot.log.ModerationActionRecorder; +import net.neoforged.camelot.module.api.CamelotModule; import net.neoforged.camelot.server.WebServer; import net.neoforged.camelot.util.Utils; import net.neoforged.camelot.util.oauth.OAuthClient; @@ -48,18 +48,14 @@ import static j2html.TagCreator.br; import static j2html.TagCreator.button; import static j2html.TagCreator.div; -import static j2html.TagCreator.footer; import static j2html.TagCreator.h1; import static j2html.TagCreator.h2; -import static j2html.TagCreator.h3; import static j2html.TagCreator.h5; import static j2html.TagCreator.hr; import static j2html.TagCreator.i; import static j2html.TagCreator.p; -import static j2html.TagCreator.pre; import static j2html.TagCreator.script; import static j2html.TagCreator.span; -import static j2html.TagCreator.sub; import static j2html.TagCreator.text; import static j2html.TagCreator.title; @@ -69,21 +65,7 @@ public class MinecraftVerificationModule extends CamelotModule.Base getDependencies() { - return Set.of("webserver"); - } - - @Override - public void acceptFrom(String moduleId, Object object) { - if (moduleId.equals("webserver") && object instanceof Javalin javalin) { + accept(WebServerModule.SERVER, javalin -> { javalin.get("/minecraft//verify", this::onVerifyRoot); javalin.post("/minecraft//verify", this::onVerifyPost); javalin.get("/minecraft/verify/discord", ctx -> verifyOauth(ctx, "discord_token", discord)); @@ -94,7 +76,17 @@ public void acceptFrom(String moduleId, Object object) { ctx.removeCookie("discord_token", path); ctx.removeCookie("xbox_token", path); }); - } + }); + } + + @Override + public String id() { + return "mc-verification"; + } + + @Override + public Set getDependencies() { + return Set.of("webserver"); } @Override diff --git a/src/main/java/net/neoforged/camelot/module/ModerationModule.java b/src/main/java/net/neoforged/camelot/module/ModerationModule.java index 776af19..5d96ac5 100644 --- a/src/main/java/net/neoforged/camelot/module/ModerationModule.java +++ b/src/main/java/net/neoforged/camelot/module/ModerationModule.java @@ -13,6 +13,7 @@ import net.neoforged.camelot.commands.moderation.UnmuteCommand; import net.neoforged.camelot.commands.moderation.WarnCommand; import net.neoforged.camelot.config.module.Moderation; +import net.neoforged.camelot.module.api.CamelotModule; /** * The module that provides moderation commands. diff --git a/src/main/java/net/neoforged/camelot/module/QuotesModule.java b/src/main/java/net/neoforged/camelot/module/QuotesModule.java index a2f7d5f..5428a90 100644 --- a/src/main/java/net/neoforged/camelot/module/QuotesModule.java +++ b/src/main/java/net/neoforged/camelot/module/QuotesModule.java @@ -15,6 +15,7 @@ import net.neoforged.camelot.config.module.Quotes; import net.neoforged.camelot.db.schemas.Quote; import net.neoforged.camelot.db.transactionals.QuotesDAO; +import net.neoforged.camelot.module.api.CamelotModule; import org.jetbrains.annotations.Nullable; import javax.imageio.ImageIO; diff --git a/src/main/java/net/neoforged/camelot/module/RemindersModule.java b/src/main/java/net/neoforged/camelot/module/RemindersModule.java index 559bfed..3a68684 100644 --- a/src/main/java/net/neoforged/camelot/module/RemindersModule.java +++ b/src/main/java/net/neoforged/camelot/module/RemindersModule.java @@ -17,7 +17,6 @@ import net.dv8tion.jda.api.interactions.components.ItemComponent; import net.dv8tion.jda.api.interactions.components.buttons.Button; import net.dv8tion.jda.api.requests.RestAction; -import net.dv8tion.jda.api.utils.Result; import net.dv8tion.jda.api.utils.TimeFormat; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import net.dv8tion.jda.api.utils.messages.MessageCreateData; @@ -29,11 +28,11 @@ import net.neoforged.camelot.db.transactionals.RemindersDAO; import net.neoforged.camelot.listener.DismissListener; import net.neoforged.camelot.listener.ReferencingListener; +import net.neoforged.camelot.module.api.CamelotModule; import net.neoforged.camelot.util.DateUtils; import net.neoforged.camelot.util.Utils; import java.awt.*; -import java.time.Duration; import java.time.Instant; import java.util.Collection; import java.util.EnumSet; @@ -43,7 +42,6 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; -import java.util.stream.Stream; @AutoService(CamelotModule.class) public class RemindersModule extends CamelotModule.Base { diff --git a/src/main/java/net/neoforged/camelot/module/StatsModule.java b/src/main/java/net/neoforged/camelot/module/StatsModule.java index 788e61d..8ff8632 100644 --- a/src/main/java/net/neoforged/camelot/module/StatsModule.java +++ b/src/main/java/net/neoforged/camelot/module/StatsModule.java @@ -4,6 +4,7 @@ import net.neoforged.camelot.Database; import net.neoforged.camelot.config.module.Statistics; import net.neoforged.camelot.db.transactionals.StatsDAO; +import net.neoforged.camelot.module.api.CamelotModule; import org.jdbi.v3.core.extension.ExtensionConsumer; /** diff --git a/src/main/java/net/neoforged/camelot/module/ThreadPingsModule.java b/src/main/java/net/neoforged/camelot/module/ThreadPingsModule.java index f5c4950..542ece5 100644 --- a/src/main/java/net/neoforged/camelot/module/ThreadPingsModule.java +++ b/src/main/java/net/neoforged/camelot/module/ThreadPingsModule.java @@ -6,6 +6,7 @@ import net.neoforged.camelot.commands.utility.ThreadPingsCommand; import net.neoforged.camelot.config.module.ThreadPings; import net.neoforged.camelot.listener.ThreadPingsListener; +import net.neoforged.camelot.module.api.CamelotModule; /** * Module for thread pings, for automatically mentioning a role in public threads created under a channel and diff --git a/src/main/java/net/neoforged/camelot/module/TricksModule.java b/src/main/java/net/neoforged/camelot/module/TricksModule.java index c1bdab7..84f61cf 100644 --- a/src/main/java/net/neoforged/camelot/module/TricksModule.java +++ b/src/main/java/net/neoforged/camelot/module/TricksModule.java @@ -19,6 +19,7 @@ import net.neoforged.camelot.db.transactionals.SlashTricksDAO; import net.neoforged.camelot.db.transactionals.TricksDAO; import net.neoforged.camelot.listener.TrickListener; +import net.neoforged.camelot.module.api.CamelotModule; import net.neoforged.camelot.script.SlashTrickManager; /** diff --git a/src/main/java/net/neoforged/camelot/module/WebServerModule.java b/src/main/java/net/neoforged/camelot/module/WebServerModule.java index 30fb593..8123dd9 100644 --- a/src/main/java/net/neoforged/camelot/module/WebServerModule.java +++ b/src/main/java/net/neoforged/camelot/module/WebServerModule.java @@ -6,6 +6,8 @@ import io.javalin.http.staticfiles.Location; import net.dv8tion.jda.api.JDA; import net.neoforged.camelot.BotMain; +import net.neoforged.camelot.module.api.CamelotModule; +import net.neoforged.camelot.module.api.ParameterType; import net.neoforged.camelot.server.WebServer; import java.io.IOException; @@ -14,6 +16,8 @@ @AutoService(CamelotModule.class) public class WebServerModule extends CamelotModule.Base { + public static final ParameterType SERVER = ParameterType.get("server", Javalin.class); + private WebServer webServer; public WebServerModule() { @@ -50,7 +54,7 @@ public void setup(JDA jda) { }); }), config().getPort()); - BotMain.forEachModule(module -> module.acceptFrom(id(), webServer.javalin)); + BotMain.forEachModule(module -> module.acceptParameter(SERVER, webServer.javalin)); this.webServer.run(); } diff --git a/src/main/java/net/neoforged/camelot/module/CamelotModule.java b/src/main/java/net/neoforged/camelot/module/api/CamelotModule.java similarity index 75% rename from src/main/java/net/neoforged/camelot/module/CamelotModule.java rename to src/main/java/net/neoforged/camelot/module/api/CamelotModule.java index e1760f7..d2284c9 100644 --- a/src/main/java/net/neoforged/camelot/module/CamelotModule.java +++ b/src/main/java/net/neoforged/camelot/module/api/CamelotModule.java @@ -1,20 +1,15 @@ -package net.neoforged.camelot.module; +package net.neoforged.camelot.module.api; import com.jagrosh.jdautilities.command.CommandClientBuilder; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; import net.neoforged.camelot.config.CamelotConfig; -import net.neoforged.camelot.config.module.GHAuth; import net.neoforged.camelot.config.module.ModuleConfiguration; -import net.neoforged.camelot.util.AuthUtil; -import org.kohsuke.github.GitHub; -import org.kohsuke.github.GitHubBuilder; - -import java.io.IOException; -import java.nio.file.Files; -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; + +import java.util.IdentityHashMap; +import java.util.Map; import java.util.Set; +import java.util.function.Consumer; /** * A camelot module is a part of the bot that can be disabled and is loaded via a {@link java.util.ServiceLoader}. @@ -53,10 +48,10 @@ default void setup(JDA jda) { /** * Accept an object from another module. * - * @param moduleId the ID of the module sending the object - * @param object the sent object + * @param type the type of the object + * @param object the sent object */ - default void acceptFrom(String moduleId, Object object) { + default void acceptParameter(ParameterType type, T object) { } @@ -86,16 +81,22 @@ default boolean shouldLoad() { /** * Base class for {@link CamelotModule camelot modules}. + * * @param the configuration type */ abstract class Base implements CamelotModule { private final Class configType; + private final Map, Consumer> parameters = new IdentityHashMap<>(); private C config; protected Base(Class configType) { this.configType = configType; } + protected void accept(ParameterType type, Consumer acceptor) { + parameters.put(type, acceptor); + } + @Override public C config() { if (config == null) { @@ -104,6 +105,13 @@ public C config() { return config; } + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + public void acceptParameter(ParameterType type, T object) { + Consumer accept = parameters.get(type); + if (accept != null) accept.accept(object); + } + @Override public Class configType() { return configType; diff --git a/src/main/java/net/neoforged/camelot/module/api/ParameterType.java b/src/main/java/net/neoforged/camelot/module/api/ParameterType.java new file mode 100644 index 0000000..f326c8d --- /dev/null +++ b/src/main/java/net/neoforged/camelot/module/api/ParameterType.java @@ -0,0 +1,70 @@ +package net.neoforged.camelot.module.api; + +import com.google.common.collect.MapMaker; + +import java.util.concurrent.ConcurrentMap; + +/** + * The type of a parameter passed to a module. + * + * @param the type of the parameter + */ +@SuppressWarnings({"unchecked", "rawtypes"}) +public final class ParameterType { + private static final ConcurrentMap> INTERNER = new MapMaker() + .weakKeys() + .makeMap(); + + private final String name; + private final Class type; + private final Class> source; + + private ParameterType(String name, Class type, Class> source) { + this.name = name; + this.type = type; + this.source = source; + } + + /** + * {@return the name of the parameter} + */ + public String getName() { + return name; + } + + /** + * {@return the module source of the parameter} + */ + public Class> getSource() { + return source; + } + + /** + * {@return the type of the parameter} + */ + public Class getType() { + return type; + } + + /** + * {@return an interned parameter} + */ + public static ParameterType get(String name, Class type, Class> source) { + return (ParameterType) INTERNER.computeIfAbsent(new Parameters(name, type, source), _ -> new ParameterType<>(name, type, source)); + } + + /** + * {@return an interned parameter} + * @apiNote must be called from within a module class + */ + public static ParameterType get(String name, Class type) { + var caller = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE) + .getCallerClass(); + if (!CamelotModule.class.isAssignableFrom(caller)) { + throw new IllegalCallerException("ParameterType#get not called from a module"); + } + return (ParameterType) INTERNER.computeIfAbsent(new Parameters(name, type, caller), _ -> new ParameterType<>(name, type, (Class)caller)); + } + + private record Parameters(String name, Class type, Class source) {} +}