From 8a2fc89d8460697b3f651a6ded59fa5ef952cbba Mon Sep 17 00:00:00 2001 From: Gareth Coles Date: Tue, 31 Dec 2024 18:19:56 +0000 Subject: [PATCH] Add threadpool context for interaction events. --- .../kotlin/dev/kordex/core/ExtensibleBot.kt | 152 +++++++++++------- .../core/builders/ExtensibleBotBuilder.kt | 7 + 2 files changed, 101 insertions(+), 58 deletions(-) diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/ExtensibleBot.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/ExtensibleBot.kt index c37653d5f..a4821adde 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/ExtensibleBot.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/ExtensibleBot.kt @@ -53,6 +53,7 @@ import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromJsonElement import org.koin.core.component.inject import org.koin.dsl.bind +import kotlin.Throws import kotlin.concurrent.thread /** @@ -74,6 +75,10 @@ public open class ExtensibleBot( override var mutex: Mutex? = Mutex() override var locking: Boolean = settings.membersBuilder.lockMemberRequests + @OptIn(DelicateCoroutinesApi::class) + public val interactionCoroutineContext: CoroutineDispatcher = + newFixedThreadPoolContext(settings.interactionContextThreads, "kord-extensions-interactions") + /** @suppress Meant for internal use by public inline function. **/ public val kordRef: Kord by inject() @@ -185,6 +190,7 @@ public open class ExtensibleBot( **/ public open suspend fun stop() { dataCollector.stop() + interactionCoroutineContext.cancel() getKoin().get().logout() } @@ -215,6 +221,8 @@ public open class ExtensibleBot( } dataCollector.stop() + interactionCoroutineContext.cancel() + getKoin().get().shutdown() KordExContext.stopKoin() @@ -288,30 +296,6 @@ public open class ExtensibleBot( logger.warn { "Disconnected: $closeCode" } } - on { - try { - getKoin().get().handle(this) - } catch (e: Exception) { - logger.error(e) { "Exception thrown while handling button interaction event" } - } - } - - on { - try { - getKoin().get().handle(this) - } catch (e: Exception) { - logger.error(e) { "Exception thrown while handling select menu interaction event" } - } - } - - on { - try { - getKoin().get().handle(this) - } catch (e: Exception) { - logger.error(e) { "Exception thrown while handling modal interaction event" } - } - } - if (settings.chatCommandsBuilder.enabled) { on { try { @@ -328,38 +312,6 @@ public open class ExtensibleBot( } if (settings.applicationCommandsBuilder.enabled) { - on { - try { - getKoin().get().handle(this) - } catch (e: Exception) { - logger.error(e) { "Exception thrown while handling slash command interaction event" } - } - } - - on { - try { - getKoin().get().handle(this) - } catch (e: Exception) { - logger.error(e) { "Exception thrown while handling message command interaction event" } - } - } - - on { - try { - getKoin().get().handle(this) - } catch (e: Exception) { - logger.error(e) { "Exception thrown while handling user command interaction event" } - } - } - - on { - try { - getKoin().get().handle(this) - } catch (e: Exception) { - logger.error(e) { "Exception thrown while handling autocomplete interaction event" } - } - } - try { getKoin().get().initialRegistration() } catch (e: Exception) { @@ -431,11 +383,95 @@ public open class ExtensibleBot( }.catch { logger.error(it) { "Exception thrown from low-level event handler: $consumer" } } .launchIn(kordRef) + /** + * @suppress Internal function used to additionally process all events. Don't call this yourself. + */ + @Suppress("TooGenericExceptionCaught") + public suspend inline fun publishEvent(event: Event) { + when (event) { + // General interaction events + is ButtonInteractionCreateEvent -> + kordRef.launch(interactionCoroutineContext) { + try { + getKoin().get().handle(event) + + } catch (e: Exception) { + logger.error(e) { "Exception thrown while handling button interaction event" } + } + } + + is SelectMenuInteractionCreateEvent -> + kordRef.launch(interactionCoroutineContext) { + try { + getKoin().get().handle(event) + } catch (e: Exception) { + logger.error(e) { "Exception thrown while handling select menu interaction event" } + } + } + + is ModalSubmitInteractionCreateEvent -> + kordRef.launch(interactionCoroutineContext) { + try { + getKoin().get().handle(event) + } catch (e: Exception) { + logger.error(e) { "Exception thrown while handling modal interaction event" } + } + } + + // Interaction command events + is ChatInputCommandInteractionCreateEvent -> + if (settings.applicationCommandsBuilder.enabled) { + kordRef.launch(interactionCoroutineContext) { + try { + getKoin().get().handle(event) + } catch (e: Exception) { + logger.error(e) { "Exception thrown while handling slash command interaction event" } + } + } + } + + is MessageCommandInteractionCreateEvent -> + if (settings.applicationCommandsBuilder.enabled) { + kordRef.launch(interactionCoroutineContext) { + try { + getKoin().get().handle(event) + } catch (e: Exception) { + logger.error(e) { "Exception thrown while handling message command interaction event" } + } + } + } + + is UserCommandInteractionCreateEvent -> + if (settings.applicationCommandsBuilder.enabled) { + kordRef.launch(interactionCoroutineContext) { + try { + getKoin().get().handle(event) + } catch (e: Exception) { + logger.error(e) { "Exception thrown while handling user command interaction event" } + } + } + } + + is AutoCompleteInteractionCreateEvent -> + if (settings.applicationCommandsBuilder.enabled) { + kordRef.launch(interactionCoroutineContext) { + try { + getKoin().get().handle(event) + } catch (e: Exception) { + logger.error(e) { "Exception thrown while handling autocomplete interaction event" } + } + } + } + } + + eventPublisher.emit(event) + } + /** * @suppress Internal function used to process Kord events. Don't call this yourself. */ protected suspend inline fun sendKord(event: Event) { - eventPublisher.emit(event) + publishEvent(event) } /** @@ -445,7 +481,7 @@ public open class ExtensibleBot( */ public suspend inline fun send(event: Event, filter: Boolean = true) { if (!filter || settings.kordExEventFilter?.invoke(event) != false) { - eventPublisher.emit(event) + publishEvent(event) } } diff --git a/kord-extensions/src/main/kotlin/dev/kordex/core/builders/ExtensibleBotBuilder.kt b/kord-extensions/src/main/kotlin/dev/kordex/core/builders/ExtensibleBotBuilder.kt index 054bc7b59..b686ad994 100644 --- a/kord-extensions/src/main/kotlin/dev/kordex/core/builders/ExtensibleBotBuilder.kt +++ b/kord-extensions/src/main/kotlin/dev/kordex/core/builders/ExtensibleBotBuilder.kt @@ -79,6 +79,13 @@ public open class ExtensibleBotBuilder { /** Called to create an [ExtensibleBot], can be set to the constructor of your own subtype if needed. **/ public var constructor: (ExtensibleBotBuilder, String) -> ExtensibleBot = ::ExtensibleBot + /** + * The number of threads to use for interaction event coroutines. + * + * Defaults to double the available CPU cores, as returned by `Runtime.getRuntime().availableProcessors()`. + */ + public var interactionContextThreads: Int = Runtime.getRuntime().availableProcessors() * 2 + /** @suppress Builder that shouldn't be set directly by the user. **/ public val aboutBuilder: AboutBuilder = AboutBuilder()