Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite http module and start migrating to Exposed ORM #28

Merged
merged 21 commits into from
Apr 10, 2024
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-java@v1
with:
java-version: 16
java-version: 21
- uses: gradle/gradle-build-action@v2
with:
arguments: build
Expand Down
56 changes: 39 additions & 17 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import org.apache.tools.ant.filters.ReplaceTokens
import java.time.Instant

plugins {
id 'org.jetbrains.kotlin.jvm' version '1.6.20'
id 'org.jetbrains.kotlin.jvm' version '1.9.22'
id "org.jetbrains.kotlin.plugin.serialization" version "1.9.22"
id 'java'
id 'application'
id 'com.github.johnrengelman.shadow' version '7.0.0'
Expand All @@ -13,15 +14,15 @@ plugins {
mainClassName = 'at.xirado.bean.Bean'

group 'at.xirado'
version '5.0.0'
version '5.1.0'

shadowJar {
archiveFileName = "Bean.jar"
}

compileJava {
sourceCompatibility = JavaVersion.VERSION_16
targetCompatibility = JavaVersion.VERSION_16
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
options.encoding = 'UTF-8'
}

Expand All @@ -34,16 +35,16 @@ repositories {
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib"
implementation 'net.dv8tion:JDA:5.0.0-beta.13'
implementation 'net.dv8tion:JDA:5.0.0-beta.21'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0'
implementation 'com.sparkjava:spark-core:2.9.3'
implementation 'org.codehaus.groovy:groovy-jsr223:3.0.8'
implementation 'org.apache.commons:commons-lang3:3.12.0'
implementation 'org.mariadb.jdbc:mariadb-java-client:2.7.3'
implementation 'ch.qos.logback:logback-classic:1.3.0-alpha5'
implementation 'com.zaxxer:HikariCP:5.0.0'
implementation 'org.fusesource.jansi:jansi:2.4.0'

implementation 'org.slf4j:slf4j-api:2.0.12'
implementation 'ch.qos.logback:logback-classic:1.5.3'

implementation 'org.mariadb.jdbc:mariadb-java-client:3.3.3'
implementation 'com.zaxxer:HikariCP:5.1.0'

implementation 'com.google.guava:guava:31.0.1-jre'
implementation 'com.github.chew:jda-chewtils:5e1a9f93f9'
implementation 'org.scilab.forge:jlatexmath:1.0.7'
Expand All @@ -52,16 +53,38 @@ dependencies {
implementation 'com.github.minndevelopment:jda-ktx:17eb77a'
implementation 'net.jodah:expiringmap:0.5.10'
implementation 'org.apache.httpcomponents:httpclient:4.5.13'
implementation 'org.jline:jline-style:3.21.0'

// Prometheus
implementation "io.prometheus:simpleclient:0.14.1"
implementation "io.prometheus:simpleclient_hotspot:0.14.1"
implementation "io.prometheus:simpleclient_httpserver:0.14.1"
implementation 'com.facebook:ktfmt:0.36'

runtimeOnly 'org.jetbrains.kotlin:kotlin-scripting-jsr223:1.6.21'
implementation 'dev.reformator.stacktracedecoroutinator:stacktrace-decoroutinator-jvm:2.3.8'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3'
implementation 'com.akuleshov7:ktoml-core:0.5.1'

def ktorVersion = "2.3.9"
def exposedVersion = "0.48.0"

implementation "io.ktor:ktor-server-core:${ktorVersion}"
implementation "io.ktor:ktor-server-netty:${ktorVersion}"
implementation "io.ktor:ktor-server-content-negotiation:${ktorVersion}"
implementation "io.ktor:ktor-serialization-kotlinx-json:${ktorVersion}"
implementation "io.ktor:ktor-server-auth:${ktorVersion}"
implementation "io.ktor:ktor-server-auth-jwt:${ktorVersion}"
implementation "io.ktor:ktor-server-cors:${ktorVersion}"
implementation "io.ktor:ktor-server-status-pages:${ktorVersion}"

implementation "org.jetbrains.exposed:exposed-core:${exposedVersion}"
implementation "org.jetbrains.exposed:exposed-crypt:${exposedVersion}"
implementation "org.jetbrains.exposed:exposed-dao:${exposedVersion}"
implementation "org.jetbrains.exposed:exposed-jdbc:${exposedVersion}"
implementation "org.jetbrains.exposed:exposed-json:${exposedVersion}"

implementation "com.sksamuel.aedile:aedile-core:1.3.1"

runtimeOnly 'org.jetbrains.kotlin:kotlin-scripting-jsr223:1.9.22'
}

/**
Expand All @@ -86,7 +109,6 @@ processResources {
}

compileKotlin {
kotlinOptions {
jvmTarget = "16"
}
kotlinOptions.jvmTarget = "21"
kotlinOptions.freeCompilerArgs = List.of("-Xcontext-receivers")
}
130 changes: 66 additions & 64 deletions src/main/java/at/xirado/bean/Bean.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package at.xirado.bean;

import at.xirado.bean.backend.Authenticator;
import at.xirado.bean.backend.WebServer;
import at.xirado.bean.command.handler.CommandHandler;
import at.xirado.bean.command.handler.InteractionHandler;
import at.xirado.bean.data.OkHttpInterceptor;
import at.xirado.bean.data.content.DismissableContentManager;
import at.xirado.bean.data.database.Database;
import at.xirado.bean.data.repository.Repository;
import at.xirado.bean.event.*;
import at.xirado.bean.http.HttpServer;
import at.xirado.bean.http.oauth.DiscordAPI;
import at.xirado.bean.log.WebhookAppenderKt;
import at.xirado.bean.mee6.MEE6Queue;
import at.xirado.bean.misc.Util;
import at.xirado.bean.prometheus.MetricsJob;
import at.xirado.bean.prometheus.Prometheus;
import club.minnced.discord.webhook.WebhookClient;
import club.minnced.discord.webhook.WebhookClientBuilder;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
import dev.reformator.stacktracedecoroutinator.runtime.DecoroutinatorRuntime;
import net.dv8tion.jda.api.GatewayEncoding;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Activity;
Expand All @@ -26,17 +27,12 @@
import net.dv8tion.jda.api.utils.ChunkingFilter;
import net.dv8tion.jda.api.utils.MemberCachePolicy;
import net.dv8tion.jda.api.utils.cache.CacheFlag;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.internal.utils.IOUtil;
import okhttp3.OkHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.security.auth.login.LoginException;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.EnumSet;
import java.util.Properties;
import java.util.Set;
Expand All @@ -52,6 +48,7 @@ public class Bean {
public static final long START_TIME = System.currentTimeMillis() / 1000;

private static final Logger LOGGER = LoggerFactory.getLogger(Bean.class);
private static final String WEBHOOK_APPENDER_LAYOUT = "%boldGreen(%-15.-15logger{0}) %highlight(%-4level) %msg%n";
private static String VERSION;
private static long BUILD_TIME;
private static Bean instance;
Expand All @@ -74,41 +71,58 @@ public class Bean {
private final ExecutorService executor =
Executors.newSingleThreadExecutor(new ThreadFactoryBuilder()
.setNameFormat("Bean Executor Thread")
.setUncaughtExceptionHandler((t, e) -> LOGGER.error("An uncaught error occurred on the executor!", e))
.setUncaughtExceptionHandler((t, e) -> LOGGER.error("Uncaught exception in executor", e))
.build());

private final ExecutorService virtualThreadExecutor =
Executors.newThreadPerTaskExecutor(new ThreadFactoryBuilder()
.setThreadFactory(Thread.ofVirtual().factory())
.setUncaughtExceptionHandler((t, e) -> LOGGER.error("Uncaught exception in executor", e))
.build());

private final InteractionHandler interactionHandler;
private final CommandHandler commandHandler;
private final EventWaiter eventWaiter;
private final OkHttpClient okHttpClient;
private final WebServer webServer;
private final Authenticator authenticator;

private final HttpServer httpServer;

private final MEE6Queue mee6Queue;
private final DismissableContentManager dismissableContentManager;
private final Database database;
private final Repository repository;

private WebhookClient webhookClient = null;
private DataObject config = loadConfig();
private final Config config = ConfigKt.readConfig(true);

private final DiscordAPI discordApi;

public Bean() throws Exception {
instance = this;
Message.suppressContentIntentWarning();
Database.connect();
Database.awaitReady();
debug = !config.isNull("debug") && config.getBoolean("debug");

String webhookUrl = config.getWebhookUrl();

if (webhookUrl != null) {
WebhookAppenderKt.initWebhookLogger("info", webhookUrl, WEBHOOK_APPENDER_LAYOUT, 5000);
}

database = new Database(config.getDb());
repository = new Repository(database);

debug = config.getDebugMode();
interactionHandler = new InteractionHandler(this);
commandHandler = new CommandHandler();
eventWaiter = new EventWaiter();
Class.forName("at.xirado.bean.translation.LocaleLoader");

okHttpClient = new OkHttpClient.Builder()
.build();
if (!config.isNull("webhook_url"))
webhookClient = new WebhookClientBuilder(config.getString("webhook_url"))
.build();
if (config.isNull("token"))
throw new IllegalStateException("Can not start without a token!");
shardManager = DefaultShardManagerBuilder.create(config.getString("token"), getIntents())

setupShutdownHook();
shardManager = DefaultShardManagerBuilder.create(config.getDiscordToken(), getIntents())
.setShardsTotal(-1)
.setMemberCachePolicy(MemberCachePolicy.VOICE)
.setMemberCachePolicy(MemberCachePolicy.lru(500))
.setActivity(Activity.playing("bean.bz"))
.enableCache(CacheFlag.VOICE_STATE)
.setBulkDeleteSplittingEnabled(false)
Expand All @@ -122,11 +136,12 @@ public Bean() throws Exception {
eventWaiter, new GuildMemberJoinListener(), new DismissableContentButtonListener(), new GuildJoinListener(),
EvalListener.INSTANCE)
.build();
authenticator = new Authenticator();
String host = config.isNull("ip") ? "127.0.0.1" : config.getString("ip");
int port = config.isNull("port") ? 8887 : config.getInt("port");

webServer = new WebServer(host, port);
OAuthConfig oauthConfig = config.getOauth();

discordApi = new DiscordAPI(oauthConfig);
httpServer = new HttpServer(config);

dismissableContentManager = new DismissableContentManager();
new Prometheus();
new MetricsJob().start();
Expand All @@ -150,9 +165,9 @@ public static EnumSet<GatewayIntent> getIntents() {

public static void main(String[] args) {
Thread.currentThread().setName("Main");
DecoroutinatorRuntime.INSTANCE.load();
try {
loadPropertiesFile();
setupShutdownHook();
new Bean();
} catch (Exception e) {
if (e instanceof LoginException ex) {
Expand All @@ -169,7 +184,7 @@ private static void loadPropertiesFile() {
properties.load(Bean.class.getClassLoader().getResourceAsStream("app.properties"));
VERSION = properties.getProperty("app-version");
BUILD_TIME = Long.parseLong(properties.getProperty("build-time"));
} catch(Throwable e) {
} catch (Throwable e) {
LOGGER.error("Could not read app.properties file!");
VERSION = "0.0.0";
BUILD_TIME = 0L;
Expand Down Expand Up @@ -208,14 +223,10 @@ public static boolean isWhitelistedUser(long userId) {
return WHITELISTED_USERS.contains(userId);
}

public synchronized DataObject getConfig() {
public synchronized Config getConfig() {
return config;
}

public synchronized void updateConfig() {
this.config = loadConfig();
}

public ExecutorService getCommandExecutor() {
return commandExecutor;
}
Expand All @@ -228,6 +239,10 @@ public ExecutorService getExecutor() {
return executor;
}

public ExecutorService getVirtualThreadExecutor() {
return virtualThreadExecutor;
}

public boolean isDebug() {
return debug;
}
Expand All @@ -252,53 +267,40 @@ public OkHttpClient getOkHttpClient() {
return okHttpClient;
}

public Authenticator getAuthenticator() {
return authenticator;
}

public DismissableContentManager getDismissableContentManager() {
return dismissableContentManager;
}

private DataObject loadConfig() {
File configFile = new File("config.json");
if (!configFile.exists()) {
InputStream inputStream = Bean.class.getResourceAsStream("/config.json");
if (inputStream == null) {
LOGGER.error("Could not copy config from resources folder!");
return DataObject.empty();
}
Path path = Paths.get(Util.getJarPath() + "/config.json");
try {
Files.copy(inputStream, path);
} catch (IOException e) {
LOGGER.error("Could not copy config file!", e);
}
}
try {
return DataObject.fromJson(new FileInputStream(configFile));
} catch (FileNotFoundException ignored) {
}
return DataObject.empty();
public DiscordAPI getDiscordApi() {
return discordApi;
}

public WebServer getWebServer() {
return webServer;
public HttpServer getHttpServer() {
return httpServer;
}

public MEE6Queue getMEE6Queue() {
return mee6Queue;
}

private static void setupShutdownHook() {
public Database getDatabase() {
return database;
}

public Repository getRepository() {
return repository;
}

private void setupShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
LOGGER.info("Awaiting JDA ShardManager shutdown...");
getInstance().getShardManager().shutdown();
for (JDA jda : getInstance().getShardManager().getShards()) {
getShardManager().shutdown();
for (JDA jda : getShardManager().getShards()) {
while (jda.getStatus() != JDA.Status.SHUTDOWN) {
try {
Thread.sleep(20);
} catch (InterruptedException ignored) {}
} catch (InterruptedException ignored) {
}
}
}
LOGGER.info("Stopped all shards");
Expand Down
Loading
Loading