Skip to content

Commit 787fa7c

Browse files
committed
Command timeouts and context coroutines
1 parent 67369f8 commit 787fa7c

File tree

4 files changed

+60
-13
lines changed

4 files changed

+60
-13
lines changed

src/main/kotlin/com/mrkirby153/botcore/command/slashcommand/dsl/Context.kt

+5-4
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ class SlashContext(
9797
*
9898
* @param event The [UserContextInteractionEvent] that this context wraps
9999
*/
100-
class UserContext(event: UserContextInteractionEvent) : UserContextInteraction by event {
100+
class UserContext(event: UserContextInteractionEvent, scope: CoroutineScope) :
101+
UserContextInteraction by event, CoroutineScope by scope {
101102

102103
}
103104

@@ -106,10 +107,10 @@ class UserContext(event: UserContextInteractionEvent) : UserContextInteraction b
106107
*
107108
* @param event The [MessageContextInteractionEvent] that this context wraps
108109
*/
109-
class MessageContext(private val event: MessageContextInteractionEvent) :
110-
MessageContextInteraction by event {
110+
class MessageContext(private val event: MessageContextInteractionEvent, scope: CoroutineScope) :
111+
MessageContextInteraction by event, CoroutineScope by scope {
111112
/**
112-
* The message that the interaction is being ran from
113+
* The message that the interaction is being run from
113114
*/
114115
val message
115116
get() = event.target

src/main/kotlin/com/mrkirby153/botcore/command/slashcommand/dsl/ContextCommands.kt

+13-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.mrkirby153.botcore.command.slashcommand.dsl
22

33
import com.mrkirby153.botcore.utils.PrerequisiteCheck
44
import net.dv8tion.jda.api.interactions.commands.context.ContextInteraction
5+
import java.util.concurrent.TimeUnit
56

67
/**
78
* Top level class for context menu interactions
@@ -16,10 +17,12 @@ open class ContextCommand<Event : ContextInteraction<*>>(
1617
* The name of the command
1718
*/
1819

19-
private lateinit var commandAction: (Event) -> Unit
20+
private lateinit var commandAction: suspend (Event) -> Unit
2021

2122
private val checks = mutableListOf<PrerequisiteCheck<Event>.() -> Unit>()
2223

24+
internal var timeout: Long = 30_000
25+
2326
/**
2427
* If this command should be enabled by default
2528
*/
@@ -28,7 +31,7 @@ open class ContextCommand<Event : ContextInteraction<*>>(
2831
/**
2932
* The function ran when this interaction is invoked
3033
*/
31-
fun action(action: (Event) -> Unit) {
34+
fun action(action: suspend (Event) -> Unit) {
3235
this.commandAction = action
3336
}
3437

@@ -42,7 +45,7 @@ open class ContextCommand<Event : ContextInteraction<*>>(
4245
/**
4346
* Executes this command with the provided [event]
4447
*/
45-
fun execute(event: Event) {
48+
suspend fun execute(event: Event) {
4649
val checkCtx = PrerequisiteCheck(event)
4750
checks.forEach {
4851
it(checkCtx)
@@ -59,6 +62,13 @@ open class ContextCommand<Event : ContextInteraction<*>>(
5962
.setEphemeral(true).queue()
6063
}
6164
}
65+
66+
/**
67+
* Determines how long an invocation can run for
68+
*/
69+
fun timeout(timeout: Long, unit: TimeUnit) {
70+
this.timeout = unit.toMillis(timeout)
71+
}
6272
}
6373

6474
/**

src/main/kotlin/com/mrkirby153/botcore/command/slashcommand/dsl/DslCommandExecutor.kt

+24-6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import kotlinx.coroutines.CoroutineScope
1010
import kotlinx.coroutines.Dispatchers
1111
import kotlinx.coroutines.SupervisorJob
1212
import kotlinx.coroutines.launch
13+
import kotlinx.coroutines.withTimeout
1314
import net.dv8tion.jda.api.JDA
1415
import net.dv8tion.jda.api.entities.Guild
1516
import net.dv8tion.jda.api.events.GenericEvent
@@ -291,32 +292,49 @@ class DslCommandExecutor private constructor(
291292
override suspend fun onEvent(event: GenericEvent) {
292293
when (event) {
293294
is SlashCommandInteractionEvent -> {
294-
if (getSlashCommand(event) != null) {
295+
val slashCommand = getSlashCommand(event)
296+
if (slashCommand != null) {
295297
scope.launch {
296-
execute(event, this)
298+
log.trace("Running command ${slashCommand.name} with a timeout of ${slashCommand.timeout}")
299+
withTimeout(slashCommand.timeout) {
300+
execute(event, this)
301+
}
302+
297303
}
298304
}
299305
}
300306

301307
is CommandAutoCompleteInteractionEvent -> {
302-
if (getSlashCommand(event) != null) {
308+
val slashCommand = getSlashCommand(event)
309+
if (slashCommand != null) {
303310
scope.launch {
304-
handleAutocomplete(event)
311+
log.trace("Running autocomplete handler ${slashCommand.name} with a timeout of ${slashCommand.timeout}")
312+
withTimeout(slashCommand.autocompleteTimeout) {
313+
handleAutocomplete(event)
314+
}
305315
}
306316
}
307317
}
308318

309319
is UserContextInteractionEvent -> {
310320
scope.launch {
311321
userContextCommands.firstOrNull { it.name == event.name }
312-
?.execute(UserContext(event))
322+
?.apply {
323+
withTimeout(timeout) {
324+
execute(UserContext(event, scope))
325+
}
326+
}
313327
}
314328
}
315329

316330
is MessageContextInteractionEvent -> {
317331
scope.launch {
318332
messageContextCommands.firstOrNull { it.name == event.name }
319-
?.execute(MessageContext(event))
333+
?.apply {
334+
withTimeout(timeout) {
335+
execute(MessageContext(event, scope))
336+
}
337+
}
320338
}
321339
}
322340
}

src/main/kotlin/com/mrkirby153/botcore/command/slashcommand/dsl/SlashCommand.kt

+18
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInterac
66
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent
77
import net.dv8tion.jda.api.interactions.commands.Command
88
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions
9+
import java.util.concurrent.TimeUnit
910

1011
open class AbstractSlashCommand(
1112
val name: String
@@ -15,6 +16,9 @@ open class AbstractSlashCommand(
1516

1617
internal var action: (suspend SlashContext.() -> Unit)? = null
1718

19+
internal var timeout: Long = 30_000 // 30 seconds
20+
internal var autocompleteTimeout: Long = 5_000 // 5 seconds
21+
1822
/**
1923
* Executes the slash command. If [body] is null, this is a no-op
2024
*/
@@ -40,6 +44,20 @@ open class AbstractSlashCommand(
4044
internal open fun addArgument(name: String, argument: ArgumentContainer<*, *>) {
4145
arguments[name] = argument
4246
}
47+
48+
/**
49+
* Sets how long this command is allowed to execute before it times out
50+
*/
51+
fun commandTimeout(time: Long, timeUnit: TimeUnit) {
52+
timeout = timeUnit.toMillis(time)
53+
}
54+
55+
/**
56+
* Sets how long any autocomplete query can take
57+
*/
58+
fun autocompleteTimeout(time: Long, timeUnit: TimeUnit) {
59+
autocompleteTimeout = timeUnit.toMillis(time)
60+
}
4361
}
4462

4563
@SlashDsl

0 commit comments

Comments
 (0)