diff --git a/src/main/kotlin/moe/kageru/kagebot/features/Features.kt b/src/main/kotlin/moe/kageru/kagebot/features/Features.kt index ce1f25b..9e38b7f 100644 --- a/src/main/kotlin/moe/kageru/kagebot/features/Features.kt +++ b/src/main/kotlin/moe/kageru/kagebot/features/Features.kt @@ -1,19 +1,20 @@ package moe.kageru.kagebot.features -class Features(val welcome: WelcomeFeature?, val timeout: TimeoutFeature?) { +class Features(val welcome: WelcomeFeature?, val timeout: TimeoutFeature?, vc: TempVCFeature = TempVCFeature(null)) { private val debug = DebugFeature() private val help = HelpFeature() private val getConfig = GetConfigFeature() private val setConfig = SetConfigFeature() - private val all = listOf(welcome, debug, help, getConfig, setConfig, timeout) + private val all = listOf(welcome, debug, help, getConfig, setConfig, timeout, vc) private val featureMap = mapOf( "help" to help, "debug" to debug, "welcome" to welcome, "getConfig" to getConfig, "setConfig" to setConfig, - "timeout" to timeout + "timeout" to timeout, + "vc" to vc ) fun findByString(feature: String) = featureMap[feature] diff --git a/src/main/kotlin/moe/kageru/kagebot/features/TempVCFeature.kt b/src/main/kotlin/moe/kageru/kagebot/features/TempVCFeature.kt new file mode 100644 index 0000000..1086c32 --- /dev/null +++ b/src/main/kotlin/moe/kageru/kagebot/features/TempVCFeature.kt @@ -0,0 +1,69 @@ +package moe.kageru.kagebot.features + +import com.fasterxml.jackson.annotation.JsonProperty +import moe.kageru.kagebot.Log +import moe.kageru.kagebot.Util.failed +import moe.kageru.kagebot.config.Config +import moe.kageru.kagebot.persistence.Dao +import org.javacord.api.DiscordApi +import org.javacord.api.entity.channel.ChannelCategory +import org.javacord.api.entity.channel.ServerVoiceChannel +import org.javacord.api.event.message.MessageCreateEvent +import java.util.concurrent.CompletionException + +class TempVCFeature(@JsonProperty("category") category: String? = null) : EventFeature, MessageFeature { + private val category: ChannelCategory? = + category?.let { Config.server.getChannelCategoriesByNameIgnoreCase(it).first() } + + override fun handle(message: MessageCreateEvent) { + if (" " !in message.readableMessageContent) { + message.channel.sendMessage("Invalid syntax, expected 2 arguments") + return + } + val (_, limit) = message.readableMessageContent.split(" ", limit = 2) + limit.toLongOrNull()?.let { parsedLimit -> + if (parsedLimit > 99) { + message.channel.sendMessage("You can’t create a channel with that many users.") + } + createChannel(message, parsedLimit) + message.channel.sendMessage("Done") + } ?: message.channel.sendMessage("Invalid syntax, expected a number, got $limit") + } + + override fun register(api: DiscordApi) { + api.addServerVoiceChannelMemberLeaveListener { event -> + println("asdsad") + if (event.channel.connectedUsers.isEmpty() && Dao.isTemporaryVC(event.channel.idAsString)) { + deleteChannel(event.channel) + } + } + } + + private fun deleteChannel(channel: ServerVoiceChannel) { + val deletion = channel.delete("Empty temporary channel") + if (deletion.failed()) { + Log.warn("Attempted to delete temporary VC without the necessary permissions") + } else { + Dao.removeTemporaryVC(channel.idAsString) + } + } + + private fun createChannel(message: MessageCreateEvent, limit: Long) { + val creation = Config.server.createVoiceChannelBuilder().apply { + setUserlimit(limit.toInt()) + setName(generateChannelName(message)) + setAuditLogReason("Created temporary VC for user ${message.messageAuthor.discriminatedName}") + category?.let { setCategory(it) } + }.create() + try { + val channel = creation.join() + Dao.addTemporaryVC(channel.idAsString) + } catch (e: CompletionException) { + Log.warn("Attempted to create temporary VC without the necessary permissions") + } + } + + private fun generateChannelName(message: MessageCreateEvent): String { + return "${message.messageAuthor.name}’s volatile corner" + } +} \ No newline at end of file diff --git a/src/main/kotlin/moe/kageru/kagebot/persistence/Dao.kt b/src/main/kotlin/moe/kageru/kagebot/persistence/Dao.kt index fb159fc..6c565a5 100644 --- a/src/main/kotlin/moe/kageru/kagebot/persistence/Dao.kt +++ b/src/main/kotlin/moe/kageru/kagebot/persistence/Dao.kt @@ -7,6 +7,7 @@ object Dao { private val db = DBMaker.fileDB("kagebot.db").fileMmapEnable().closeOnJvmShutdown().make() private val prisoners = db.hashMap("timeout", Serializer.LONG, Serializer.LONG_ARRAY).createOrOpen() private val commands = db.hashMap("commands", Serializer.STRING, Serializer.INTEGER).createOrOpen() + private val tempVcs = db.hashSet("vcs", Serializer.STRING).createOrOpen() fun saveTimeout(releaseTime: Long, roles: List) { prisoners[releaseTime] = roles.toLongArray() @@ -27,4 +28,16 @@ object Dao { prisoners.remove(releaseTime) return timeout } + + fun isTemporaryVC(channel: String): Boolean { + return channel in tempVcs + } + + fun addTemporaryVC(channel: String) { + tempVcs.add(channel) + } + + fun removeTemporaryVC(channel: String) { + tempVcs.remove(channel) + } } diff --git a/src/main/resources/config.toml b/src/main/resources/config.toml index c1db7fd..f93db48 100644 --- a/src/main/resources/config.toml +++ b/src/main/resources/config.toml @@ -123,3 +123,7 @@ feature = "setConfig" [[command]] trigger = "!prison" feature = "timeout" + +[[command]] +trigger = "!vc" +feature = "vc" diff --git a/src/test/kotlin/moe/kageru/kagebot/TestUtil.kt b/src/test/kotlin/moe/kageru/kagebot/TestUtil.kt index cea6808..24e1fc2 100644 --- a/src/test/kotlin/moe/kageru/kagebot/TestUtil.kt +++ b/src/test/kotlin/moe/kageru/kagebot/TestUtil.kt @@ -19,6 +19,7 @@ import org.javacord.api.event.message.MessageCreateEvent import org.javacord.core.entity.message.embed.EmbedBuilderDelegateImpl import java.io.File import java.util.* +import java.util.concurrent.CompletableFuture object TestUtil { private val TIMEOUT_ROLE = mockk { @@ -58,6 +59,7 @@ object TestUtil { every { messageAuthor.isYourself } returns isBot every { messageAuthor.isBotOwner } returns false every { messageAuthor.asUser() } returns Optional.of(messageableAuthor(replyEmbeds)) + every { messageAuthor.name } returns "kageru" } } @@ -85,13 +87,18 @@ object TestUtil { every { sendMessage(capture(sentMessages)) } returns mockk(relaxed = true) } } - val api = mockk { - every { getServerById(any()) } returns Optional.of(mockk { + val api = mockk(relaxed = true) { + every { getServerById(any()) } returns Optional.of(mockk(relaxed = true) { every { icon.ifPresent(any()) } just Runs every { getTextChannelById(any()) } returns channel every { getTextChannelsByName(any()) } returns listOf(channel.get()) every { getRolesByNameIgnoreCase("testrole") } returns listOf(TEST_ROLE) every { getRolesByNameIgnoreCase("timeout") } returns listOf(TIMEOUT_ROLE) + every { getChannelCategoriesByNameIgnoreCase(any()) } returns listOf(mockk()) + every { createVoiceChannelBuilder().create() } returns mockk { + every { isCompletedExceptionally } returns false + every { join().idAsString } returns "12345" + } every { getMembersByName(any()) } returns listOf(mockk(relaxed = true) { every { id } returns 123 every { getRoles(any()) } returns listOf(TEST_ROLE) @@ -102,6 +109,8 @@ object TestUtil { }) } Globals.api = api + // write our mocked server to the config + Config.server = api.getServerById("").get() ConfigParser.initialLoad("testconfig.toml") } diff --git a/src/test/kotlin/moe/kageru/kagebot/command/CommandTest.kt b/src/test/kotlin/moe/kageru/kagebot/command/CommandTest.kt index 2241c73..bfe126f 100644 --- a/src/test/kotlin/moe/kageru/kagebot/command/CommandTest.kt +++ b/src/test/kotlin/moe/kageru/kagebot/command/CommandTest.kt @@ -19,6 +19,7 @@ import moe.kageru.kagebot.Util import moe.kageru.kagebot.config.Config import moe.kageru.kagebot.config.Config.localization import moe.kageru.kagebot.config.LocalizationSpec +import moe.kageru.kagebot.persistence.Dao import org.javacord.api.entity.message.embed.EmbedBuilder import org.javacord.api.entity.permission.Role import org.javacord.api.entity.user.User @@ -292,4 +293,29 @@ class CommandTest : StringSpec({ roles shouldBe mutableListOf(Util.findRole("testrole")) } } + "should create VC" { + withCommands( + """ + [[command]] + trigger = "!vc" + feature = "vc" + """.trimIndent() + ) { + testMessageSuccess("!vc 2", "Done") + Dao.isTemporaryVC("12345") shouldBe true + Dao.removeTemporaryVC("12345") + } + } + "should reject invalid vc command" { + withCommands( + """ + [[command]] + trigger = "!vc" + feature = "vc" + """.trimIndent() + ) { + testMessageSuccess("!vc asd", "Invalid syntax, expected a number, got asd") + Dao.isTemporaryVC("12345") shouldBe false + } + } }) diff --git a/src/test/resources/testconfig.toml b/src/test/resources/testconfig.toml index 269efe9..47e16c7 100644 --- a/src/test/resources/testconfig.toml +++ b/src/test/resources/testconfig.toml @@ -21,6 +21,9 @@ content = [ [feature.timeout] role = "timeout" +[feature.vc] +category = "testcategory" + [[command]] trigger = "!debug" feature = "debug"