Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,5 @@
}
}
}
},
"postCreateCommand": {
"config": "cp application/config.json.template application/config.json"
}
}
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,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
Expand Down
120 changes: 27 additions & 93 deletions application/config.json.template → application/config.json
Original file line number Diff line number Diff line change
@@ -1,113 +1,45 @@
{
"token": "<put_your_token_here>",
"githubApiKey": "<your_github_personal_access_token>",
"databasePath": "local-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",
"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",
Expand All @@ -118,7 +50,7 @@
],
"categoryRoleSuffix": " - Helper"
},
"mediaOnlyChannelPattern": "memes",
"mediaOnlyChannelPattern": "memes|educational-media|resources|ide-themes-config",
"blacklistedFileExtension": [
"application",
"bat",
Expand Down Expand Up @@ -147,6 +79,7 @@
"ps2xml",
"psc1",
"psc2",
"rar",
"scf",
"scr",
"vb",
Expand All @@ -159,12 +92,10 @@
],
"githubReferencingEnabledChannelPattern": "server-suggestions|tjbot-discussion|modernjava-discussion",
"githubRepositories": [403389278,587644974,601602394],
"logInfoChannelWebhook": "<put_your_webhook_here>",
"logErrorChannelWebhook": "<put_your_webhook_here>",
"openaiApiKey": "<check pins in #tjbot_discussion for the key>",
"modMailChannelPattern": "modmail",
"projectsChannelPattern": "projects",
"sourceCodeBaseUrl": "https://github.com/Together-Java/TJ-Bot/blob/master/application/src/main/java/",
"jshell": {
"baseUrl": "<put_jshell_rest_api_url_here>",
"rateLimitWindowSeconds": 10,
"rateLimitRequestsInWindow": 3
},
Expand All @@ -176,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",
Expand Down
9 changes: 9 additions & 0 deletions application/secrets.json.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"token": "<put_your_token_here>",
"githubApiKey": "<your_github_personal_access_token>",
"logInfoChannelWebhook": "<put_your_webhook_here>",
"logErrorChannelWebhook": "<put_your_webhook_here>",
"openaiApiKey": "<check pins in #tjbot_discussion for the key>",
"jshellBaseUrl": "<put_jshell_rest_api_url_here>",
"wolframAlphaAppId": "79J52T-6239TVXHR7"
}
98 changes: 85 additions & 13 deletions application/src/main/java/org/togetherjava/tjbot/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
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.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.SQLException;
Expand All @@ -33,45 +36,114 @@ 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";

/**
* Starts the application.
* <p>
* 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;

if (args.length > 0) {
configPath = args[0];
} else if (useIncludedConfig) {
configPath = DEFAULT_CONFIG_PATH_RESOURCES;
} else {
configPath = DEFAULT_CONFIG_PATH_DISK;
}

Path configPath = Path.of(args.length == 1 ? args[0] : DEFAULT_CONFIG_PATH);
Config config;
try {
config = Config.load(configPath);
config = loadConfig(useIncludedConfig, configPath);
} catch (IOException e) {
logger.error("Unable to load the configuration file '{}'", configPath, e);
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);
secretsPath.toAbsolutePath(), e);
return;
}

Thread.setDefaultUncaughtExceptionHandler(Application::onUncaughtException);
Runtime.getRuntime().addShutdownHook(new Thread(Application::onShutdown));
DiscordLogging.startDiscordLogging(config);
DiscordLogging.startDiscordLogging(config, secrets);

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));
}

runBot(config);
/**
* 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.
*
* @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) {
public static void runBot(Config config, Secrets secrets) {
logger.info("Starting bot...");

Path databasePath = Path.of(config.getDatabasePath());
Expand All @@ -82,13 +154,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);

Expand Down
Loading
Loading