Skip to content

Commit 1f18977

Browse files
committed
modals: Support reading a single value from string selects and file uploads
1 parent 91b5a8b commit 1f18977

File tree

4 files changed

+81
-23
lines changed

4 files changed

+81
-23
lines changed

BotCommands-core/src/main/kotlin/io/github/freya022/botcommands/api/parameters/resolvers/ModalParameterResolver.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ import kotlin.reflect.KType
2222
*
2323
* ### Types supported by default
2424
* - [TextInput] : `String`
25-
* - [StringSelectMenu] : `List<String>`
25+
* - [StringSelectMenu] : `List<String>`, `String`
2626
* - [EntitySelectMenu] : [Mentions], `T` and `List<T>` where `T` is one of:
2727
* [IMentionable], [Role], [User], [InputUser], [Member], [GuildChannel]
28-
* - [AttachmentUpload] : `List` of [Message.Attachment]
28+
* - [AttachmentUpload] : `List` of [Message.Attachment], [Message.Attachment]
2929
*
3030
* @param T Type of the implementation
3131
* @param R Type of the returned resolved objects
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package io.github.freya022.botcommands.internal.modals.resolvers
2+
3+
import io.github.freya022.botcommands.api.core.service.annotations.Resolver
4+
import io.github.freya022.botcommands.api.modals.ModalEvent
5+
import io.github.freya022.botcommands.api.modals.options.ModalOption
6+
import io.github.freya022.botcommands.api.parameters.ClassParameterResolver
7+
import io.github.freya022.botcommands.api.parameters.resolvers.ModalParameterResolver
8+
import net.dv8tion.jda.api.components.Component
9+
import net.dv8tion.jda.api.entities.Message
10+
import net.dv8tion.jda.api.interactions.modals.ModalMapping
11+
12+
@Resolver
13+
internal object ModalAttachmentResolver :
14+
ClassParameterResolver<ModalAttachmentResolver, Message.Attachment>(Message.Attachment::class),
15+
ModalParameterResolver<ModalAttachmentResolver, Message.Attachment> {
16+
17+
override suspend fun resolveSuspend(
18+
option: ModalOption,
19+
event: ModalEvent,
20+
modalMapping: ModalMapping,
21+
): Message.Attachment? {
22+
return when (modalMapping.type) {
23+
Component.Type.FILE_UPLOAD -> {
24+
val values = modalMapping.asAttachmentList
25+
if (values.size > 1)
26+
error("Cannot get an Attachment from a file upload with more than a single value")
27+
values.firstOrNull()
28+
}
29+
else -> error("Cannot get an Attachment from a ${modalMapping.type} input")
30+
}
31+
}
32+
}

BotCommands-core/src/main/kotlin/io/github/freya022/botcommands/internal/modals/resolvers/ModalStringResolver.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import io.github.freya022.botcommands.api.modals.ModalEvent
55
import io.github.freya022.botcommands.api.modals.options.ModalOption
66
import io.github.freya022.botcommands.api.parameters.ClassParameterResolver
77
import io.github.freya022.botcommands.api.parameters.resolvers.ModalParameterResolver
8+
import net.dv8tion.jda.api.components.Component
89
import net.dv8tion.jda.api.interactions.modals.ModalMapping
910

1011
@Resolver
@@ -16,5 +17,16 @@ internal object ModalStringResolver :
1617
option: ModalOption,
1718
event: ModalEvent,
1819
modalMapping: ModalMapping,
19-
): String = modalMapping.asString
20+
): String? {
21+
return when (modalMapping.type) {
22+
Component.Type.STRING_SELECT -> {
23+
val values = modalMapping.asStringList
24+
if (values.size > 1)
25+
error("Cannot get a String from a string select menu with more than a single value")
26+
values.firstOrNull()
27+
}
28+
Component.Type.TEXT_INPUT -> modalMapping.asString
29+
else -> error("Cannot get a String from a ${modalMapping.type} input")
30+
}
31+
}
2032
}

BotCommands-core/src/test/kotlin/io/github/freya022/botcommands/modals/ModalInputResolverTests.kt

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import io.github.freya022.botcommands.api.parameters.resolvers.ModalParameterRes
1313
import io.github.freya022.botcommands.internal.modals.resolvers.*
1414
import io.github.freya022.botcommands.internal.parameters.ResolverContainer
1515
import io.mockk.every
16+
import io.mockk.just
1617
import io.mockk.mockk
18+
import io.mockk.runs
19+
import net.dv8tion.jda.api.components.Component
20+
import net.dv8tion.jda.api.components.Component.Type.*
1721
import net.dv8tion.jda.api.entities.*
1822
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel
1923
import net.dv8tion.jda.api.interactions.modals.ModalMapping
@@ -50,27 +54,32 @@ object ModalInputResolverTests {
5054
every { channels } returns this@ModalInputResolverTests.channels
5155
every { getMentions() } returns this@ModalInputResolverTests.roles
5256
}
53-
private val attachments = listOf<Message.Attachment>(mockk())
57+
private val attachments = listOf<Message.Attachment>(mockk {
58+
every { close() } just runs
59+
})
5460

5561
@MethodSource("modalInputs")
5662
@ParameterizedTest
57-
suspend fun `Modal input parameter can be resolved`(index: Int, expected: Any?) {
63+
suspend fun `Modal input parameter can be resolved`(index: Int, type: Component.Type, expected: Any?) {
5864
val serviceContainer = mockk<ServiceContainer> {
5965
every { getServiceNamesForAnnotation(Resolver::class) } returns listOf(
6066
"modalMentionsResolver",
6167
"modalStringResolver",
6268
"modalStringListResolver",
69+
"modalAttachmentResolver",
6370
"modalAttachmentListResolver",
6471
)
6572

6673
every { findAnnotationOnService("modalMentionsResolver", Resolver::class) } returns Resolver(0)
6774
every { findAnnotationOnService("modalStringResolver", Resolver::class) } returns Resolver(0)
6875
every { findAnnotationOnService("modalStringListResolver", Resolver::class) } returns Resolver(0)
76+
every { findAnnotationOnService("modalAttachmentResolver", Resolver::class) } returns Resolver(0)
6977
every { findAnnotationOnService("modalAttachmentListResolver", Resolver::class) } returns Resolver(0)
7078

7179
every { getService("modalMentionsResolver", ParameterResolver::class) } returns ModalMentionsResolver
7280
every { getService("modalStringResolver", ParameterResolver::class) } returns ModalStringResolver
7381
every { getService("modalStringListResolver", ParameterResolver::class) } returns ModalStringListResolver
82+
every { getService("modalAttachmentResolver", ParameterResolver::class) } returns ModalAttachmentResolver
7483
every { getService("modalAttachmentListResolver", ParameterResolver::class) } returns ModalAttachmentListResolver
7584
}
7685
val resolvers = ResolverContainer(serviceContainer, listOf(ModalIMentionableResolverFactory))
@@ -83,6 +92,7 @@ object ModalInputResolverTests {
8392
every { asStringList } returns strings
8493
every { asMentions } returns mentions
8594
every { asAttachmentList } returns attachments
95+
every { this@mockk.type } returns type
8696
}
8797

8898
val value = resolver.resolveSuspend(
@@ -97,28 +107,30 @@ object ModalInputResolverTests {
97107
@JvmStatic
98108
fun modalInputs(): List<Arguments> {
99109
val listOf = listOf(
100-
arguments("TextInput String", 0, STRING),
101-
arguments("Select menu strings", 1, strings),
102-
arguments("Select menu mentionable", 2, role),
103-
arguments("Select menu mentionables", 3, roles),
104-
arguments("Select menu role", 4, role),
105-
arguments("Select menu roles", 5, roles),
106-
arguments("Select menu user", 6, user),
107-
arguments("Select menu users", 7, users),
108-
arguments("Select menu input user", 8, inputUser),
109-
arguments("Select menu input users", 9, inputUsers),
110-
arguments("Select menu member", 10, member),
111-
arguments("Select menu members", 11, members),
112-
arguments("Select menu channel", 12, channel),
113-
arguments("Select menu channels", 13, channels),
114-
arguments("Select menu mentions", 14, mentions),
115-
arguments("Attachments", 15, attachments),
110+
arguments("TextInput String", 0, TEXT_INPUT, STRING),
111+
arguments("Select menu string", 16, STRING_SELECT, STRING),
112+
arguments("Select menu strings", 1, STRING_SELECT, strings),
113+
arguments("Select menu mentionable", 2, MENTIONABLE_SELECT, role),
114+
arguments("Select menu mentionables", 3, MENTIONABLE_SELECT, roles),
115+
arguments("Select menu role", 4, ROLE_SELECT, role),
116+
arguments("Select menu roles", 5, ROLE_SELECT, roles),
117+
arguments("Select menu user", 6, USER_SELECT, user),
118+
arguments("Select menu users", 7, USER_SELECT, users),
119+
arguments("Select menu input user", 8, USER_SELECT, inputUser),
120+
arguments("Select menu input users", 9, USER_SELECT, inputUsers),
121+
arguments("Select menu member", 10, USER_SELECT, member),
122+
arguments("Select menu members", 11, USER_SELECT, members),
123+
arguments("Select menu channel", 12, CHANNEL_SELECT, channel),
124+
arguments("Select menu channels", 13, CHANNEL_SELECT, channels),
125+
arguments("Select menu mentions", 14, MENTIONABLE_SELECT, mentions),
126+
arguments("Attachment", 17, FILE_UPLOAD, attachments.first()),
127+
arguments("Attachments", 15, FILE_UPLOAD, attachments),
116128
)
117129
return listOf
118130
}
119131

120-
private fun arguments(name: String, index: Int, expected: Any?) =
121-
argumentSet(name, index, expected)
132+
private fun arguments(name: String, index: Int, type: Component.Type, expected: Any?) =
133+
argumentSet(name, index, type, expected)
122134

123135
private fun userFunc(
124136
@Suppress("unused") textInput: String,
@@ -137,5 +149,7 @@ object ModalInputResolverTests {
137149
@Suppress("unused") selectedChannels: List<GuildChannel>,
138150
@Suppress("unused") selectedMentions: Mentions,
139151
@Suppress("unused") attachments: List<Message.Attachment>,
152+
@Suppress("unused") selectedString: String,
153+
@Suppress("unused") attachment: Message.Attachment,
140154
) {}
141155
}

0 commit comments

Comments
 (0)