diff --git a/foxy/build.gradle.kts b/foxy/build.gradle.kts index c5897b5d..0a4e661a 100644 --- a/foxy/build.gradle.kts +++ b/foxy/build.gradle.kts @@ -38,6 +38,8 @@ dependencies { implementation("io.ktor:ktor-client-cio:${Versions.KTOR}") implementation("io.ktor:ktor-server-content-negotiation:${Versions.KTOR}") + implementation("com.google.guava:guava:32.1.3-jre") + // Caching implementation("com.github.ben-manes.caffeine:caffeine:${Versions.CAFFEINE}") diff --git a/foxy/src/main/kotlin/net/cakeyfox/foxy/FoxyInstance.kt b/foxy/src/main/kotlin/net/cakeyfox/foxy/FoxyInstance.kt index 7992dc14..89476175 100644 --- a/foxy/src/main/kotlin/net/cakeyfox/foxy/FoxyInstance.kt +++ b/foxy/src/main/kotlin/net/cakeyfox/foxy/FoxyInstance.kt @@ -5,20 +5,21 @@ import io.ktor.client.engine.cio.* import io.ktor.client.plugins.* import io.ktor.client.plugins.contentnegotiation.* import io.ktor.serialization.kotlinx.json.* -import kotlinx.coroutines.Job import mu.KotlinLogging import net.cakeyfox.artistry.ArtistryClient import net.cakeyfox.common.Constants import net.cakeyfox.foxy.command.FoxyCommandManager import net.cakeyfox.foxy.command.component.FoxyComponentManager -import net.cakeyfox.foxy.listeners.GuildEventListener -import net.cakeyfox.foxy.listeners.InteractionEventListener -import net.cakeyfox.foxy.listeners.MajorEventListener +import net.cakeyfox.foxy.listeners.GuildListener +import net.cakeyfox.foxy.listeners.InteractionsListener +import net.cakeyfox.foxy.listeners.MessageListener import net.cakeyfox.foxy.utils.ActivityUpdater import net.cakeyfox.foxy.utils.config.FoxyConfig import net.cakeyfox.foxy.utils.FoxyUtils import net.cakeyfox.foxy.utils.analytics.TopggStatsSender import net.cakeyfox.foxy.utils.database.MongoDBClient +import net.cakeyfox.foxy.utils.threads.ThreadPoolManager +import net.cakeyfox.foxy.utils.threads.ThreadUtils import net.dv8tion.jda.api.OnlineStatus import net.dv8tion.jda.api.entities.Activity import net.dv8tion.jda.api.entities.User @@ -28,7 +29,6 @@ import net.dv8tion.jda.api.sharding.ShardManager import net.dv8tion.jda.api.utils.ChunkingFilter import net.dv8tion.jda.api.utils.MemberCachePolicy import net.dv8tion.jda.api.utils.cache.CacheFlag -import java.util.concurrent.ConcurrentLinkedQueue import kotlin.concurrent.thread import kotlin.reflect.jvm.jvmName @@ -46,6 +46,8 @@ class FoxyInstance( lateinit var selfUser: User private lateinit var topggStatsSender: TopggStatsSender private lateinit var environment: String + private val activeJobs = ThreadUtils.activeJobs + val threadPoolManager = ThreadPoolManager() suspend fun start() { val logger = KotlinLogging.logger(this::class.jvmName) @@ -75,10 +77,11 @@ class FoxyInstance( GatewayIntent.GUILD_EMOJIS_AND_STICKERS, GatewayIntent.SCHEDULED_EVENTS ).addEventListeners( - MajorEventListener(this), - GuildEventListener(this), - InteractionEventListener(this) + GuildListener(this), + InteractionsListener(this), + MessageListener(this) ) + .setAutoReconnect(true) .setStatus(OnlineStatus.ONLINE) .setActivity(Activity.customStatus(Constants.DEFAULT_ACTIVITY(config.environment))) .setShardsTotal(config.discord.totalShards) @@ -121,6 +124,4 @@ class FoxyInstance( } }) } - - private val activeJobs = ConcurrentLinkedQueue() } \ No newline at end of file diff --git a/foxy/src/main/kotlin/net/cakeyfox/foxy/utils/threads/ThreadPoolManager.kt b/foxy/src/main/kotlin/net/cakeyfox/foxy/utils/threads/ThreadPoolManager.kt new file mode 100644 index 00000000..79c0d9b3 --- /dev/null +++ b/foxy/src/main/kotlin/net/cakeyfox/foxy/utils/threads/ThreadPoolManager.kt @@ -0,0 +1,41 @@ +package net.cakeyfox.foxy.utils.threads + +import kotlinx.coroutines.* +import mu.KotlinLogging +import net.dv8tion.jda.api.events.Event +import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent +import net.dv8tion.jda.api.events.message.MessageReceivedEvent +import java.util.concurrent.ExecutorService + +class ThreadPoolManager { + private val coroutineMessageExecutor: ExecutorService = ThreadUtils.createThreadPool("MessageExecutor [%d]") + private val coroutineMessageDispatcher = coroutineMessageExecutor.asCoroutineDispatcher() + private val activeJobs = ThreadUtils.activeJobs + + @OptIn(DelicateCoroutinesApi::class) + fun launchMessageJob(event: Event, block: suspend CoroutineScope.() -> Unit) { + val coroutineName = when (event) { + is MessageReceivedEvent -> "Message ${event.message} by user ${event.author}" + is SlashCommandInteractionEvent -> "Slash Command ${event.fullCommandName} by user ${event.user}" + is MessageContextInteractionEvent -> "User Command ${event.fullCommandName} by user ${event.user}" + else -> throw IllegalArgumentException("Event $event is not supported") + } + + val start = System.currentTimeMillis() + val job = GlobalScope.launch( + coroutineMessageDispatcher + CoroutineName(coroutineName), + block = block + ) + + activeJobs.add(job) + job.invokeOnCompletion { + activeJobs.remove(job) + val end = System.currentTimeMillis() + val time = end - start + if (time > 10_000) { + KotlinLogging.logger("MessageExecutor").warn { "Job $job took ${time}ms to complete" } + } + } + } +} \ No newline at end of file diff --git a/foxy/src/main/kotlin/net/cakeyfox/foxy/utils/threads/ThreadUtils.kt b/foxy/src/main/kotlin/net/cakeyfox/foxy/utils/threads/ThreadUtils.kt new file mode 100644 index 00000000..55b86d67 --- /dev/null +++ b/foxy/src/main/kotlin/net/cakeyfox/foxy/utils/threads/ThreadUtils.kt @@ -0,0 +1,14 @@ +package net.cakeyfox.foxy.utils.threads + +import com.google.common.util.concurrent.ThreadFactoryBuilder +import kotlinx.coroutines.Job +import java.util.concurrent.ConcurrentLinkedQueue +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +object ThreadUtils { + fun createThreadPool(name: String): ExecutorService = + Executors.newCachedThreadPool(ThreadFactoryBuilder().setNameFormat(name).build()) + + val activeJobs = ConcurrentLinkedQueue() +} \ No newline at end of file