From 6bf34d88a779c01ad4061ad8b0f38dc1803a2b2d Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Tue, 6 Jan 2026 22:50:13 +0000 Subject: [PATCH 01/15] chore: move secrets out of `config.json` into `secrets.json` --- application/config.json.template | 6 ------ application/secrets.json.template | 8 ++++++++ 2 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 application/secrets.json.template diff --git a/application/config.json.template b/application/config.json.template index 5cfe9ac38e..a2a14bed45 100644 --- a/application/config.json.template +++ b/application/config.json.template @@ -1,6 +1,4 @@ { - "token": "", - "githubApiKey": "", "databasePath": "local-database.db", "projectWebsite": "https://github.com/Together-Java/TJ-Bot", "discordGuildInvite": "https://discord.com/invite/XXFUXzK", @@ -159,12 +157,8 @@ ], "githubReferencingEnabledChannelPattern": "server-suggestions|tjbot-discussion|modernjava-discussion", "githubRepositories": [403389278,587644974,601602394], - "logInfoChannelWebhook": "", - "logErrorChannelWebhook": "", - "openaiApiKey": "", "sourceCodeBaseUrl": "https://github.com/Together-Java/TJ-Bot/blob/master/application/src/main/java/", "jshell": { - "baseUrl": "", "rateLimitWindowSeconds": 10, "rateLimitRequestsInWindow": 3 }, diff --git a/application/secrets.json.template b/application/secrets.json.template new file mode 100644 index 0000000000..49c1012549 --- /dev/null +++ b/application/secrets.json.template @@ -0,0 +1,8 @@ +{ + "token": "", + "githubApiKey": "", + "logInfoChannelWebhook": "", + "logErrorChannelWebhook": "", + "openaiApiKey": "", + "jshellBaseUrl": "" +} From 1798bd738d7a3102158bfa93417c1e7a3b839cc3 Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Tue, 6 Jan 2026 22:50:49 +0000 Subject: [PATCH 02/15] feat: create Secrets.java to contain project secrets --- .../togetherjava/tjbot/secrets/Secrets.java | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 application/src/main/java/org/togetherjava/tjbot/secrets/Secrets.java diff --git a/application/src/main/java/org/togetherjava/tjbot/secrets/Secrets.java b/application/src/main/java/org/togetherjava/tjbot/secrets/Secrets.java new file mode 100644 index 0000000000..fd0b19fb7e --- /dev/null +++ b/application/src/main/java/org/togetherjava/tjbot/secrets/Secrets.java @@ -0,0 +1,99 @@ +package org.togetherjava.tjbot.secrets; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Objects; + +public class Secrets { + private final String token; + private final String githubApiKey; + private final String logInfoChannelWebhook; + private final String logErrorChannelWebhook; + private final String openaiApiKey; + private final String jshellBaseUrl; + + @SuppressWarnings("ConstructorWithTooManyParameters") + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + private Secrets(@JsonProperty(value = "token", required = true) String token, + @JsonProperty(value = "githubApiKey", required = true) String githubApiKey, + @JsonProperty(value = "logInfoChannelWebhook", + required = true) String logInfoChannelWebhook, + @JsonProperty(value = "logErrorChannelWebhook", + required = true) String logErrorChannelWebhook, + @JsonProperty(value = "openaiApiKey", required = true) String openaiApiKey, + @JsonProperty(value = "jshellBaseUrl", required = true) String jshellBaseUrl) { + this.token = Objects.requireNonNull(token); + this.githubApiKey = Objects.requireNonNull(githubApiKey); + this.logInfoChannelWebhook = Objects.requireNonNull(logInfoChannelWebhook); + this.logErrorChannelWebhook = Objects.requireNonNull(logErrorChannelWebhook); + this.openaiApiKey = Objects.requireNonNull(openaiApiKey); + this.jshellBaseUrl = Objects.requireNonNull(jshellBaseUrl); + } + + public static Secrets load(Path path) throws IOException { + return new ObjectMapper().registerModule(new JavaTimeModule()) + .readValue(path.toFile(), Secrets.class); + } + + /** + * Gets the token of the Discord bot to connect this application to. + * + * @return the Discord bot token + */ + public String getToken() { + return token; + } + + /** + * Gets the API Key of GitHub. + * + * @return the API Key + * @see Create + * a GitHub key + */ + public String getGitHubApiKey() { + return githubApiKey; + } + + /** + * The Discord channel webhook for posting log messages with levels INFO, DEBUG and TRACE. + * + * @return the webhook URL + */ + public String getLogInfoChannelWebhook() { + return logInfoChannelWebhook; + } + + /** + * The Discord channel webhook for posting log messages with levels FATAL, ERROR and WARNING. + * + * @return the webhook URL + */ + public String getLogErrorChannelWebhook() { + return logErrorChannelWebhook; + } + + /** + * The OpenAI token needed for communicating with OpenAI ChatGPT. + * + * @return the OpenAI API Token + */ + public String getOpenaiApiKey() { + return openaiApiKey; + } + + /** + * The base URL for the jshell REST API. + * + * @return the jshell base url + */ + public String getJshellBaseUrl() { + return jshellBaseUrl; + } +} From e084d42da77fffc165c8926907ab95db989ad46c Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Tue, 6 Jan 2026 22:51:16 +0000 Subject: [PATCH 03/15] chore: update old references of secrets in config to secrets class --- .../org/togetherjava/tjbot/Application.java | 22 ++++-- .../org/togetherjava/tjbot/config/Config.java | 68 +------------------ .../tjbot/config/JShellConfig.java | 9 +-- .../togetherjava/tjbot/features/Features.java | 15 ++-- .../features/chatgpt/ChatGptService.java | 8 +-- .../FileSharingMessageListener.java | 5 +- .../features/github/GitHubReference.java | 9 ++- .../tjbot/features/jshell/JShellEval.java | 4 +- .../tjbot/features/system/BotCore.java | 5 +- .../tjbot/logging/discord/DiscordLogging.java | 14 ++-- 10 files changed, 56 insertions(+), 103 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/Application.java b/application/src/main/java/org/togetherjava/tjbot/Application.java index 4c228cb02a..25f3dd1c05 100644 --- a/application/src/main/java/org/togetherjava/tjbot/Application.java +++ b/application/src/main/java/org/togetherjava/tjbot/Application.java @@ -15,6 +15,7 @@ import org.togetherjava.tjbot.features.system.BotCore; import org.togetherjava.tjbot.logging.LogMarkers; import org.togetherjava.tjbot.logging.discord.DiscordLogging; +import org.togetherjava.tjbot.secrets.Secrets; import java.io.IOException; import java.nio.file.Files; @@ -34,6 +35,7 @@ private Application() { private static final Logger logger = LoggerFactory.getLogger(Application.class); private static final String DEFAULT_CONFIG_PATH = "config.json"; + private static final String DEFAULT_SECRETS_PATH = "secrets.json"; /** * Starts the application. @@ -58,11 +60,21 @@ public static void main(final String[] args) { return; } + Path secretsPath = Path.of(args.length == 1 ? args[0] : DEFAULT_SECRETS_PATH); + Secrets secrets; + try { + secrets = Secrets.load(secretsPath); + } catch (IOException e) { + logger.error("Unable to load the configuration file from path '{}'", + configPath.toAbsolutePath(), e); + return; + } + Thread.setDefaultUncaughtExceptionHandler(Application::onUncaughtException); Runtime.getRuntime().addShutdownHook(new Thread(Application::onShutdown)); - DiscordLogging.startDiscordLogging(config); + DiscordLogging.startDiscordLogging(config, secrets); - runBot(config); + runBot(config, secrets); } /** @@ -71,7 +83,7 @@ public static void main(final String[] args) { * @param config the configuration to run the bot with */ @SuppressWarnings("WeakerAccess") - public static void runBot(Config config) { + public static void runBot(Config config, Secrets secrets) { logger.info("Starting bot..."); Path databasePath = Path.of(config.getDatabasePath()); @@ -82,13 +94,13 @@ public static void runBot(Config config) { } Database database = new Database("jdbc:sqlite:" + databasePath.toAbsolutePath()); - JDA jda = JDABuilder.createDefault(config.getToken()) + JDA jda = JDABuilder.createDefault(secrets.getToken()) .enableIntents(GatewayIntent.GUILD_MEMBERS, GatewayIntent.MESSAGE_CONTENT) .build(); jda.awaitReady(); - BotCore core = new BotCore(jda, database, config); + BotCore core = new BotCore(jda, database, config, secrets); CommandReloading.reloadCommands(jda, core); core.scheduleRoutines(jda); diff --git a/application/src/main/java/org/togetherjava/tjbot/config/Config.java b/application/src/main/java/org/togetherjava/tjbot/config/Config.java index 60e6622cbc..d72bac6fcc 100644 --- a/application/src/main/java/org/togetherjava/tjbot/config/Config.java +++ b/application/src/main/java/org/togetherjava/tjbot/config/Config.java @@ -16,8 +16,6 @@ * Configuration of the application. Create instances using {@link #load(Path)}. */ public final class Config { - private final String token; - private final String githubApiKey; private final String databasePath; private final String projectWebsite; private final String discordGuildInvite; @@ -36,11 +34,8 @@ public final class Config { private final HelpSystemConfig helpSystem; private final List blacklistedFileExtension; private final String mediaOnlyChannelPattern; - private final String logInfoChannelWebhook; - private final String logErrorChannelWebhook; private final String githubReferencingEnabledChannelPattern; private final List githubRepositories; - private final String openaiApiKey; private final String sourceCodeBaseUrl; private final JShellConfig jshell; private final HelperPruneConfig helperPruneConfig; @@ -52,9 +47,7 @@ public final class Config { @SuppressWarnings("ConstructorWithTooManyParameters") @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - private Config(@JsonProperty(value = "token", required = true) String token, - @JsonProperty(value = "githubApiKey", required = true) String githubApiKey, - @JsonProperty(value = "databasePath", required = true) String databasePath, + private Config(@JsonProperty(value = "databasePath", required = true) String databasePath, @JsonProperty(value = "projectWebsite", required = true) String projectWebsite, @JsonProperty(value = "discordGuildInvite", required = true) String discordGuildInvite, @JsonProperty(value = "modAuditLogChannelPattern", @@ -82,15 +75,11 @@ private Config(@JsonProperty(value = "token", required = true) String token, required = true) String mediaOnlyChannelPattern, @JsonProperty(value = "blacklistedFileExtension", required = true) List blacklistedFileExtension, - @JsonProperty(value = "logInfoChannelWebhook", - required = true) String logInfoChannelWebhook, - @JsonProperty(value = "logErrorChannelWebhook", - required = true) String logErrorChannelWebhook, + @JsonProperty(value = "githubReferencingEnabledChannelPattern", required = true) String githubReferencingEnabledChannelPattern, @JsonProperty(value = "githubRepositories", required = true) List githubRepositories, - @JsonProperty(value = "openaiApiKey", required = true) String openaiApiKey, @JsonProperty(value = "sourceCodeBaseUrl", required = true) String sourceCodeBaseUrl, @JsonProperty(value = "jshell", required = true) JShellConfig jshell, @JsonProperty(value = "memberCountCategoryPattern", @@ -103,8 +92,6 @@ private Config(@JsonProperty(value = "token", required = true) String token, @JsonProperty(value = "selectRolesChannelPattern", required = true) String selectRolesChannelPattern, @JsonProperty(value = "topHelpers", required = true) TopHelpersConfig topHelpers) { - this.token = Objects.requireNonNull(token); - this.githubApiKey = Objects.requireNonNull(githubApiKey); this.databasePath = Objects.requireNonNull(databasePath); this.projectWebsite = Objects.requireNonNull(projectWebsite); this.memberCountCategoryPattern = Objects.requireNonNull(memberCountCategoryPattern); @@ -125,12 +112,9 @@ private Config(@JsonProperty(value = "token", required = true) String token, this.helpSystem = Objects.requireNonNull(helpSystem); this.mediaOnlyChannelPattern = Objects.requireNonNull(mediaOnlyChannelPattern); this.blacklistedFileExtension = Objects.requireNonNull(blacklistedFileExtension); - this.logInfoChannelWebhook = Objects.requireNonNull(logInfoChannelWebhook); - this.logErrorChannelWebhook = Objects.requireNonNull(logErrorChannelWebhook); this.githubReferencingEnabledChannelPattern = Objects.requireNonNull(githubReferencingEnabledChannelPattern); this.githubRepositories = Objects.requireNonNull(githubRepositories); - this.openaiApiKey = Objects.requireNonNull(openaiApiKey); this.sourceCodeBaseUrl = Objects.requireNonNull(sourceCodeBaseUrl); this.jshell = Objects.requireNonNull(jshell); this.helperPruneConfig = Objects.requireNonNull(helperPruneConfig); @@ -191,27 +175,6 @@ public String getProjectsChannelPattern() { return projectsChannelPattern; } - /** - * Gets the token of the Discord bot to connect this application to. - * - * @return the Discord bot token - */ - public String getToken() { - return token; - } - - /** - * Gets the API Key of GitHub. - * - * @return the API Key - * @see Create - * a GitHub key - */ - public String getGitHubApiKey() { - return githubApiKey; - } - /** * Gets the path where the database of the application is located at. * @@ -356,33 +319,6 @@ public List getGitHubRepositories() { return githubRepositories; } - /** - * The Discord channel webhook for posting log messages with levels INFO, DEBUG and TRACE. - * - * @return the webhook URL - */ - public String getLogInfoChannelWebhook() { - return logInfoChannelWebhook; - } - - /** - * The Discord channel webhook for posting log messages with levels FATAL, ERROR and WARNING. - * - * @return the webhook URL - */ - public String getLogErrorChannelWebhook() { - return logErrorChannelWebhook; - } - - /** - * The OpenAI token needed for communicating with OpenAI ChatGPT. - * - * @return the OpenAI API Token - */ - public String getOpenaiApiKey() { - return openaiApiKey; - } - /** * The base URL of the source code of this bot. E.g. * {@code getSourceCodeBaseUrl() + "/org/togetherjava/tjbot/config/Config.java"} would point to diff --git a/application/src/main/java/org/togetherjava/tjbot/config/JShellConfig.java b/application/src/main/java/org/togetherjava/tjbot/config/JShellConfig.java index 91b85eb2d8..4ff45bd06e 100644 --- a/application/src/main/java/org/togetherjava/tjbot/config/JShellConfig.java +++ b/application/src/main/java/org/togetherjava/tjbot/config/JShellConfig.java @@ -3,30 +3,25 @@ import org.togetherjava.tjbot.features.utils.RateLimiter; -import java.util.Objects; /** * JShell config. - * - * @param baseUrl the base url of the JShell REST API + * * @param rateLimitWindowSeconds the number of seconds of the {@link RateLimiter rate limiter} for * jshell commands and code actions * @param rateLimitRequestsInWindow the number of requests of the {@link RateLimiter rate limiter} * for jshell commands and code actions */ -public record JShellConfig(String baseUrl, int rateLimitWindowSeconds, - int rateLimitRequestsInWindow) { +public record JShellConfig(int rateLimitWindowSeconds, int rateLimitRequestsInWindow) { /** * Creates a JShell config. * - * @param baseUrl the base url of the JShell REST API, must be not null * @param rateLimitWindowSeconds the number of seconds of the {@link RateLimiter rate limiter} * for jshell commands and code actions, must be higher than 0 * @param rateLimitRequestsInWindow the number of requests of the {@link RateLimiter rate * limiter} for jshell commands and code actions, must be higher than 0 */ public JShellConfig { - Objects.requireNonNull(baseUrl); if (rateLimitWindowSeconds < 0) { throw new IllegalArgumentException( "Illegal rateLimitWindowSeconds : " + rateLimitWindowSeconds); diff --git a/application/src/main/java/org/togetherjava/tjbot/features/Features.java b/application/src/main/java/org/togetherjava/tjbot/features/Features.java index 8ed07eff6c..756fc37aa7 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/Features.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/Features.java @@ -78,6 +78,7 @@ import org.togetherjava.tjbot.features.tophelper.TopHelpersMessageListener; import org.togetherjava.tjbot.features.tophelper.TopHelpersPurgeMessagesRoutine; import org.togetherjava.tjbot.features.tophelper.TopHelpersService; +import org.togetherjava.tjbot.secrets.Secrets; import java.util.ArrayList; import java.util.Collection; @@ -88,7 +89,7 @@ * it with the system. *

* To add a new slash command, extend the commands returned by - * {@link #createFeatures(JDA, Database, Config)}. + * {@link #createFeatures(JDA, Database, Config, Secrets)}. */ public class Features { private Features() { @@ -106,19 +107,21 @@ private Features() { * @param config the configuration features should use * @return a collection of all features */ - public static Collection createFeatures(JDA jda, Database database, Config config) { + public static Collection createFeatures(JDA jda, Database database, Config config, + Secrets secrets) { FeatureBlacklistConfig blacklistConfig = config.getFeatureBlacklistConfig(); - JShellEval jshellEval = new JShellEval(config.getJshell(), config.getGitHubApiKey()); + JShellEval jshellEval = new JShellEval(config.getJshell(), secrets.getJshellBaseUrl(), + secrets.getGitHubApiKey()); TagSystem tagSystem = new TagSystem(database); BookmarksSystem bookmarksSystem = new BookmarksSystem(config, database); ModerationActionsStore actionsStore = new ModerationActionsStore(database); ModAuditLogWriter modAuditLogWriter = new ModAuditLogWriter(config); ScamHistoryStore scamHistoryStore = new ScamHistoryStore(database); - GitHubReference githubReference = new GitHubReference(config); + GitHubReference githubReference = new GitHubReference(config, secrets); CodeMessageHandler codeMessageHandler = new CodeMessageHandler(blacklistConfig.special(), jshellEval); - ChatGptService chatGptService = new ChatGptService(config); + ChatGptService chatGptService = new ChatGptService(secrets); HelpSystemHelper helpSystemHelper = new HelpSystemHelper(config, database, chatGptService); HelpThreadLifecycleListener helpThreadLifecycleListener = new HelpThreadLifecycleListener(helpSystemHelper, database); @@ -153,7 +156,7 @@ public static Collection createFeatures(JDA jda, Database database, Con features.add(new SuggestionsUpDownVoter(config)); features.add(new ScamBlocker(actionsStore, scamHistoryStore, config)); features.add(new MediaOnlyChannelListener(config)); - features.add(new FileSharingMessageListener(config)); + features.add(new FileSharingMessageListener(config, secrets)); features.add(new BlacklistedAttachmentListener(config, modAuditLogWriter)); features.add(githubReference); features.add(codeMessageHandler); diff --git a/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptService.java b/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptService.java index a6fdcbcb9d..1a47661d68 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptService.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptService.java @@ -8,7 +8,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.togetherjava.tjbot.config.Config; +import org.togetherjava.tjbot.secrets.Secrets; import javax.annotation.Nullable; @@ -54,10 +54,10 @@ public class ChatGptService { /** * Creates instance of ChatGPTService * - * @param config needed for token to OpenAI API. + * @param secrets needed for token to OpenAI API. */ - public ChatGptService(Config config) { - String apiKey = config.getOpenaiApiKey(); + public ChatGptService(Secrets secrets) { + String apiKey = secrets.getOpenaiApiKey(); boolean keyIsDefaultDescription = apiKey.startsWith("<") && apiKey.endsWith(">"); if (apiKey.isBlank() || keyIsDefaultDescription) { isDisabled = true; diff --git a/application/src/main/java/org/togetherjava/tjbot/features/filesharing/FileSharingMessageListener.java b/application/src/main/java/org/togetherjava/tjbot/features/filesharing/FileSharingMessageListener.java index c040eaf065..b59201013c 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/filesharing/FileSharingMessageListener.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/filesharing/FileSharingMessageListener.java @@ -21,6 +21,7 @@ import org.togetherjava.tjbot.features.componentids.ComponentIdGenerator; import org.togetherjava.tjbot.features.componentids.ComponentIdInteractor; import org.togetherjava.tjbot.features.utils.Guilds; +import org.togetherjava.tjbot.secrets.Secrets; import java.io.IOException; import java.io.InputStream; @@ -58,9 +59,9 @@ public final class FileSharingMessageListener extends MessageReceiverAdapter * @param config used to get api key and channel names. * @see org.togetherjava.tjbot.features.Features */ - public FileSharingMessageListener(Config config) { + public FileSharingMessageListener(Config config, Secrets secrets) { super(Pattern.compile(".*")); - githubApiKey = config.getGitHubApiKey(); + githubApiKey = secrets.getGitHubApiKey(); isHelpForumName = Pattern.compile(config.getHelpSystem().getHelpForumPattern()).asMatchPredicate(); isSoftModRole = Pattern.compile(config.getSoftModerationRolePattern()).asMatchPredicate(); diff --git a/application/src/main/java/org/togetherjava/tjbot/features/github/GitHubReference.java b/application/src/main/java/org/togetherjava/tjbot/features/github/GitHubReference.java index 960587e8a4..7dee6495f1 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/github/GitHubReference.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/github/GitHubReference.java @@ -21,6 +21,7 @@ import org.togetherjava.tjbot.config.Config; import org.togetherjava.tjbot.features.MessageReceiverAdapter; +import org.togetherjava.tjbot.secrets.Secrets; import java.awt.Color; import java.io.FileNotFoundException; @@ -67,6 +68,7 @@ public final class GitHubReference extends MessageReceiverAdapter { DateTimeFormatter.ofPattern("dd MMM, yyyy").withZone(ZoneOffset.UTC); private final Predicate hasGithubIssueReferenceEnabled; private final Config config; + private final Secrets secrets; /** * The repositories that are searched when looking for an issue. @@ -81,8 +83,9 @@ public final class GitHubReference extends MessageReceiverAdapter { * * @param config The Config to get allowed channel pattern for feature. */ - public GitHubReference(Config config) { + public GitHubReference(Config config, Secrets secrets) { this.config = config; + this.secrets = secrets; this.hasGithubIssueReferenceEnabled = Pattern.compile(config.getGitHubReferencingEnabledChannelPattern()) .asMatchPredicate(); @@ -96,7 +99,7 @@ private void acquireRepositories() { try { repositories = new ArrayList<>(); - GitHub githubApi = GitHub.connectUsingOAuth(config.getGitHubApiKey()); + GitHub githubApi = GitHub.connectUsingOAuth(secrets.getGitHubApiKey()); for (long repoId : config.getGitHubRepositories()) { repositories.add(githubApi.getRepositoryById(repoId)); @@ -104,7 +107,7 @@ private void acquireRepositories() { } catch (IOException ex) { logger.warn( "The GitHub key ({}) used in this config is invalid. Skipping GitHubReference feature – {}", - config.getGitHubApiKey(), ex.getMessage()); + secrets.getGitHubApiKey(), ex.getMessage()); } } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/jshell/JShellEval.java b/application/src/main/java/org/togetherjava/tjbot/features/jshell/JShellEval.java index cd965d128d..6acf2c2b89 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/jshell/JShellEval.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/jshell/JShellEval.java @@ -38,10 +38,10 @@ public class JShellEval { * @param config the JShell configuration to use * @param gistApiToken token of Gist api in case a JShell result is uploaded here */ - public JShellEval(JShellConfig config, String gistApiToken) { + public JShellEval(JShellConfig config, String baseUrl, String gistApiToken) { this.gistApiToken = gistApiToken; this.api = new JShellApi(new ObjectMapper().registerModule(new Jdk17SealedClassesModule()), - config.baseUrl()); + baseUrl); this.renderer = new ResultRenderer(); this.rateLimiter = new RateLimiter(Duration.ofSeconds(config.rateLimitWindowSeconds()), diff --git a/application/src/main/java/org/togetherjava/tjbot/features/system/BotCore.java b/application/src/main/java/org/togetherjava/tjbot/features/system/BotCore.java index 7c337e2efb..43d739f83d 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/system/BotCore.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/system/BotCore.java @@ -45,6 +45,7 @@ import org.togetherjava.tjbot.features.componentids.ComponentIdParser; import org.togetherjava.tjbot.features.componentids.ComponentIdStore; import org.togetherjava.tjbot.features.componentids.InvalidComponentIdFormatException; +import org.togetherjava.tjbot.secrets.Secrets; import java.util.Collection; import java.util.HashMap; @@ -95,9 +96,9 @@ public final class BotCore extends ListenerAdapter implements CommandProvider { * @param database the database that commands may use to persist data * @param config the configuration to use for this system */ - public BotCore(JDA jda, Database database, Config config) { + public BotCore(JDA jda, Database database, Config config, Secrets secrets) { this.config = config; - Collection features = Features.createFeatures(jda, database, config); + Collection features = Features.createFeatures(jda, database, config, secrets); // Message receivers features.stream() diff --git a/application/src/main/java/org/togetherjava/tjbot/logging/discord/DiscordLogging.java b/application/src/main/java/org/togetherjava/tjbot/logging/discord/DiscordLogging.java index f7afed7fde..44ad330505 100644 --- a/application/src/main/java/org/togetherjava/tjbot/logging/discord/DiscordLogging.java +++ b/application/src/main/java/org/togetherjava/tjbot/logging/discord/DiscordLogging.java @@ -14,6 +14,7 @@ import org.togetherjava.tjbot.config.Config; import org.togetherjava.tjbot.logging.LogMarkers; +import org.togetherjava.tjbot.secrets.Secrets; import java.net.URI; import java.util.Optional; @@ -34,23 +35,24 @@ private DiscordLogging() { *

* Disables the feature if the config is set up incorrectly. * - * @param botConfig to get the logging details from, such as the Discord webhook urls + * @param botConfig to get the logging details from + * @param secrets to get the details such as the Discord webhook urls */ - public static void startDiscordLogging(Config botConfig) { + public static void startDiscordLogging(Config botConfig, Secrets secrets) { LoggerContext context = (LoggerContext) LogManager.getContext(false); Configuration logConfig = context.getConfiguration(); - addAppenders(logConfig, botConfig); + addAppenders(logConfig, botConfig, secrets); context.updateLoggers(); } - private static void addAppenders(Configuration logConfig, Config botConfig) { - parseWebhookUri(botConfig.getLogInfoChannelWebhook(), "info") + private static void addAppenders(Configuration logConfig, Config botConfig, Secrets secrets) { + parseWebhookUri(secrets.getLogInfoChannelWebhook(), "info") .ifPresent(webhookUri -> addDiscordLogAppender("DiscordInfo", createInfoRangeFilter(), webhookUri, botConfig.getSourceCodeBaseUrl(), logConfig)); - parseWebhookUri(botConfig.getLogErrorChannelWebhook(), "error") + parseWebhookUri(secrets.getLogErrorChannelWebhook(), "error") .ifPresent(webhookUri -> addDiscordLogAppender("DiscordError", createErrorRangeFilter(), webhookUri, botConfig.getSourceCodeBaseUrl(), logConfig)); } From aa934caf7dbcda7a7d3b32f0ce34983ca2575042 Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Tue, 6 Jan 2026 22:52:25 +0000 Subject: [PATCH 04/15] chore: add secrets.json to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 62ff123564..f2a80086fd 100644 --- a/.gitignore +++ b/.gitignore @@ -145,6 +145,7 @@ gradle-app.setting # End of https://www.toptal.com/developers/gitignore/api/netbeans,intellij,java,gradle,eclipse application/db/ config.json +secrets.json application/config.json *.db *.db-shm From 6bc7da5706b5377c1762edb0727bffb27d4eeed3 Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Tue, 6 Jan 2026 23:05:30 +0000 Subject: [PATCH 05/15] chore: load secrets from the resources folder --- .gitignore | 1 - .../org/togetherjava/tjbot/Application.java | 21 ++++++++++++------- .../org/togetherjava/tjbot/config/Config.java | 6 +++--- .../main/resources/config.json} | 0 4 files changed, 17 insertions(+), 11 deletions(-) rename application/{config.json.template => src/main/resources/config.json} (100%) diff --git a/.gitignore b/.gitignore index f2a80086fd..020eada532 100644 --- a/.gitignore +++ b/.gitignore @@ -144,7 +144,6 @@ gradle-app.setting # End of https://www.toptal.com/developers/gitignore/api/netbeans,intellij,java,gradle,eclipse application/db/ -config.json secrets.json application/config.json *.db diff --git a/application/src/main/java/org/togetherjava/tjbot/Application.java b/application/src/main/java/org/togetherjava/tjbot/Application.java index 25f3dd1c05..1aa82c66b0 100644 --- a/application/src/main/java/org/togetherjava/tjbot/Application.java +++ b/application/src/main/java/org/togetherjava/tjbot/Application.java @@ -18,6 +18,7 @@ import org.togetherjava.tjbot.secrets.Secrets; import java.io.IOException; +import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.sql.SQLException; @@ -34,7 +35,7 @@ private Application() { } private static final Logger logger = LoggerFactory.getLogger(Application.class); - private static final String DEFAULT_CONFIG_PATH = "config.json"; + private static final String DEFAULT_CONFIG_PATH = "/config.json"; private static final String DEFAULT_SECRETS_PATH = "secrets.json"; /** @@ -50,13 +51,19 @@ public static void main(final String[] args) { + DEFAULT_CONFIG_PATH + "' will be assumed."); } - Path configPath = Path.of(args.length == 1 ? args[0] : DEFAULT_CONFIG_PATH); + String configPath = args.length == 1 ? args[0] : DEFAULT_CONFIG_PATH; Config config; - try { - config = Config.load(configPath); + + try (InputStream stream = Application.class.getResourceAsStream(configPath)) { + if (stream == null) { + throw new IOException("InputStream is null when loading " + configPath); + } + + String content = new String(stream.readAllBytes()); + config = Config.load(content); + } catch (IOException e) { - logger.error("Unable to load the configuration file from path '{}'", - configPath.toAbsolutePath(), e); + logger.error("Unable to load the configuration file from path '{}'", configPath, e); return; } @@ -66,7 +73,7 @@ public static void main(final String[] args) { secrets = Secrets.load(secretsPath); } catch (IOException e) { logger.error("Unable to load the configuration file from path '{}'", - configPath.toAbsolutePath(), e); + secretsPath.toAbsolutePath(), e); return; } diff --git a/application/src/main/java/org/togetherjava/tjbot/config/Config.java b/application/src/main/java/org/togetherjava/tjbot/config/Config.java index d72bac6fcc..a406292532 100644 --- a/application/src/main/java/org/togetherjava/tjbot/config/Config.java +++ b/application/src/main/java/org/togetherjava/tjbot/config/Config.java @@ -127,13 +127,13 @@ private Config(@JsonProperty(value = "databasePath", required = true) String dat /** * Loads the configuration from the given file. * - * @param path the configuration file, as JSON object + * @param content the configuration file, as Stringified JSON object * @return the loaded configuration * @throws IOException if the file could not be loaded */ - public static Config load(Path path) throws IOException { + public static Config load(String content) throws IOException { return new ObjectMapper().registerModule(new JavaTimeModule()) - .readValue(path.toFile(), Config.class); + .readValue(content, Config.class); } /** diff --git a/application/config.json.template b/application/src/main/resources/config.json similarity index 100% rename from application/config.json.template rename to application/src/main/resources/config.json From 9f0aeaf2b942fcc87ffba46fcd62a97586b1bacd Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Tue, 6 Jan 2026 23:10:16 +0000 Subject: [PATCH 06/15] chore: address sonar issues --- .../main/java/org/togetherjava/tjbot/Application.java | 1 + .../org/togetherjava/tjbot/features/Features.java | 1 + .../filesharing/FileSharingMessageListener.java | 3 ++- .../tjbot/features/github/GitHubReference.java | 1 + .../tjbot/features/jshell/JShellEval.java | 1 + .../togetherjava/tjbot/features/system/BotCore.java | 1 + .../java/org/togetherjava/tjbot/secrets/Secrets.java | 10 ++++++++++ .../org/togetherjava/tjbot/secrets/package-info.java | 11 +++++++++++ 8 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 application/src/main/java/org/togetherjava/tjbot/secrets/package-info.java diff --git a/application/src/main/java/org/togetherjava/tjbot/Application.java b/application/src/main/java/org/togetherjava/tjbot/Application.java index 1aa82c66b0..bd9781f36b 100644 --- a/application/src/main/java/org/togetherjava/tjbot/Application.java +++ b/application/src/main/java/org/togetherjava/tjbot/Application.java @@ -88,6 +88,7 @@ public static void main(final String[] args) { * Runs an instance of the bot, connecting to the given token and using the given database. * * @param config the configuration to run the bot with + * @param secrets the secrets to run the bot with */ @SuppressWarnings("WeakerAccess") public static void runBot(Config config, Secrets secrets) { diff --git a/application/src/main/java/org/togetherjava/tjbot/features/Features.java b/application/src/main/java/org/togetherjava/tjbot/features/Features.java index 756fc37aa7..0042b2e906 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/Features.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/Features.java @@ -105,6 +105,7 @@ private Features() { * @param jda the JDA instance commands will be registered at * @param database the database of the application, which features can use to persist data * @param config the configuration features should use + * @param secrets the secrets features may need * @return a collection of all features */ public static Collection createFeatures(JDA jda, Database database, Config config, diff --git a/application/src/main/java/org/togetherjava/tjbot/features/filesharing/FileSharingMessageListener.java b/application/src/main/java/org/togetherjava/tjbot/features/filesharing/FileSharingMessageListener.java index b59201013c..ec79e8e776 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/filesharing/FileSharingMessageListener.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/filesharing/FileSharingMessageListener.java @@ -56,7 +56,8 @@ public final class FileSharingMessageListener extends MessageReceiverAdapter /** * Creates a new instance. * - * @param config used to get api key and channel names. + * @param config used to get channel names. + * @param secrets used to get api key * @see org.togetherjava.tjbot.features.Features */ public FileSharingMessageListener(Config config, Secrets secrets) { diff --git a/application/src/main/java/org/togetherjava/tjbot/features/github/GitHubReference.java b/application/src/main/java/org/togetherjava/tjbot/features/github/GitHubReference.java index 7dee6495f1..03854b550b 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/github/GitHubReference.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/github/GitHubReference.java @@ -82,6 +82,7 @@ public final class GitHubReference extends MessageReceiverAdapter { * a predicate for matching allowed channels for feature and acquires repositories. * * @param config The Config to get allowed channel pattern for feature. + * @param secrets The Secrets to get the GitHub API key. */ public GitHubReference(Config config, Secrets secrets) { this.config = config; diff --git a/application/src/main/java/org/togetherjava/tjbot/features/jshell/JShellEval.java b/application/src/main/java/org/togetherjava/tjbot/features/jshell/JShellEval.java index 6acf2c2b89..b10ffe4a64 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/jshell/JShellEval.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/jshell/JShellEval.java @@ -36,6 +36,7 @@ public class JShellEval { * Creates a JShell evaluation instance * * @param config the JShell configuration to use + * @param baseUrl the base URL for the JSHell API * @param gistApiToken token of Gist api in case a JShell result is uploaded here */ public JShellEval(JShellConfig config, String baseUrl, String gistApiToken) { diff --git a/application/src/main/java/org/togetherjava/tjbot/features/system/BotCore.java b/application/src/main/java/org/togetherjava/tjbot/features/system/BotCore.java index 43d739f83d..e94bc231c2 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/system/BotCore.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/system/BotCore.java @@ -95,6 +95,7 @@ public final class BotCore extends ListenerAdapter implements CommandProvider { * @param jda the JDA instance that this command system will be used with * @param database the database that commands may use to persist data * @param config the configuration to use for this system + * @param secrets the secrets to use for this system */ public BotCore(JDA jda, Database database, Config config, Secrets secrets) { this.config = config; diff --git a/application/src/main/java/org/togetherjava/tjbot/secrets/Secrets.java b/application/src/main/java/org/togetherjava/tjbot/secrets/Secrets.java index fd0b19fb7e..b99cf91022 100644 --- a/application/src/main/java/org/togetherjava/tjbot/secrets/Secrets.java +++ b/application/src/main/java/org/togetherjava/tjbot/secrets/Secrets.java @@ -9,6 +9,9 @@ import java.nio.file.Path; import java.util.Objects; +/** + * This class contains secrets such as API keys, tokens etc., used by the application. + */ public class Secrets { private final String token; private final String githubApiKey; @@ -35,6 +38,13 @@ private Secrets(@JsonProperty(value = "token", required = true) String token, this.jshellBaseUrl = Objects.requireNonNull(jshellBaseUrl); } + /** + * Loads the configuration from the given file. + * + * @param path the location to secrets file + * @return the loaded configuration + * @throws IOException if the file could not be loaded + */ public static Secrets load(Path path) throws IOException { return new ObjectMapper().registerModule(new JavaTimeModule()) .readValue(path.toFile(), Secrets.class); diff --git a/application/src/main/java/org/togetherjava/tjbot/secrets/package-info.java b/application/src/main/java/org/togetherjava/tjbot/secrets/package-info.java new file mode 100644 index 0000000000..8da4481716 --- /dev/null +++ b/application/src/main/java/org/togetherjava/tjbot/secrets/package-info.java @@ -0,0 +1,11 @@ +/** + * This package contains the secrets of the application. It revolves around the class + * {@link org.togetherjava.tjbot.secrets.Secrets}. + */ +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package org.togetherjava.tjbot.secrets; + +import org.togetherjava.tjbot.annotations.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; From dab6919648fe12e39f974959d1a3d46ea0902c77 Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Tue, 6 Jan 2026 23:32:40 +0000 Subject: [PATCH 07/15] chore: inline `new String` and use `StandardCharsets.UTF_8` when reading --- .../src/main/java/org/togetherjava/tjbot/Application.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/Application.java b/application/src/main/java/org/togetherjava/tjbot/Application.java index bd9781f36b..54125a92b8 100644 --- a/application/src/main/java/org/togetherjava/tjbot/Application.java +++ b/application/src/main/java/org/togetherjava/tjbot/Application.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.sql.SQLException; @@ -59,8 +60,7 @@ public static void main(final String[] args) { throw new IOException("InputStream is null when loading " + configPath); } - String content = new String(stream.readAllBytes()); - config = Config.load(content); + config = Config.load(new String(stream.readAllBytes(), StandardCharsets.UTF_8)); } catch (IOException e) { logger.error("Unable to load the configuration file from path '{}'", configPath, e); From 2138edb93129526e2912212ba8e602c2988a606e Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Tue, 6 Jan 2026 23:35:38 +0000 Subject: [PATCH 08/15] docs: fix class doc --- .../src/main/java/org/togetherjava/tjbot/config/Config.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/config/Config.java b/application/src/main/java/org/togetherjava/tjbot/config/Config.java index a406292532..5610ac13a5 100644 --- a/application/src/main/java/org/togetherjava/tjbot/config/Config.java +++ b/application/src/main/java/org/togetherjava/tjbot/config/Config.java @@ -6,14 +6,13 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import java.io.IOException; -import java.nio.file.Path; import java.util.Collections; import java.util.List; import java.util.Objects; /** - * Configuration of the application. Create instances using {@link #load(Path)}. + * Configuration of the application. Create instances using {@link #load(String)}. */ public final class Config { private final String databasePath; From 41a3fdae98bb4a1478dfee16a92b30614446517c Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Wed, 7 Jan 2026 13:56:42 +0000 Subject: [PATCH 09/15] docs: update docs to include changes to config and addition of secrets --- .devcontainer/devcontainer.json | 3 --- wiki/Edit-the-Config.md | 17 +++++++++-------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 7f0783c0a6..bb91b66660 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -31,8 +31,5 @@ } } } - }, - "postCreateCommand": { - "config": "cp application/config.json.template application/config.json" } } diff --git a/wiki/Edit-the-Config.md b/wiki/Edit-the-Config.md index 013f29a1d2..5e1cafcc8b 100644 --- a/wiki/Edit-the-Config.md +++ b/wiki/Edit-the-Config.md @@ -1,13 +1,14 @@ # Overview -In order to edit the configuration file of the bot, one has to login to the VPS and adjust the config file manually. Only members of the [Moderator](https://github.com/orgs/Together-Java/teams/moderators)-Team can do the following steps. +## Config -See [[Access the VPS]] for details of the login process. +The configuration file for the bot is located under `/application/src/main/resources/config.json` -# Guide +Any new configuration additions made to this file must be loaded within the `Config.java` file. -1. `ssh togetherjava` to login to the VPS -2. Either `cd ~/docker-infra/master-bot` or `cd ~/docker-infra/develop-bot` to go to the directory of the corresponding bot -3. Use `cd config` -4. Edit the `config.json` file, for example `vim config.json` or `nano config.json` -4. Save the file and [[restart the bot|Shutdown or restart the bot]]. \ No newline at end of file +## Secrets + +The secrets configuration file for the bot is located under `/application/secrets.json` and is used to store sensitive +information such as API keys. + +Any new configuration additions made to this file must be loaded within the `Secrets.java` file. From 26ca4de372dab1649f13e58d6ec5445413b8c725 Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Wed, 7 Jan 2026 13:59:05 +0000 Subject: [PATCH 10/15] docs: update Setup-project-locally.md --- wiki/Setup-project-locally.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/wiki/Setup-project-locally.md b/wiki/Setup-project-locally.md index b62a6430df..daea13d029 100644 --- a/wiki/Setup-project-locally.md +++ b/wiki/Setup-project-locally.md @@ -98,14 +98,13 @@ See the following guide if you still have to create a server and a bot first: ![Discord Developer Portal - Bot Token](https://i.imgur.com/IB5W8vZ.png) -To run the bot, you will need a `config.json` file with specific content. You can find a template for this file, with meaningful default values, in `application/config.json.template`. +To run the bot, you will need a `secrets.json` file with specific content. You can find a template for this file, with meaningful default values, in `application/secrets.json.template`. Replace `` with your bot token; you can also adjust the other settings if you want. ### IntelliJ -1. put the configuration file to `TJ-Bot\application\config.json` or run the program with a single argument, the path to your config file -2. in the Gradle view, click the `run` task and start it +1. in the Gradle view, click the `run` task and start it ![Bot runs](https://i.imgur.com/KdsSsx0.png) From 016dd5499dabf42fe1f0c020b824605ed529ad40 Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Thu, 8 Jan 2026 11:54:42 +0000 Subject: [PATCH 11/15] chore: move Wolfram App ID into Secrets --- application/secrets.json.template | 3 ++- .../java/org/togetherjava/tjbot/config/Config.java | 13 ------------- .../org/togetherjava/tjbot/features/Features.java | 2 +- .../wolframalpha/WolframAlphaCommand.java | 8 ++++---- .../org/togetherjava/tjbot/secrets/Secrets.java | 14 +++++++++++++- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/application/secrets.json.template b/application/secrets.json.template index 49c1012549..1813d663a2 100644 --- a/application/secrets.json.template +++ b/application/secrets.json.template @@ -4,5 +4,6 @@ "logInfoChannelWebhook": "", "logErrorChannelWebhook": "", "openaiApiKey": "", - "jshellBaseUrl": "" + "jshellBaseUrl": "", + "wolframAlphaAppId": "79J52T-6239TVXHR7" } diff --git a/application/src/main/java/org/togetherjava/tjbot/config/Config.java b/application/src/main/java/org/togetherjava/tjbot/config/Config.java index 5610ac13a5..f43bdfb366 100644 --- a/application/src/main/java/org/togetherjava/tjbot/config/Config.java +++ b/application/src/main/java/org/togetherjava/tjbot/config/Config.java @@ -29,7 +29,6 @@ public final class Config { private final SuggestionsConfig suggestions; private final String quarantinedRolePattern; private final ScamBlockerConfig scamBlocker; - private final String wolframAlphaAppId; private final HelpSystemConfig helpSystem; private final List blacklistedFileExtension; private final String mediaOnlyChannelPattern; @@ -68,13 +67,11 @@ private Config(@JsonProperty(value = "databasePath", required = true) String dat @JsonProperty(value = "quarantinedRolePattern", required = true) String quarantinedRolePattern, @JsonProperty(value = "scamBlocker", required = true) ScamBlockerConfig scamBlocker, - @JsonProperty(value = "wolframAlphaAppId", required = true) String wolframAlphaAppId, @JsonProperty(value = "helpSystem", required = true) HelpSystemConfig helpSystem, @JsonProperty(value = "mediaOnlyChannelPattern", required = true) String mediaOnlyChannelPattern, @JsonProperty(value = "blacklistedFileExtension", required = true) List blacklistedFileExtension, - @JsonProperty(value = "githubReferencingEnabledChannelPattern", required = true) String githubReferencingEnabledChannelPattern, @JsonProperty(value = "githubRepositories", @@ -107,7 +104,6 @@ private Config(@JsonProperty(value = "databasePath", required = true) String dat this.suggestions = Objects.requireNonNull(suggestions); this.quarantinedRolePattern = Objects.requireNonNull(quarantinedRolePattern); this.scamBlocker = Objects.requireNonNull(scamBlocker); - this.wolframAlphaAppId = Objects.requireNonNull(wolframAlphaAppId); this.helpSystem = Objects.requireNonNull(helpSystem); this.mediaOnlyChannelPattern = Objects.requireNonNull(mediaOnlyChannelPattern); this.blacklistedFileExtension = Objects.requireNonNull(blacklistedFileExtension); @@ -268,15 +264,6 @@ public ScamBlockerConfig getScamBlocker() { return scamBlocker; } - /** - * Gets the application ID used to connect to the WolframAlpha API. - * - * @return the application ID for the WolframAlpha API - */ - public String getWolframAlphaAppId() { - return wolframAlphaAppId; - } - /** * Gets the config for the help system. * diff --git a/application/src/main/java/org/togetherjava/tjbot/features/Features.java b/application/src/main/java/org/togetherjava/tjbot/features/Features.java index 0042b2e906..b05aae263f 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/Features.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/Features.java @@ -200,7 +200,7 @@ public static Collection createFeatures(JDA jda, Database database, Con features.add(new QuarantineCommand(actionsStore, config)); features.add(new UnquarantineCommand(actionsStore, config)); features.add(new WhoIsCommand()); - features.add(new WolframAlphaCommand(config)); + features.add(new WolframAlphaCommand(secrets)); features.add(new GitHubCommand(githubReference)); features.add(new ModMailCommand(jda, config)); features.add(new HelpThreadCommand(config, helpSystemHelper)); diff --git a/application/src/main/java/org/togetherjava/tjbot/features/mathcommands/wolframalpha/WolframAlphaCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/mathcommands/wolframalpha/WolframAlphaCommand.java index 7f37ab6d0a..45b4e499c2 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/mathcommands/wolframalpha/WolframAlphaCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/mathcommands/wolframalpha/WolframAlphaCommand.java @@ -6,9 +6,9 @@ import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.utils.FileUpload; -import org.togetherjava.tjbot.config.Config; import org.togetherjava.tjbot.features.CommandVisibility; import org.togetherjava.tjbot.features.SlashCommandAdapter; +import org.togetherjava.tjbot.secrets.Secrets; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -37,14 +37,14 @@ public final class WolframAlphaCommand extends SlashCommandAdapter { /** * Creates a new instance. * - * @param config the config to use + * @param secrets containing the Wolfram app id */ - public WolframAlphaCommand(Config config) { + public WolframAlphaCommand(Secrets secrets) { super("wolfram-alpha", "Renders mathematical queries using WolframAlpha", CommandVisibility.GUILD); getData().addOption(OptionType.STRING, QUERY_OPTION, "the query to send to WolframAlpha", true); - appId = config.getWolframAlphaAppId(); + appId = secrets.getWolframAlphaAppId(); } @Override diff --git a/application/src/main/java/org/togetherjava/tjbot/secrets/Secrets.java b/application/src/main/java/org/togetherjava/tjbot/secrets/Secrets.java index b99cf91022..3fc6c52ca6 100644 --- a/application/src/main/java/org/togetherjava/tjbot/secrets/Secrets.java +++ b/application/src/main/java/org/togetherjava/tjbot/secrets/Secrets.java @@ -19,6 +19,7 @@ public class Secrets { private final String logErrorChannelWebhook; private final String openaiApiKey; private final String jshellBaseUrl; + private final String wolframAlphaAppId; @SuppressWarnings("ConstructorWithTooManyParameters") @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) @@ -29,13 +30,15 @@ private Secrets(@JsonProperty(value = "token", required = true) String token, @JsonProperty(value = "logErrorChannelWebhook", required = true) String logErrorChannelWebhook, @JsonProperty(value = "openaiApiKey", required = true) String openaiApiKey, - @JsonProperty(value = "jshellBaseUrl", required = true) String jshellBaseUrl) { + @JsonProperty(value = "jshellBaseUrl", required = true) String jshellBaseUrl, + @JsonProperty(value = "wolframAlphaAppId", required = true) String wolframAlphaAppId) { this.token = Objects.requireNonNull(token); this.githubApiKey = Objects.requireNonNull(githubApiKey); this.logInfoChannelWebhook = Objects.requireNonNull(logInfoChannelWebhook); this.logErrorChannelWebhook = Objects.requireNonNull(logErrorChannelWebhook); this.openaiApiKey = Objects.requireNonNull(openaiApiKey); this.jshellBaseUrl = Objects.requireNonNull(jshellBaseUrl); + this.wolframAlphaAppId = Objects.requireNonNull(wolframAlphaAppId); } /** @@ -89,6 +92,15 @@ public String getLogErrorChannelWebhook() { return logErrorChannelWebhook; } + /** + * Gets the application ID used to connect to the WolframAlpha API. + * + * @return the application ID for the WolframAlpha API + */ + public String getWolframAlphaAppId() { + return wolframAlphaAppId; + } + /** * The OpenAI token needed for communicating with OpenAI ChatGPT. * From 5c0eca4950cbc2215fc8abda68cae765d2899d65 Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Thu, 8 Jan 2026 11:56:10 +0000 Subject: [PATCH 12/15] chore: copy over config.json from vps --- application/src/main/resources/config.json | 118 +++++---------------- 1 file changed, 29 insertions(+), 89 deletions(-) diff --git a/application/src/main/resources/config.json b/application/src/main/resources/config.json index a2a14bed45..bd92f41bf5 100644 --- a/application/src/main/resources/config.json +++ b/application/src/main/resources/config.json @@ -1,111 +1,45 @@ { - "databasePath": "local-database.db", + "databasePath": "/home/bot/database/database.db", "projectWebsite": "https://github.com/Together-Java/TJ-Bot", "discordGuildInvite": "https://discord.com/invite/XXFUXzK", "modAuditLogChannelPattern": "mod-audit-log", - "modMailChannelPattern": "modmail", - "projectsChannelPattern": "projects", "mutedRolePattern": "Muted", "heavyModerationRolePattern": "Moderator", "softModerationRolePattern": "Moderator|Community Ambassador", "tagManageRolePattern": "Moderator|Community Ambassador|Top Helper.*", - "excludeCodeAutoDetectionRolePattern": "Top Helper.*|Moderator|Community Ambassador|Expert", + "excludeCodeAutoDetectionRolePattern": "Moderator|Community Ambassador|Expert|Top Helper.*", "suggestions": { - "channelPattern": "tj-suggestions", - "upVoteEmoteName": "peepo_yes", - "downVoteEmoteName": "peepo_no" + "channelPattern": "server-suggestions", + "upVoteEmoteName": "upvote", + "downVoteEmoteName": "downvote" }, "quarantinedRolePattern": "Quarantined", "scamBlocker": { - "mode": "AUTO_DELETE_BUT_APPROVE_QUARANTINE", - "reportChannelPattern": "commands", - "botTrapChannelPattern": "bot-trap", + "mode": "AUTO_DELETE_AND_QUARANTINE", + "reportChannelPattern": "community-commands", "trustedUserRolePattern": "Top Helper.*|Moderator|Community Ambassador|Expert", - "suspiciousKeywords": [ - "nitro", - "boob", - "sexy", - "sexi", - "esex", - "steam", - "gift", - "onlyfans", - "bitcoin", - "btc", - "promo", - "trader", - "trading", - "whatsapp", - "crypto", - "^claim", - "^teen$", - "adobe", - "^hack$", - "hacks", - "steamcommunity", - "freenitro", - "^earn$", - "^earning", - ".exe$", - "mrbeast" - ], - "hostWhitelist": [ - "discord.com", - "discord.media", - "discordapp.com", - "discordapp.net", - "discordstatus.com", - "thehackernews.com", - "gradle.org", - "help.gradle.org", - "youtube.com", - "www.youtube.com", - "cdn.discordapp.com", - "media.discordapp.net", - "store.steampowered.com", - "help.steampowered.com", - "learn.microsoft.com" - ], - "hostBlacklist": [ - "bit.ly", - "discord.gg", - "teletype.in", - "t.me", - "corematrix.us", - "u.to", - "steamcommunity.com", - "goo.su", - "telegra.ph", - "shorturl.at", - "cheatings.xyz", - "transfer.sh", - "tobimoller.space" - ], - "suspiciousHostKeywords": [ - "discord", - "nitro", - "premium", - "free", - "cheat", - "crypto", - "telegra", - "telety" - ], + "botTrapChannelPattern": "ignore-me", + "suspiciousKeywords": ["nitro", "boob", "sexy", "sexi", "esex", "jobcord", "steam", "gift", "onlyfans", "bitcoin", "btc", "promo", "trader", "trading", "whatsapp", "crypto", "^claim", "^teen$", "adobe", "^hack$", "hacks", "steamcommunity", "freenitro", "^earn$", "^earning", ".exe$", "mrbeast"], + "hostWhitelist": ["discord.com", "discord.media", "discordapp.com", "discordapp.net", "discordstatus.com", "cwiki.apache.org", "help.gradle.org", "thehackernews.com", "gradle.org", "youtube.com", "www.youtube.com", "cdn.discordapp.com", "media.discordapp.net", "store.steampowered.com", "help.steampowered.com", "learn.microsoft.com"], + "hostBlacklist": ["bit.ly", "gg.gg", "dsaocrdgift.xyz", "twitchcsgo.cfd", "link-hub.net", "discord.gg", "teletype.in", "t.me", "corematrix.us", "u.to", "steamcommunity.com", "goo.su", "telegra.ph", "shorturl.at", "cheatings.xyz", "transfer.sh", "tobimoller.space"], + "suspiciousHostKeywords": ["discord", "nitro", "premium", "deepfake", "free", "cheat", "crypto", "telegra", "telety"], "isHostSimilarToKeywordDistanceThreshold": 2, "suspiciousAttachmentsThreshold": 3, "suspiciousAttachmentNamePattern": "(image|\\d{1,2})\\.[^.]{0,5}" }, - "wolframAlphaAppId": "79J52T-6239TVXHR7", "helpSystem": { "helpForumPattern": "questions", "categories": [ "Java", "Frameworks", + "Spring", "JavaFX|Swing", "IDE", "Build Tools", "Database", "Android", + "Minecraft", + "Kotlin", "C|C++", "Algorithms", "Math", @@ -116,7 +50,7 @@ ], "categoryRoleSuffix": " - Helper" }, - "mediaOnlyChannelPattern": "memes", + "mediaOnlyChannelPattern": "memes|educational-media|resources|ide-themes-config", "blacklistedFileExtension": [ "application", "bat", @@ -145,6 +79,7 @@ "ps2xml", "psc1", "psc2", + "rar", "scf", "scr", "vb", @@ -157,6 +92,8 @@ ], "githubReferencingEnabledChannelPattern": "server-suggestions|tjbot-discussion|modernjava-discussion", "githubRepositories": [403389278,587644974,601602394], + "modMailChannelPattern": "modmail", + "projectsChannelPattern": "projects", "sourceCodeBaseUrl": "https://github.com/Together-Java/TJ-Bot/blob/master/application/src/main/java/", "jshell": { "rateLimitWindowSeconds": 10, @@ -170,25 +107,28 @@ "recentlyJoinedDays": 4 }, "featureBlacklist": { - "normal": [ - ], - "special": [ - ] + "normal": [], + "special": [] }, + "memberCountCategoryPattern": "Info", "selectRolesChannelPattern": "select-your-roles", "rssConfig": { "feeds": [ { - "url": "https://blogs.oracle.com/java/rss", + "url":"https://inside.java/feed.xml", "targetChannelPattern": "java-news-and-changes", - "dateFormatterPattern": "EEE, d MMM yyyy HH:mm:ss z" + "dateFormatterPattern": "yyyy-MM-dd'T'HH:mm:ssXXX" + }, + { + "url":"https://www.youtube.com/feeds/videos.xml?playlist_id=UUSHmRtPmgnQ04CMUpSUqPfhxQ", + "targetChannelPattern": "today-i-teach", + "dateFormatterPattern": "yyyy-MM-dd'T'HH:mm:ssXXX" } ], "fallbackChannelPattern": "java-news-and-changes", "videoLinkPattern": "http(s)?://www\\.youtube.com.*", "pollIntervalInMinutes": 10 }, - "memberCountCategoryPattern": "Info", "topHelpers": { "rolePattern": "Top Helper.*", "assignmentChannelPattern": "community-commands", From a73c9081011736a8b28007ec9d0fe775a6fcfa7f Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Thu, 8 Jan 2026 18:16:22 +0000 Subject: [PATCH 13/15] make this backward compatible --- .../org/togetherjava/tjbot/Application.java | 79 +++++++++++++++---- .../org/togetherjava/tjbot/config/Config.java | 15 +++- 2 files changed, 79 insertions(+), 15 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/Application.java b/application/src/main/java/org/togetherjava/tjbot/Application.java index 54125a92b8..32a08ee549 100644 --- a/application/src/main/java/org/togetherjava/tjbot/Application.java +++ b/application/src/main/java/org/togetherjava/tjbot/Application.java @@ -36,34 +36,44 @@ private Application() { } private static final Logger logger = LoggerFactory.getLogger(Application.class); - private static final String DEFAULT_CONFIG_PATH = "/config.json"; + private static final String DEFAULT_CONFIG_PATH = "config.json"; private static final String DEFAULT_SECRETS_PATH = "secrets.json"; /** * Starts the application. + *

+ * Note: By default the configuration file will be loaded from a config.json unless overridden + * by either: 1. Setting the USE_INCLUDED_CONFIG environment variable to true, which will use + * the config.json packed in the built jar. 2. Passing a program argument including the path to + * the config file. * * @param args command line arguments - [the path to the configuration file (optional, by * default "config.json")] */ public static void main(final String[] args) { - if (args.length > 1) { - throw new IllegalArgumentException("Expected no or one argument but " + args.length - + " arguments were provided. The first argument is the path to the configuration file. If no argument was provided, '" - + DEFAULT_CONFIG_PATH + "' will be assumed."); + boolean useIncludedConfig; + try { + useIncludedConfig = Boolean.parseBoolean(System.getenv("USE_INCLUDED_CONFIG")); + logger.info("Using config.json included in jar"); + } catch (Exception _) { + useIncludedConfig = false; } - String configPath = args.length == 1 ? args[0] : DEFAULT_CONFIG_PATH; - Config config; - - try (InputStream stream = Application.class.getResourceAsStream(configPath)) { - if (stream == null) { - throw new IOException("InputStream is null when loading " + configPath); - } + String configPath; - config = Config.load(new String(stream.readAllBytes(), StandardCharsets.UTF_8)); + if (args.length > 0) { + configPath = args[0]; + } else if (useIncludedConfig) { + configPath = "/" + DEFAULT_CONFIG_PATH; + } else { + configPath = DEFAULT_CONFIG_PATH; + } + Config config; + try { + config = loadConfig(useIncludedConfig, configPath); } catch (IOException e) { - logger.error("Unable to load the configuration file from path '{}'", configPath, e); + logger.error("Unable to load the configuration file '{}'", configPath, e); return; } @@ -84,6 +94,47 @@ public static void main(final String[] args) { runBot(config, secrets); } + /** + * Attempts to load the configuration file and return a new {@code Config}. + * + * @param useIncludedConfig if the config should be loaded from the resources' directory. + * @param configPath the location of the config file. + * @return a new {@code Config} object + * @throws IOException if the configuration file could not be loaded. + */ + private static Config loadConfig(boolean useIncludedConfig, String configPath) + throws IOException { + return useIncludedConfig ? loadConfigFromResource(configPath) + : loadConfigFromFile(Path.of(configPath)); + } + + /** + * Loads a configuration file from the application resources directory. + * + * @param configPath the location of the configuration file + * @return a new {@code Config} object + * @throws IOException if the configuration file could not be loaded + */ + private static Config loadConfigFromResource(String configPath) throws IOException { + try (InputStream stream = Application.class.getResourceAsStream(configPath)) { + if (stream == null) { + throw new IOException("InputStream is null when loading " + configPath); + } + return Config.load(new String(stream.readAllBytes(), StandardCharsets.UTF_8)); + } + } + + /** + * Loads a configuration file from a specified path. + * + * @param configPath the location of the configuration file + * @return a new {@code Config} object + * @throws IOException if the configuration file could not be loaded + */ + private static Config loadConfigFromFile(Path configPath) throws IOException { + return Config.load(configPath); + } + /** * Runs an instance of the bot, connecting to the given token and using the given database. * diff --git a/application/src/main/java/org/togetherjava/tjbot/config/Config.java b/application/src/main/java/org/togetherjava/tjbot/config/Config.java index f43bdfb366..88f84ddadd 100644 --- a/application/src/main/java/org/togetherjava/tjbot/config/Config.java +++ b/application/src/main/java/org/togetherjava/tjbot/config/Config.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import java.io.IOException; +import java.nio.file.Path; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -120,7 +121,7 @@ private Config(@JsonProperty(value = "databasePath", required = true) String dat } /** - * Loads the configuration from the given file. + * Loads the configuration from the String payload. * * @param content the configuration file, as Stringified JSON object * @return the loaded configuration @@ -131,6 +132,18 @@ public static Config load(String content) throws IOException { .readValue(content, Config.class); } + /** + * Loads the configuration from the given file. + * + * @param path the configuration file + * @return the loaded configuration + * @throws IOException if the file could not be loaded + */ + public static Config load(Path path) throws IOException { + return new ObjectMapper().registerModule(new JavaTimeModule()) + .readValue(path.toFile(), Config.class); + } + /** * Gets the REGEX pattern used to identify the role assigned to muted users. * From 55ad8dc8916ad292fbed409de38cd47240dbc8a4 Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Thu, 8 Jan 2026 18:17:29 +0000 Subject: [PATCH 14/15] add local config.json --- application/config.json | 137 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 application/config.json diff --git a/application/config.json b/application/config.json new file mode 100644 index 0000000000..634b8acd3b --- /dev/null +++ b/application/config.json @@ -0,0 +1,137 @@ +{ + "databasePath": "local-database.db", + "projectWebsite": "https://github.com/Together-Java/TJ-Bot", + "discordGuildInvite": "https://discord.com/invite/XXFUXzK", + "modAuditLogChannelPattern": "mod-audit-log", + "mutedRolePattern": "Muted", + "heavyModerationRolePattern": "Moderator", + "softModerationRolePattern": "Moderator|Community Ambassador", + "tagManageRolePattern": "Moderator|Community Ambassador|Top Helper.*", + "excludeCodeAutoDetectionRolePattern": "Moderator|Community Ambassador|Expert|Top Helper.*", + "suggestions": { + "channelPattern": "server-suggestions", + "upVoteEmoteName": "upvote", + "downVoteEmoteName": "downvote" + }, + "quarantinedRolePattern": "Quarantined", + "scamBlocker": { + "mode": "AUTO_DELETE_BUT_APPROVE_QUARANTINE", + "reportChannelPattern": "community-commands", + "trustedUserRolePattern": "Top Helper.*|Moderator|Community Ambassador|Expert", + "botTrapChannelPattern": "ignore-me", + "suspiciousKeywords": ["nitro", "boob", "sexy", "sexi", "esex", "jobcord", "steam", "gift", "onlyfans", "bitcoin", "btc", "promo", "trader", "trading", "whatsapp", "crypto", "^claim", "^teen$", "adobe", "^hack$", "hacks", "steamcommunity", "freenitro", "^earn$", "^earning", ".exe$", "mrbeast"], + "hostWhitelist": ["discord.com", "discord.media", "discordapp.com", "discordapp.net", "discordstatus.com", "cwiki.apache.org", "help.gradle.org", "thehackernews.com", "gradle.org", "youtube.com", "www.youtube.com", "cdn.discordapp.com", "media.discordapp.net", "store.steampowered.com", "help.steampowered.com", "learn.microsoft.com"], + "hostBlacklist": ["bit.ly", "gg.gg", "dsaocrdgift.xyz", "twitchcsgo.cfd", "link-hub.net", "discord.gg", "teletype.in", "t.me", "corematrix.us", "u.to", "steamcommunity.com", "goo.su", "telegra.ph", "shorturl.at", "cheatings.xyz", "transfer.sh", "tobimoller.space"], + "suspiciousHostKeywords": ["discord", "nitro", "premium", "deepfake", "free", "cheat", "crypto", "telegra", "telety"], + "isHostSimilarToKeywordDistanceThreshold": 2, + "suspiciousAttachmentsThreshold": 3, + "suspiciousAttachmentNamePattern": "(image|\\d{1,2})\\.[^.]{0,5}" + }, + "helpSystem": { + "helpForumPattern": "questions", + "categories": [ + "Java", + "Frameworks", + "Spring", + "JavaFX|Swing", + "IDE", + "Build Tools", + "Database", + "Android", + "Minecraft", + "Kotlin", + "C|C++", + "Algorithms", + "Math", + "Architecture", + "Code Review", + "Together Java Bot", + "Other" + ], + "categoryRoleSuffix": " - Helper" + }, + "mediaOnlyChannelPattern": "memes|educational-media|resources|ide-themes-config", + "blacklistedFileExtension": [ + "application", + "bat", + "cmd", + "com", + "cpl", + "exe", + "gadget", + "hta", + "inf", + "jse", + "lnk", + "msc", + "msh", + "msh1", + "msh1xml", + "msh2", + "msh2xml", + "mshxml", + "msi", + "msp", + "pif", + "ps1", + "ps1xml", + "ps2", + "ps2xml", + "psc1", + "psc2", + "rar", + "scf", + "scr", + "vb", + "vbe", + "vbs", + "ws", + "wsc", + "wsf", + "wsh" + ], + "githubReferencingEnabledChannelPattern": "server-suggestions|tjbot-discussion|modernjava-discussion", + "githubRepositories": [403389278,587644974,601602394], + "modMailChannelPattern": "modmail", + "projectsChannelPattern": "projects", + "sourceCodeBaseUrl": "https://github.com/Together-Java/TJ-Bot/blob/master/application/src/main/java/", + "jshell": { + "rateLimitWindowSeconds": 10, + "rateLimitRequestsInWindow": 3 + }, + "helperPruneConfig": { + "roleFullLimit": 250, + "roleFullThreshold": 245, + "pruneMemberAmount": 7, + "inactivateAfterDays": 90, + "recentlyJoinedDays": 4 + }, + "featureBlacklist": { + "normal": [], + "special": [] + }, + "memberCountCategoryPattern": "Info", + "selectRolesChannelPattern": "select-your-roles", + "rssConfig": { + "feeds": [ + { + "url":"https://inside.java/feed.xml", + "targetChannelPattern": "java-news-and-changes", + "dateFormatterPattern": "yyyy-MM-dd'T'HH:mm:ssXXX" + }, + { + "url":"https://www.youtube.com/feeds/videos.xml?playlist_id=UUSHmRtPmgnQ04CMUpSUqPfhxQ", + "targetChannelPattern": "today-i-teach", + "dateFormatterPattern": "yyyy-MM-dd'T'HH:mm:ssXXX" + } + ], + "fallbackChannelPattern": "java-news-and-changes", + "videoLinkPattern": "http(s)?://www\\.youtube.com.*", + "pollIntervalInMinutes": 10 + }, + "topHelpers": { + "rolePattern": "Top Helper.*", + "assignmentChannelPattern": "community-commands", + "announcementChannelPattern": "hall-of-fame" + } +} From 17e90243ae4ec82c81b87f4773ffe32a483ca36a Mon Sep 17 00:00:00 2001 From: Suraj Kumar Date: Thu, 8 Jan 2026 18:20:30 +0000 Subject: [PATCH 15/15] sonar --- .../src/main/java/org/togetherjava/tjbot/Application.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/Application.java b/application/src/main/java/org/togetherjava/tjbot/Application.java index 32a08ee549..6a38071491 100644 --- a/application/src/main/java/org/togetherjava/tjbot/Application.java +++ b/application/src/main/java/org/togetherjava/tjbot/Application.java @@ -36,7 +36,8 @@ private Application() { } private static final Logger logger = LoggerFactory.getLogger(Application.class); - private static final String DEFAULT_CONFIG_PATH = "config.json"; + private static final String DEFAULT_CONFIG_PATH_DISK = "config.json"; + private static final String DEFAULT_CONFIG_PATH_RESOURCES = "/config.json"; private static final String DEFAULT_SECRETS_PATH = "secrets.json"; /** @@ -64,9 +65,9 @@ public static void main(final String[] args) { if (args.length > 0) { configPath = args[0]; } else if (useIncludedConfig) { - configPath = "/" + DEFAULT_CONFIG_PATH; + configPath = DEFAULT_CONFIG_PATH_RESOURCES; } else { - configPath = DEFAULT_CONFIG_PATH; + configPath = DEFAULT_CONFIG_PATH_DISK; } Config config;