Added redirect action
This commit is contained in:
parent
6d7cd5b776
commit
92dc0a3322
|
@ -31,6 +31,9 @@ dependencies {
|
|||
|
||||
testImplementation("io.kotlintest:kotlintest-runner-junit5:3.3.2")
|
||||
testImplementation("io.mockk:mockk:1.9")
|
||||
// these two are needed to access javacord internals (such as reading from sent embeds during tests)
|
||||
testImplementation("org.javacord:javacord-core:3.0.4")
|
||||
testCompile("com.fasterxml.jackson.core:jackson-databind:2.9.9")
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
|
|
|
@ -2,6 +2,7 @@ package moe.kageru.kagebot
|
|||
|
||||
import moe.kageru.kagebot.Config.Companion.config
|
||||
import moe.kageru.kagebot.Util.doIf
|
||||
import moe.kageru.kagebot.Util.ifNotEmpty
|
||||
import org.javacord.api.entity.message.MessageAuthor
|
||||
import org.javacord.api.event.message.MessageCreateEvent
|
||||
|
||||
|
@ -11,20 +12,20 @@ class Command(
|
|||
trigger: String?,
|
||||
private val response: String?,
|
||||
matchType: MatchType?,
|
||||
private val deleteMessage: Boolean,
|
||||
neededPermissions: Iterable<Long>?
|
||||
neededPermissions: Iterable<Long>?,
|
||||
private val actions: MessageActions?
|
||||
) {
|
||||
val trigger: String = trigger!!
|
||||
val regex: Regex? = if (matchType == MatchType.REGEX) Regex(trigger!!) else null
|
||||
private val matchType: MatchType = matchType ?: MatchType.PREFIX
|
||||
val matchType: MatchType = matchType ?: MatchType.PREFIX
|
||||
private val neededRoles = neededPermissions?.toSet()
|
||||
|
||||
constructor(cmd: Command) : this(
|
||||
cmd.trigger,
|
||||
cmd.response,
|
||||
cmd.matchType,
|
||||
cmd.deleteMessage,
|
||||
cmd.neededRoles
|
||||
cmd.neededRoles,
|
||||
cmd.actions
|
||||
)
|
||||
|
||||
fun execute(message: MessageCreateEvent) {
|
||||
|
@ -34,20 +35,16 @@ class Command(
|
|||
return
|
||||
}
|
||||
}
|
||||
if (this.deleteMessage && message.message.canYouDelete()) {
|
||||
message.deleteMessage()
|
||||
}
|
||||
this.actions?.run(message, this)
|
||||
this.response?.let {
|
||||
message.channel.sendMessage(respond(message.messageAuthor))
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasOneOf(messageAuthor: MessageAuthor, roles: Set<Long>): Boolean {
|
||||
val optional = messageAuthor.asUser()
|
||||
return when {
|
||||
optional.isEmpty -> false
|
||||
else -> optional.get().getRoles(Config.server).map { it.id }.toSet().intersect(roles).isNotEmpty()
|
||||
}
|
||||
return messageAuthor.asUser().ifNotEmpty { user ->
|
||||
user.getRoles(Config.server).map { it.id }.toSet().intersect(roles).isNotEmpty()
|
||||
} ?: false
|
||||
}
|
||||
|
||||
fun matches(msg: String) = this.matchType.matches(msg, this)
|
||||
|
@ -60,9 +57,6 @@ enum class MatchType {
|
|||
PREFIX {
|
||||
override fun matches(message: String, command: Command) = message.startsWith(command.trigger)
|
||||
},
|
||||
FULL {
|
||||
override fun matches(message: String, command: Command) = message == command.trigger
|
||||
},
|
||||
CONTAINS {
|
||||
override fun matches(message: String, command: Command) = message.contains(command.trigger)
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package moe.kageru.kagebot
|
||||
|
||||
import com.moandjiezana.toml.Toml
|
||||
import org.javacord.api.DiscordApi
|
||||
import org.javacord.api.entity.server.Server
|
||||
import java.io.File
|
||||
|
||||
|
@ -10,6 +11,8 @@ class Config(val system: System, val localization: Localization, val commands: L
|
|||
val secret = File("secret").readText().replace("\n", "")
|
||||
var server: Server? = null
|
||||
get() = field!!
|
||||
var api: DiscordApi? = null
|
||||
get() = field!!
|
||||
|
||||
private fun read(path: String): Config {
|
||||
val rawConfig: Toml = Toml().read(run {
|
||||
|
@ -31,5 +34,5 @@ class Config(val system: System, val localization: Localization, val commands: L
|
|||
}
|
||||
}
|
||||
|
||||
data class System(val serverId: String)
|
||||
data class Localization(val permissionDenied: String)
|
||||
data class System(val serverId: String, val color: String)
|
||||
data class Localization(val permissionDenied: String, val redirectedMessage: String)
|
|
@ -23,6 +23,7 @@ class Kagebot {
|
|||
init {
|
||||
val api = DiscordApiBuilder().setToken(Config.secret).login().join()
|
||||
Config.server = api.getServerById(config.system.serverId).orElseThrow()
|
||||
Config.api = api
|
||||
Runtime.getRuntime().addShutdownHook(Thread {
|
||||
log.info("Bot has been interrupted. Shutting down.")
|
||||
api.disconnect()
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package moe.kageru.kagebot
|
||||
|
||||
import moe.kageru.kagebot.Config.Companion.config
|
||||
import moe.kageru.kagebot.Log.log
|
||||
import moe.kageru.kagebot.Util.ifNotEmpty
|
||||
import org.javacord.api.event.message.MessageCreateEvent
|
||||
|
||||
class MessageActions(private val delete: Boolean, private val redirect: Redirect?) {
|
||||
fun run(message: MessageCreateEvent, command: Command) {
|
||||
if (delete && message.message.canYouDelete()) {
|
||||
message.deleteMessage()
|
||||
}
|
||||
redirect?.execute(message, command)
|
||||
}
|
||||
}
|
||||
|
||||
class Redirect(private val target: Long, private val anonymous: Boolean) {
|
||||
|
||||
fun execute(message: MessageCreateEvent, command: Command) {
|
||||
val embed = MessageUtil.getEmbedBuilder()
|
||||
.addField(
|
||||
config.localization.redirectedMessage,
|
||||
message.readableMessageContent.let { content ->
|
||||
when (command.matchType) {
|
||||
MatchType.PREFIX -> content.removePrefix("${command.trigger} ")
|
||||
else -> content
|
||||
}
|
||||
}
|
||||
)
|
||||
// No inlined if/else because the types are different.
|
||||
// Passing the full message author will also include the avatar in the embed.
|
||||
embed.apply {
|
||||
if (anonymous) {
|
||||
setAuthor("Anonymous")
|
||||
} else {
|
||||
setAuthor(message.messageAuthor)
|
||||
}
|
||||
}
|
||||
Config.server!!.getTextChannelById(target).ifNotEmpty { it.sendMessage(embed) }
|
||||
?: log.warning("Could not redirect message to channel $target")
|
||||
}
|
||||
}
|
|
@ -1,9 +1,22 @@
|
|||
package moe.kageru.kagebot
|
||||
|
||||
import org.javacord.api.entity.message.MessageAuthor
|
||||
import org.javacord.api.entity.message.embed.EmbedBuilder
|
||||
import org.javacord.api.event.message.MessageCreateEvent
|
||||
import java.awt.Color
|
||||
import moe.kageru.kagebot.Config.Companion.config
|
||||
|
||||
object MessageUtil {
|
||||
fun mention(user: MessageAuthor): String {
|
||||
return "<@${user.id}>"
|
||||
}
|
||||
|
||||
fun MessageCreateEvent.asString(): String =
|
||||
"<${this.messageAuthor.discriminatedName}> ${this.readableMessageContent}"
|
||||
|
||||
fun getEmbedBuilder(): EmbedBuilder {
|
||||
val builder = EmbedBuilder()
|
||||
Config.server!!.icon.ifPresent { builder.setThumbnail(it) }
|
||||
return builder.setColor(Color.decode(config.system.color)).setTimestampToNow()
|
||||
}
|
||||
}
|
|
@ -1,13 +1,16 @@
|
|||
package moe.kageru.kagebot
|
||||
|
||||
import org.javacord.api.event.message.MessageCreateEvent
|
||||
import java.util.*
|
||||
|
||||
object Util {
|
||||
inline fun <T> T.doIf(condition: (T) -> Boolean, op: (T) -> T): T {
|
||||
return if (condition(this)) op(this) else this
|
||||
}
|
||||
|
||||
fun MessageCreateEvent.asString(): String =
|
||||
"<${this.messageAuthor.discriminatedName}> ${this.readableMessageContent}"
|
||||
|
||||
inline fun <T, R> Optional<T>.ifNotEmpty(op: (T) -> R): R? {
|
||||
if (this.isPresent) {
|
||||
return op(this.get())
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
[system]
|
||||
serverId = "356414885292277771"
|
||||
color = "#1793d0"
|
||||
|
||||
[localization]
|
||||
permissionDenied = "You do not have permission to use this command."
|
||||
# results in <name> says <message>
|
||||
redirectedMessage = "says"
|
||||
|
||||
[[commands]]
|
||||
trigger = "!ping"
|
||||
|
@ -26,7 +29,7 @@ response = "@@ there you go"
|
|||
|
||||
[[commands]]
|
||||
trigger = "delet this"
|
||||
deleteMessage = true
|
||||
actions = { delete = true }
|
||||
|
||||
[[commands]]
|
||||
trigger = "!restricted"
|
||||
|
@ -37,8 +40,14 @@ neededRoles = [
|
|||
446668543816106004
|
||||
]
|
||||
|
||||
# redirect every message that starts with !redirect to channel 555097559023222825
|
||||
[[commands]]
|
||||
trigger = "^[^`]*([()|DoOvVcC][-=^']?;|;[-=^']?[()|DoOpPvVcC3]|:wink:|😉)[^`]*$"
|
||||
response = "@@ Oboe!"
|
||||
matchType = "REGEX"
|
||||
deleteMessage = true
|
||||
trigger = "!redirect"
|
||||
response = "redirected"
|
||||
actions = { redirect = { target = 555097559023222825 } }
|
||||
|
||||
# the same, but without the original username
|
||||
[[commands]]
|
||||
trigger = "!anonRedirect"
|
||||
response = "redirected"
|
||||
actions = { redirect = {target = 555097559023222825, anonymous = true } }
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
package moe.kageru.kagebot
|
||||
|
||||
import io.kotlintest.matchers.string.shouldContain
|
||||
import io.kotlintest.shouldBe
|
||||
import io.kotlintest.specs.StringSpec
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import moe.kageru.kagebot.Config.Companion.config
|
||||
import org.javacord.api.entity.message.embed.EmbedBuilder
|
||||
import org.javacord.api.entity.permission.Role
|
||||
import org.javacord.api.entity.user.User
|
||||
import org.javacord.core.entity.message.embed.EmbedBuilderDelegateImpl
|
||||
import java.util.*
|
||||
|
||||
class CommandTest : StringSpec({
|
||||
Config.server = mockk()
|
||||
TestUtil.prepareServerConfig()
|
||||
"should match prefix command" {
|
||||
testMessageSuccess("!ping", "pong")
|
||||
}
|
||||
|
@ -38,8 +41,8 @@ class CommandTest : StringSpec({
|
|||
"should refuse command without permissions" {
|
||||
val calls = mutableListOf<String>()
|
||||
val mockOptional = mockk<Optional<User>>()
|
||||
every { mockOptional.isEmpty } returns false
|
||||
every { mockOptional.get().getRoles(any()) } returns emptyList()
|
||||
every { mockOptional.isPresent } returns true
|
||||
val mockMessage = TestUtil.mockMessage("!restricted", capturedCalls = calls)
|
||||
every { mockMessage.messageAuthor.asUser() } returns mockOptional
|
||||
Kagebot.processMessage(mockMessage)
|
||||
|
@ -59,7 +62,7 @@ class CommandTest : StringSpec({
|
|||
val mockRole = mockk<Role>()
|
||||
every { mockRole.id } returns 452034011393425409
|
||||
val mockOptional = mockk<Optional<User>>()
|
||||
every { mockOptional.isEmpty } returns false
|
||||
every { mockOptional.isPresent } returns true
|
||||
every { mockOptional.get().getRoles(any()) } returns listOf(mockRole)
|
||||
val mockMessage = TestUtil.mockMessage("!restricted", capturedCalls = calls)
|
||||
every { mockMessage.messageAuthor.asUser() } returns mockOptional
|
||||
|
@ -67,6 +70,15 @@ class CommandTest : StringSpec({
|
|||
calls.size shouldBe 1
|
||||
calls[0] shouldBe "access granted"
|
||||
}
|
||||
"should redirect" {
|
||||
val calls = mutableListOf<EmbedBuilder>()
|
||||
TestUtil.prepareServerConfig(calls)
|
||||
val message = "this is a message"
|
||||
Kagebot.processMessage(TestUtil.mockMessage("!anonRedirect $message"))
|
||||
calls.size shouldBe 1
|
||||
val delegateImpl = calls[0].delegate as EmbedBuilderDelegateImpl
|
||||
delegateImpl.toJsonNode().toString() shouldContain "\"$message\""
|
||||
}
|
||||
}) {
|
||||
companion object {
|
||||
fun testMessageSuccess(content: String, result: String) {
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
package moe.kageru.kagebot
|
||||
|
||||
import io.mockk.Runs
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import org.javacord.api.entity.channel.ServerTextChannel
|
||||
import org.javacord.api.entity.message.embed.EmbedBuilder
|
||||
import org.javacord.api.entity.server.Server
|
||||
import org.javacord.api.event.message.MessageCreateEvent
|
||||
import java.util.*
|
||||
|
||||
object TestUtil {
|
||||
fun mockMessage(
|
||||
|
@ -13,6 +19,7 @@ object TestUtil {
|
|||
): MessageCreateEvent {
|
||||
val message = mockk<MessageCreateEvent>()
|
||||
every { message.messageContent } returns content
|
||||
every { message.readableMessageContent } returns content
|
||||
every { message.messageAuthor.id } returns author
|
||||
every { message.channel.sendMessage(capture(capturedCalls)) } returns mockk()
|
||||
every { message.messageAuthor.isYourself } returns isSelf
|
||||
|
@ -20,4 +27,16 @@ object TestUtil {
|
|||
every { message.messageAuthor.isBotOwner } returns false
|
||||
return message
|
||||
}
|
||||
|
||||
fun prepareServerConfig(sentMessages: MutableList<EmbedBuilder> = mutableListOf()) {
|
||||
val channelMock = mockk<ServerTextChannel>()
|
||||
every { channelMock.sendMessage(capture(sentMessages)) } returns mockk()
|
||||
val resultMock = mockk<Optional<ServerTextChannel>>()
|
||||
every { resultMock.isPresent } returns true
|
||||
every {resultMock.get()} returns channelMock
|
||||
val server = mockk<Server>()
|
||||
every { server.icon.ifPresent(any()) } just Runs
|
||||
every { server.getTextChannelById(any<Long>()) } returns resultMock
|
||||
Config.server = server
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user