Add temporary VCs

This commit is contained in:
kageru 2019-10-19 11:32:28 +02:00
parent 5a95138861
commit af65dcc06b
Signed by: kageru
GPG Key ID: 8282A2BEA4ADA3D2
7 changed files with 130 additions and 5 deletions

View File

@ -1,19 +1,20 @@
package moe.kageru.kagebot.features 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 debug = DebugFeature()
private val help = HelpFeature() private val help = HelpFeature()
private val getConfig = GetConfigFeature() private val getConfig = GetConfigFeature()
private val setConfig = SetConfigFeature() 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( private val featureMap = mapOf(
"help" to help, "help" to help,
"debug" to debug, "debug" to debug,
"welcome" to welcome, "welcome" to welcome,
"getConfig" to getConfig, "getConfig" to getConfig,
"setConfig" to setConfig, "setConfig" to setConfig,
"timeout" to timeout "timeout" to timeout,
"vc" to vc
) )
fun findByString(feature: String) = featureMap[feature] fun findByString(feature: String) = featureMap[feature]

View File

@ -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"
}
}

View File

@ -7,6 +7,7 @@ object Dao {
private val db = DBMaker.fileDB("kagebot.db").fileMmapEnable().closeOnJvmShutdown().make() private val db = DBMaker.fileDB("kagebot.db").fileMmapEnable().closeOnJvmShutdown().make()
private val prisoners = db.hashMap("timeout", Serializer.LONG, Serializer.LONG_ARRAY).createOrOpen() 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 commands = db.hashMap("commands", Serializer.STRING, Serializer.INTEGER).createOrOpen()
private val tempVcs = db.hashSet("vcs", Serializer.STRING).createOrOpen()
fun saveTimeout(releaseTime: Long, roles: List<Long>) { fun saveTimeout(releaseTime: Long, roles: List<Long>) {
prisoners[releaseTime] = roles.toLongArray() prisoners[releaseTime] = roles.toLongArray()
@ -27,4 +28,16 @@ object Dao {
prisoners.remove(releaseTime) prisoners.remove(releaseTime)
return timeout 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)
}
} }

View File

@ -123,3 +123,7 @@ feature = "setConfig"
[[command]] [[command]]
trigger = "!prison" trigger = "!prison"
feature = "timeout" feature = "timeout"
[[command]]
trigger = "!vc"
feature = "vc"

View File

@ -19,6 +19,7 @@ import org.javacord.api.event.message.MessageCreateEvent
import org.javacord.core.entity.message.embed.EmbedBuilderDelegateImpl import org.javacord.core.entity.message.embed.EmbedBuilderDelegateImpl
import java.io.File import java.io.File
import java.util.* import java.util.*
import java.util.concurrent.CompletableFuture
object TestUtil { object TestUtil {
private val TIMEOUT_ROLE = mockk<Role> { private val TIMEOUT_ROLE = mockk<Role> {
@ -58,6 +59,7 @@ object TestUtil {
every { messageAuthor.isYourself } returns isBot every { messageAuthor.isYourself } returns isBot
every { messageAuthor.isBotOwner } returns false every { messageAuthor.isBotOwner } returns false
every { messageAuthor.asUser() } returns Optional.of(messageableAuthor(replyEmbeds)) 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) every { sendMessage(capture(sentMessages)) } returns mockk(relaxed = true)
} }
} }
val api = mockk<DiscordApi> { val api = mockk<DiscordApi>(relaxed = true) {
every { getServerById(any<String>()) } returns Optional.of(mockk { every { getServerById(any<String>()) } returns Optional.of(mockk(relaxed = true) {
every { icon.ifPresent(any()) } just Runs every { icon.ifPresent(any()) } just Runs
every { getTextChannelById(any<String>()) } returns channel every { getTextChannelById(any<String>()) } returns channel
every { getTextChannelsByName(any()) } returns listOf(channel.get()) every { getTextChannelsByName(any()) } returns listOf(channel.get())
every { getRolesByNameIgnoreCase("testrole") } returns listOf(TEST_ROLE) every { getRolesByNameIgnoreCase("testrole") } returns listOf(TEST_ROLE)
every { getRolesByNameIgnoreCase("timeout") } returns listOf(TIMEOUT_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 { getMembersByName(any()) } returns listOf(mockk(relaxed = true) {
every { id } returns 123 every { id } returns 123
every { getRoles(any()) } returns listOf(TEST_ROLE) every { getRoles(any()) } returns listOf(TEST_ROLE)
@ -102,6 +109,8 @@ object TestUtil {
}) })
} }
Globals.api = api Globals.api = api
// write our mocked server to the config
Config.server = api.getServerById("").get()
ConfigParser.initialLoad("testconfig.toml") ConfigParser.initialLoad("testconfig.toml")
} }

View File

@ -19,6 +19,7 @@ import moe.kageru.kagebot.Util
import moe.kageru.kagebot.config.Config import moe.kageru.kagebot.config.Config
import moe.kageru.kagebot.config.Config.localization import moe.kageru.kagebot.config.Config.localization
import moe.kageru.kagebot.config.LocalizationSpec 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.message.embed.EmbedBuilder
import org.javacord.api.entity.permission.Role import org.javacord.api.entity.permission.Role
import org.javacord.api.entity.user.User import org.javacord.api.entity.user.User
@ -292,4 +293,29 @@ class CommandTest : StringSpec({
roles shouldBe mutableListOf(Util.findRole("testrole")) 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
}
}
}) })

View File

@ -21,6 +21,9 @@ content = [
[feature.timeout] [feature.timeout]
role = "timeout" role = "timeout"
[feature.vc]
category = "testcategory"
[[command]] [[command]]
trigger = "!debug" trigger = "!debug"
feature = "debug" feature = "debug"